using System;
using System.Collections.Generic;
using System.Reflection;
using CombatLibrary.CombatLibrary.CombatCore.Utility;
using Fort23.Core;
#if !COMBAT_SERVER
using Core.Pool.GPool;
using UnityEngine;
using Object = UnityEngine.Object;
#endif
namespace Fort23.UTool
{
    public class GameObjectQueue
    {
        public string poolName
        {
            get { return _poolName; }
        }
        private bool _isDis;
        private string _poolName;
        private string _prefabName;
        private int maxCount = -1;
#if !COMBAT_SERVER
        /// 
        /// 池子的父亲
        /// 
        private AssetHandle _PoolObjectFather;
        public bool isOne;
        private readonly List _usePool = new List();
        private readonly List _queue = new List();
        private readonly List _destroyPool = new List();
        private readonly List _delayHide = new List();
        public int initCopunt;
        private bool _isSetMaxCount;
        public void Init(string poolName, string prefabName)
        {
            _poolName = poolName;
            _prefabName = prefabName;
        }
        public async CTask Enqueue(IGObjectPoolInterface poolInterface, bool isNotAwaitDelay = false)
        {
            if (poolInterface == null || poolInterface.own == null)
            {
                return;
            }
            if (_queue.Contains(poolInterface))
            {
                return;
            }
            if (_delayHide.Contains(poolInterface))
            {
                return;
            }
            _delayHide.Add(poolInterface);
            if (!isNotAwaitDelay)
            {
                await poolInterface.DelayHide();
            }
            if (_isDis)
            {
                return;
            }
            _delayHide.Remove(poolInterface);
            poolInterface.isUse = false;
            _usePool.Remove(poolInterface);
            if (poolInterface.own != null)
            {
                poolInterface.DormancyObj();
                this._queue.Add(poolInterface);
            }
        }
        public void AddDestroy(IGObjectPoolInterface poolInterface, bool immediately = false)
        {
            if (poolInterface.own == null)
            {
                return;
            }
            if (!_destroyPool.Contains(poolInterface))
            {
                if (immediately)
                {
                    poolInterface.DestroyTimer = null;
                    GameObjectDestroyPool.Instance.DestroyObject(poolInterface.own);
                }
                else
                {
                    TimerComponent.Instance.Remove(poolInterface.DestroyTimer);
                    poolInterface.DestroyTimer =
                        TimerComponent.Instance.AddTimer(6000, delegate
                        {
                            _destroyPool.Remove(poolInterface);
                            poolInterface.DestroyTimer = null;
                            GameObjectDestroyPool.Instance.DestroyObject(poolInterface.own);
                        });
                    _destroyPool.Add(poolInterface);
                }
            }
        }
        public void DormancyPool()
        {
            List alliG = new List();
            IGObjectPoolInterface[] all = _usePool.ToArray();
            if (all != null)
            {
                alliG.AddRange(all);
            }
            for (int i = 0; i < alliG.Count; i++)
            {
                IGObjectPoolInterface poolInterface = alliG[i];
                Enqueue(poolInterface);
            }
            // _usePool.Clear();
        }
        public async CTask Preset(Clock clock, bool isUseSynchronous) where T : IGObjectPoolInterface
        {
            DormancyPool();
            for (int i = 0; i < Count; i++)
            {
                IGObjectPoolInterface poolInterface = _queue[i];
                DisposePoolEntity(poolInterface);
                poolInterface.Preset();
            }
            for (int i = 0; i < _usePool.Count; i++)
            {
                IGObjectPoolInterface poolInterface = _usePool[i];
                DisposePoolEntity(poolInterface);
                poolInterface.Preset();
            }
            for (int i = 0; i < _destroyPool.Count; i++)
            {
                DisposePoolEntity(_destroyPool[i]);
            }
            _destroyPool.Clear();
            T OBJ = await Dequeue(true, clock, isUseSynchronous);
            Enqueue(OBJ, true);
        }
        private async CTask LoadAsset(Clock clock, bool isUseSynchronous)
        {
            if (AssetBundleLoadManager.Instance != null)
            {
                CTask cTask =
                    AssetBundleLoadManager.Instance.LoadAssetAsyncTask(_prefabName, clock: clock,
                        isUseSynchronous: isUseSynchronous);
                AssetHandle assetHandle = await cTask;
                return assetHandle;
            }
            return null;
        }
        public T DequeuForSynchronize(GameObject gameObject, bool isActive) where T : IGObjectPoolInterface
        {
            if (_queue.Count <= 0)
            {
                return CreateInstance(isActive, gameObject);
            }
            if (_destroyPool.Count > 0)
            {
                for (int i = 0; i < _destroyPool.Count; i++)
                {
                    DisposePoolEntity(_destroyPool[i]);
                }
                _destroyPool.Clear();
            }
            for (int i = 0; i < _queue.Count; i++)
            {
                if (_queue[i].own == null)
                {
                    _queue.RemoveAt(i);
                    i--;
                    continue;
                }
                else
                {
                    IGObjectPoolInterface objectPoolInterface = this._queue[0];
                    _queue.RemoveAt(0);
                    UseObject(objectPoolInterface, isActive);
                    return (T)objectPoolInterface;
                }
            }
            return CreateInstance(isActive, gameObject);
        }
        public async CTask Dequeue(bool isActive, Clock clock, bool isUseSynchronous)
            where T : IGObjectPoolInterface
        {
            if (maxCount > 0 && _usePool.Count > maxCount)
            {
                return default;
            }
            for (int i = 0; i < _queue.Count; i++)
            {
                if (_queue[i].own == null)
                {
                    _queue.RemoveAt(i);
                    i--;
                    continue;
                }
                else
                {
                    IGObjectPoolInterface objectPoolInterface = this._queue[i];
                    if (objectPoolInterface.own == null)
                    {
                        continue;
                    }
                    _queue.RemoveAt(i);
                    UseObject(objectPoolInterface, isActive);
                    return (T)objectPoolInterface;
                }
            }
            bool isUsePoolObject = false;
            if ((_PoolObjectFather == null || _PoolObjectFather.IsNull()) && !string.IsNullOrEmpty(_prefabName))
            {
                using (await CoroutineLockComponent.Instance.Wait(poolName, 20000))
                {
                    if (_PoolObjectFather == null)
                    {
                        _PoolObjectFather = await LoadAsset(clock, isUseSynchronous);
                        isUsePoolObject = true;
                    }
                }
            }
            // if (clock != null && clock.isBreak)
            // {
            //     if (isUsePoolObject)
            //     {
            //         Debug.LogError("已打断,但是有初始化数据" + _PoolObjectFather);
            //     }
            //     // return default;
            // }
            if (_queue.Count <= 0)
            {
                AssetHandle assetHandle = _PoolObjectFather;
                if (!isUsePoolObject)
                {
                    assetHandle = await _PoolObjectFather.InstantiateSync(isUseSynchronous);
                }
                // AssetHandle assetHandle = isUsePoolObject ? _PoolObjectFather : _PoolObjectFather;
                return CreateInstance(isActive, assetHandle);
            }
            if (_destroyPool.Count > 0)
            {
                for (int i = 0; i < _destroyPool.Count; i++)
                {
                    DisposePoolEntity(_destroyPool[i]);
                }
                _destroyPool.Clear();
            }
            AssetHandle assetHandle2 = _PoolObjectFather;
            if (!isUsePoolObject)
            {
                assetHandle2 = await _PoolObjectFather.InstantiateSync(isUseSynchronous);
            }
            assetHandle2 = await assetHandle2.InstantiateSync(isUseSynchronous);
            return CreateInstance(isActive, assetHandle2);
        }
        private T CreateInstance(bool isActive, AssetHandle assetHandle) where T : IGObjectPoolInterface
        {
            if (assetHandle == null)
            {
                return default;
            }
            T poolInerface = Activator.CreateInstance();
            GameObject go = assetHandle.AssetObject();
            GameObjectEntity objectEntity = go.GetComponent();
            if (objectEntity == null)
            {
                objectEntity = go.AddComponent();
            }
            objectEntity.IgObjectPoolInterface = poolInerface;
            objectEntity.OnDestroyCallBack = DestroyEntity;
            poolInerface.SetGameObject(go);
            poolInerface.poolObjName = _poolName;
            poolInerface.AssetHandle = assetHandle;
            UseObject(poolInerface, isActive);
            initCopunt++;
            return poolInerface;
        }
        private T CreateInstance(bool isActive, GameObject gameObject) where T : IGObjectPoolInterface
        {
            if (gameObject == null)
            {
                return default;
            }
            T poolInerface = Activator.CreateInstance();
            GameObject go = GameObject.Instantiate(gameObject);
            GameObjectEntity objectEntity = go.GetComponent();
            if (objectEntity == null)
            {
                objectEntity = go.AddComponent();
            }
            objectEntity.IgObjectPoolInterface = poolInerface;
            objectEntity.OnDestroyCallBack = DestroyEntity;
            poolInerface.SetGameObject(go);
            poolInerface.poolObjName = _poolName;
            poolInerface.AssetHandle = null;
            UseObject(poolInerface, isActive);
            initCopunt++;
            return poolInerface;
        }
        private void DestroyEntity(IGObjectPoolInterface poolInterface)
        {
            if (poolInterface != null)
            {
                _queue.Remove(poolInterface);
                _usePool.Remove(poolInterface);
            }
            else
            {
                return;
            }
            DisposePoolEntity(poolInterface);
            // poolInterface.DormancyObj();
            poolInterface.DestroyObj();
            poolInterface.AssetHandle?.Release();
            if (poolInterface.AssetHandle == _PoolObjectFather)
            {
                _PoolObjectFather = null;
                if (_queue.Count > 0)
                {
                    _PoolObjectFather = _queue[0].AssetHandle;
                }
            }
            if (_queue.Count <= 0 && _usePool.Count <= 0) //池子已经被释放完,把父物体也释掉
            {
                GObjectPool.Instance.DestroyPool(poolName);
            }
        }
        private void UseObject(IGObjectPoolInterface objectPoolInterface, bool isActive)
        {
            if (!_isSetMaxCount)
            {
                _isSetMaxCount = true;
                GObjectPoolMaxConfig gObjectPoolMaxConfig =
                    objectPoolInterface.own.GetComponent();
                if (gObjectPoolMaxConfig != null)
                {
                    maxCount = gObjectPoolMaxConfig.maxCount;
                }
            }
            _usePool.Add(objectPoolInterface);
            DisposePoolEntity(objectPoolInterface);
            // TimerComponent.Instance.Remove(objectPoolInterface.DestroyTimer);
            // GameObjectDestroyPool.Instance.RomoveDestroyObject(objectPoolInterface.own);
            objectPoolInterface.isUse = true;
            objectPoolInterface.ResetData();
            if (isActive)
            {
                objectPoolInterface.ActiveObj();
            }
        }
        public int Count => this._queue.Count;
        public void Dispose()
        {
            _isDis = true;
            for (int i = 0; i < Count; i++)
            {
                IGObjectPoolInterface poolInterface = _queue[i];
                DisposePoolEntity(poolInterface);
                if (poolInterface.own != null)
                {
                    GameObjectEntity gameObjectEntity = poolInterface.own.GetComponent();
                    if (gameObjectEntity != null)
                    {
                        gameObjectEntity.IgObjectPoolInterface = null;
                        gameObjectEntity.OnDestroyCallBack = null;
                    }
                    poolInterface.own.SetActive(false);
                    poolInterface.DormancyObj();
                    poolInterface.DestroyObj();
                    poolInterface.AssetHandle?.Release();
                    GameObjectDestroyPool.Instance.DestroyObject(poolInterface.own);
                }
            }
            for (int i = 0; i < _delayHide.Count; i++)
            {
                IGObjectPoolInterface poolInterface = _delayHide[i];
                DisposePoolEntity(poolInterface);
                if (poolInterface.own != null)
                {
                    GameObjectEntity gameObjectEntity = poolInterface.own.GetComponent();
                    if (gameObjectEntity != null)
                    {
                        gameObjectEntity.IgObjectPoolInterface = null;
                        gameObjectEntity.OnDestroyCallBack = null;
                    }
                    poolInterface.own.SetActive(false);
                    poolInterface.DormancyObj();
                    poolInterface.DestroyObj();
                    poolInterface.AssetHandle?.Release();
                    GameObjectDestroyPool.Instance.DestroyObject(poolInterface.own);
                }
            }
            for (int i = 0; i < _usePool.Count; i++)
            {
                IGObjectPoolInterface poolInterface = _usePool[i];
                DisposePoolEntity(poolInterface);
                if (poolInterface.own != null)
                {
                    GameObjectEntity gameObjectEntity = poolInterface.own.GetComponent();
                    if (gameObjectEntity != null)
                    {
                        gameObjectEntity.IgObjectPoolInterface = null;
                        gameObjectEntity.OnDestroyCallBack = null;
                    }
                    poolInterface.own.SetActive(false);
                    poolInterface.DormancyObj();
                    poolInterface.DestroyObj();
                    GameObjectDestroyPool.Instance.DestroyObject(poolInterface.own);
                }
            }
            _PoolObjectFather = null;
            _delayHide.Clear();
            _queue.Clear();
            _destroyPool.Clear();
            _usePool.Clear();
        }
        private void DisposePoolEntity(IGObjectPoolInterface poolInterface)
        {
            _destroyPool.Remove(poolInterface);
            TimerComponent.Instance.Remove(poolInterface.DestroyTimer);
            GameObjectDestroyPool.Instance.RomoveDestroyObject(poolInterface.own);
            poolInterface.DestroyTimer = null;
        }
#endif
    }
    public class GObjectPool
    {
        private static GObjectPool _instance;
        public static GObjectPool Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new GObjectPool();
                }
                return _instance;
            }
        }
        /// 
        /// key是gameObject.name.GetHashCode();
        /// 
        private readonly Dictionary _dictionary = new Dictionary();
        /// 
        /// 在ab包里面的东西,都可以通过这个方法获取
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public async CTask FetchAsync(string prefabName, System.Action callBack = null, Clock clock = null,
            bool Prestore = false, string poolName = null, bool isUseSynchronous = false)
            where T : IGObjectPoolInterface
        {
            if (!prefabName.Contains(".prefab"))
            {
                prefabName += ".prefab";
            }
            T poolInerface = await FetchObjectAsync(prefabName, clock, Prestore, poolName,
                isUseSynchronous: isUseSynchronous);
            if (Prestore)
            {
                callBack?.Invoke(default);
                return default;
            }
            if (poolInerface == null)
            {
                callBack?.Invoke(default);
                return default;
            }
            if (clock != null && clock.isBreak)
            {
                Recycle(poolInerface);
                callBack?.Invoke(default);
                return default;
            }
            callBack?.Invoke(poolInerface);
            return poolInerface;
        }
        private string GetPoolName(string name)
        {
            return name + typeof(T);
        }
