using System;
using System.Collections.Generic;
using System.Reflection;
using CombatLibrary.CombatLibrary.CombatCore.Utility;
using Fort23.Core;
#if !COMBAT_SERVER
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;
#if !COMBAT_SERVER
        /// <summary>
        /// 池子的父亲
        /// </summary>
        private AssetHandle _PoolObjectFather;

        public bool isOne;

        private readonly List<IGObjectPoolInterface> _usePool = new List<IGObjectPoolInterface>();
        private readonly List<IGObjectPoolInterface> _queue = new List<IGObjectPoolInterface>();
        private readonly List<IGObjectPoolInterface> _destroyPool = new List<IGObjectPoolInterface>();
        private readonly List<IGObjectPoolInterface> _delayHide = new List<IGObjectPoolInterface>();

        public int initCopunt;

        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<IGObjectPoolInterface> alliG = new List<IGObjectPoolInterface>();
            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<T>(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<T>(true, clock,isUseSynchronous);
            Enqueue(OBJ, true);
        }

        private async CTask<AssetHandle> LoadAsset(Clock clock,bool isUseSynchronous)
        {
            if (AssetBundleLoadManager.Instance != null)
            {
                CTask<AssetHandle> cTask =
                    AssetBundleLoadManager.Instance.LoadAssetAsyncTask<GameObject>(_prefabName, clock: clock,isUseSynchronous:isUseSynchronous);
                AssetHandle assetHandle = await cTask;
                return assetHandle;
            }

            return null;
        }

        public T DequeuForSynchronize<T>(GameObject gameObject, bool isActive) where T : IGObjectPoolInterface
        {
            if (_queue.Count <= 0)
            {
                return CreateInstance<T>(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<T>(isActive, gameObject);
        }

        public async CTask<T> Dequeue<T>(bool isActive, Clock clock,bool isUseSynchronous) where T : IGObjectPoolInterface
        {
            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))
            {
                _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<T>(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<T>(isActive, assetHandle2);
        }

        private T CreateInstance<T>(bool isActive, AssetHandle assetHandle) where T : IGObjectPoolInterface
        {
            if (assetHandle == null)
            {
                return default;
            }

            T poolInerface = Activator.CreateInstance<T>();
            GameObject go = assetHandle.AssetObject<GameObject>();
            GameObjectEntity objectEntity = go.GetComponent<GameObjectEntity>();
            if (objectEntity == null)
            {
                objectEntity = go.AddComponent<GameObjectEntity>();
            }

            objectEntity.IgObjectPoolInterface = poolInerface;
            objectEntity.OnDestroyCallBack = DestroyEntity;
            poolInerface.SetGameObject(go);
            poolInerface.poolObjName = _poolName;
            poolInerface.AssetHandle = assetHandle;
            UseObject(poolInerface, isActive);
            initCopunt++;

            return poolInerface;
        }

        private T CreateInstance<T>(bool isActive, GameObject gameObject) where T : IGObjectPoolInterface
        {
            if (gameObject == null)
            {
                return default;
            }

            T poolInerface = Activator.CreateInstance<T>();
            GameObject go = GameObject.Instantiate(gameObject);
            GameObjectEntity objectEntity = go.GetComponent<GameObjectEntity>();
            if (objectEntity == null)
            {
                objectEntity = go.AddComponent<GameObjectEntity>();
            }

            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)
        {
            _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<GameObjectEntity>();
                    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<GameObjectEntity>();
                    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<GameObjectEntity>();
                    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;
            }
        }

        /// <summary>
        /// key是gameObject.name.GetHashCode();
        /// </summary>
        private readonly Dictionary<string, GameObjectQueue> _dictionary = new Dictionary<string, GameObjectQueue>();

        /// <summary>
        /// 在ab包里面的东西,都可以通过这个方法获取
        /// </summary>
        /// <param name="prefabName"></param>
        /// <param name="callBack"></param>
        /// <param name="clock"></param>
        /// <param name="Prestore"></param>
        /// <param name="poolName"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public async CTask<T> FetchAsync<T>(string prefabName, System.Action<T> 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<T>(prefabName, clock, Prestore, poolName,isUseSynchronous:isUseSynchronous);
            if (Prestore)
            {
                return default;
            }

            if (poolInerface == null)
            {
                return default;
            }

            if (clock != null && clock.isBreak)
            {
                Recycle(poolInerface);
                return default;
            }

            callBack?.Invoke(poolInerface);
            return poolInerface;
        }


        private string GetPoolName<T>(string name)
        {
            return name + typeof(T);
        }