#if !COMBAT_SERVER
        public T FetchAsyncForGameObject(GameObject gameObject, string poolName
        ) where T : IGObjectPoolInterface
        {
            T poolInerface = Fetch(poolName, gameObject);
            if (poolInerface == null)
            {
                return default;
            }
            return poolInerface;
        }
        public T Fetch(string poolName, GameObject gameObject, bool isActive = true) where T : IGObjectPoolInterface
        {
            string newPoolName = GetPoolName(poolName);
            GameObjectQueue gameObjectQueue = null;
            if (!this._dictionary.TryGetValue(newPoolName, out gameObjectQueue))
            {
                gameObjectQueue = EnterPool(newPoolName, null);
            }
            return gameObjectQueue.DequeuForSynchronize(gameObject, isActive);
        }
#endif
        private async CTask FetchObjectAsync(string prefabName, Clock clock, bool Prestore, string poolName,
            bool isActive = true, bool isUseSynchronous = false) where T : IGObjectPoolInterface
        {
#if !COMBAT_SERVER
            try
            {
                GameObjectQueue gameObjectQueue = null;
                poolName = poolName == null ? prefabName : poolName;
                poolName = GetPoolName(poolName);
                // 加一个协程锁,让同时加载资源的时候lock住
                // using (await CoroutineLockComponent.Instance.Wait(poolName,20000))
                {
                    if (clock != null && clock.isBreak)
                    {
                        return default;
                    }
                    if (this._dictionary.TryGetValue(poolName, out gameObjectQueue))
                    {
                        if (Prestore)
                        {
                            await gameObjectQueue.Preset(clock, isUseSynchronous);
                            return default;
                        }
                        return await gameObjectQueue.Dequeue(isActive, clock, isUseSynchronous);
                    }
                    if (!this._dictionary.TryGetValue(poolName, out gameObjectQueue))
                    {
                        gameObjectQueue = EnterPool(poolName, prefabName);
                    }
                    if (gameObjectQueue == null)
                    {
                        return default;
                    }
                    if (Prestore)
                    {
                        await gameObjectQueue.Preset(clock, isUseSynchronous);
                        return default;
                    }
                    return await gameObjectQueue.Dequeue(isActive, clock, isUseSynchronous);
                }
            }
            catch (Exception e)
            {
                LogTool.Exception(e);
                return default;
            }
#else
            return default;
#endif
        }