#if !COMBAT_SERVER
        public T FetchAsyncForGameObject<T>(GameObject gameObject, string poolName
        ) where T : IGObjectPoolInterface
        {
            T poolInerface = Fetch<T>(poolName, gameObject);
            if (poolInerface == null)
            {
                return default;
            }

            return poolInerface;
        }

        public T Fetch<T>(string poolName, GameObject gameObject, bool isActive = true) where T : IGObjectPoolInterface
        {
            string newPoolName = GetPoolName<T>(poolName);
            GameObjectQueue gameObjectQueue = null;
            if (!this._dictionary.TryGetValue(newPoolName, out gameObjectQueue))
            {
                gameObjectQueue = EnterPool<T>(newPoolName, null);
            }

            return gameObjectQueue.DequeuForSynchronize<T>(gameObject, isActive);
        }

#endif
        private async CTask<T> FetchObjectAsync<T>(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<T>(poolName);


                // 加一个协程锁，让同时加载资源的时候lock住
                using (await CoroutineLockComponent.Instance.Wait(poolName))
                {
                    if (clock != null && clock.isBreak)
                    {
                        return default;
                    }

                    if (this._dictionary.TryGetValue(poolName, out gameObjectQueue))
                    {
                        if (Prestore)
                        {
                            await gameObjectQueue.Preset<T>(clock,isUseSynchronous);
                            return default;
                        }

                        return await gameObjectQueue.Dequeue<T>(isActive, clock,isUseSynchronous);
                    }

                    if (!this._dictionary.TryGetValue(poolName, out gameObjectQueue))
                    {
                        gameObjectQueue = EnterPool<T>(poolName, prefabName);
                    }

                    if (gameObjectQueue == null)
                    {
                        return default;
                    }

                    if (Prestore)
                    {
                        await gameObjectQueue.Preset<T>(clock,isUseSynchronous);
                        return default;
                    }

                    return await gameObjectQueue.Dequeue<T>(isActive, clock,isUseSynchronous);
                }
            }
            catch (Exception e)
            {
                LogTool.Exception(e);
                return default;
            }
#else
            return default;
#endif
        }
#if !COMBAT_SERVER
        private Transform _parent;

        private GameObjectQueue EnterPool<T>(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
        }


        /// <summary>
        /// 休眠一个池子
        /// </summary>
        /// <param name="poolName"></param>
        public void DormancyPool<T>(string poolName)
        {
#if !COMBAT_SERVER
            GameObjectQueue queue;
            if (!this._dictionary.TryGetValue(poolName, out queue))
            {
                string poolObjName = GetPoolName<T>(poolName);
                if (!this._dictionary.TryGetValue(poolObjName, out queue))
                {
                    // LogTool.Warning("没有这个池子 " + poolObjName);
                    return;
                }
            }

            queue?.DormancyPool();
#endif
        }

        /// <summary>
        /// 清除所有池子
        /// </summary>
        public void Clear()
        {
            Dispose();
        }

        /// <summary>
        /// 清除所有池子
        /// </summary>
        public void Dispose()
        {
#if !COMBAT_SERVER
            foreach (KeyValuePair<string, GameObjectQueue> kv in this._dictionary)
            {
                kv.Value.Dispose();
            }

            this._dictionary.Clear();
            _instance = null;
#endif
        }
    }
}