#if !COMBAT_SERVER
        private Transform _parent;
        private GameObjectQueue EnterPool(string poolName, string prefabName) where T : IGObjectPoolInterface
        {
            if (_parent == null)
            {
                _parent = new GameObject().transform;
                _parent.gameObject.name = "Pool";
            }
            GameObjectQueue queue = new GameObjectQueue();
            queue.Init(poolName, prefabName);
            _dictionary.Add(poolName, queue);
            return queue;
        }
#endif
        public void Recycle(IGObjectPoolInterface poolInterface)
        {
            if (poolInterface == null)
            {
                return;
            }
#if !COMBAT_SERVER
            GameObjectQueue queue;
            if (!this._dictionary.TryGetValue(poolInterface.poolObjName, out queue))
            {
                LogTool.Error("不再池子之中 " + poolInterface.poolObjName);
                return;
            }
            queue.Enqueue(poolInterface);
#endif
        }
        public async CTask Recycle(IGObjectPoolInterface poolInterface, bool isNotAwaitDelay)
        {
            if (poolInterface == null)
            {
                return;
            }
#if !COMBAT_SERVER
            GameObjectQueue queue;
            if (!this._dictionary.TryGetValue(poolInterface.poolObjName, out queue))
            {
                LogTool.Error("不再池子之中 " + poolInterface.poolObjName);
                return;
            }
            await queue.Enqueue(poolInterface, isNotAwaitDelay);
#endif
        }
        public void RecycleForDestroy(IGObjectPoolInterface poolInterface, bool immediately = false)
        {
            if (poolInterface == null)
            {
                return;
            }
#if !COMBAT_SERVER
            GameObjectQueue queue;
            if (!this._dictionary.TryGetValue(poolInterface.poolObjName, out queue))
            {
                LogTool.Error("不再池子之中 " + poolInterface.poolObjName);
                return;
            }
            queue.Enqueue(poolInterface);
            queue.AddDestroy(poolInterface, immediately);
#endif
        }
        public void DestroyPool(string poolObjName)
        {
#if !COMBAT_SERVER
            GameObjectQueue queue;
            if (!this._dictionary.TryGetValue(poolObjName, out queue))
            {
                // LogTool.Warning("没有这个池子 " + poolObjName);
                return;
            }
            _dictionary.Remove(poolObjName);
            queue.Dispose();
#endif
        }
        /// 
        /// 休眠一个池子
        /// 
        /// 
        public void DormancyPool(string poolName)
        {
#if !COMBAT_SERVER
            GameObjectQueue queue;
            if (!this._dictionary.TryGetValue(poolName, out queue))
            {
                string poolObjName = GetPoolName(poolName);
                if (!this._dictionary.TryGetValue(poolObjName, out queue))
                {
                    // LogTool.Warning("没有这个池子 " + poolObjName);
                    return;
                }
            }
            queue?.DormancyPool();
#endif
        }
        /// 
        /// 清除所有池子
        /// 
        public void Clear()
        {
            Dispose();
        }
        /// 
        /// 清除所有池子
        /// 
        public void Dispose()
        {
#if !COMBAT_SERVER
            foreach (KeyValuePair kv in this._dictionary)
            {
                kv.Value.Dispose();
            }
            this._dictionary.Clear();
            _instance = null;
#endif
        }
    }
}