using XGame.Framework.Asyncs;
using System.Collections.Generic;

namespace XGame.Framework.Asset
{
    /// <summary>
    /// 加载任务的异步操作池
    /// </summary>
    internal sealed class AssetAsyncPool
    {
        /// <summary>
        /// 未使用的Agents
        /// </summary>
        Stack<IAssetAsyncAgent> FreeAgents { set; get; }
        /// <summary>
        /// 使用中的Agents
        /// </summary>
        //LinkedList<IAssetAsyncAgent> WorkingAgents { set; get; }
        Dictionary<IAssetAsync, IAssetAsyncAgent> WorkingAgents { set; get; }
        /// <summary>
        /// 等待处理的Async
        /// </summary>
        LinkedList<IAssetAsync> WaitingAsyncs { set; get; }
        /// <summary>
        /// 存储所有未完成的Async，方便查找
        /// </summary>
        Dictionary<string, IAssetAsync> AssetAsyncMap { set; get; }
        /// <summary>
        /// 是否被暂停。
        /// </summary>
        public bool Paused { set; get; }
        /// <summary>
        /// agent的数量上限
        /// </summary>
        public int AgentLimit { set; get; }
        /// <summary>
        /// 构造函数。
        /// </summary>
        public AssetAsyncPool()
        {
            FreeAgents = new Stack<IAssetAsyncAgent>();
            WorkingAgents = new Dictionary<IAssetAsync, IAssetAsyncAgent>();
            WaitingAsyncs = new LinkedList<IAssetAsync>();
            AssetAsyncMap = new Dictionary<string, IAssetAsync>();
            Paused = false;
            AgentLimit = 0;
        }

        /// <summary>
        /// 获取代理总数量。
        /// 包括使用中和未使用的代理，不一定等于AgentLimit
        /// </summary>
        public int TotalAgentCount => (FreeAgentCount + WorkingAgentCount);

        /// <summary>
        /// 获取可用代理数量。
        /// 仅返回缓存池中未使用的代理数量
        /// </summary>
        public int FreeAgentCount => FreeAgents.Count;

        /// <summary>
        /// 获取工作中代理数量。
        /// </summary>
        public int WorkingAgentCount => WorkingAgents.Count;

        /// <summary>
        /// 获取等待Async数量。
        /// </summary>
        public int WaitingAsyncCount => WaitingAsyncs.Count;

        /// <summary>
        /// 获取未完成的Async
        /// </summary>
        /// <param name="addressableName"></param>
        /// <returns></returns>
        public IAssetAsync GetAssetAsync(string addressableName)
        {
            AssetAsyncMap.TryGetValue(addressableName, out IAssetAsync assetAsync);
            return assetAsync;
        }
        /// <summary>
        /// 是否包含指定的Async
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public bool ContainsAssetAsync(string addressableName)
        {
            return AssetAsyncMap.ContainsKey(addressableName);
        }

        /// <summary>
        /// 获取未使用的Agent
        /// 可能从缓存里取，也可能是新建的Agent
        /// </summary>
        /// <returns></returns>
        private IAssetAsyncAgent GetFreeAgent()
        {
            if (FreeAgents.Count > 0)
            {
                return FreeAgents.Pop();
            }
            if (TotalAgentCount < AgentLimit)
            {
                var agent = new AssetAsyncAgent();
                return agent;
            }
            return null;
        }

        /// <summary>
        /// 增加AssetAsync。
        /// </summary>
        /// <param name="assetAsync"></param>
        public void AddAsync(IAssetAsync assetAsync)
        {
            if (AssetAsyncMap.ContainsKey(assetAsync.AddressableName))
            {
                //AssetsLog.Debug("Asset Add Async Repeat. Name:{0}", assetAsync.AddressableName);
                return;
            }
            AssetAsyncMap.Add(assetAsync.AddressableName, assetAsync);
            LinkedListNode<IAssetAsync> current = WaitingAsyncs.Last;

            if (current != null)
            {
                WaitingAsyncs.AddAfter(current, assetAsync);
            }
            else
            {
                WaitingAsyncs.AddFirst(assetAsync);
            }
            ProcessWaitingAsync();
        }

        ///// <summary>
        ///// 移除任务。
        ///// </summary>
        ///// <param name="serialId">要移除任务的序列编号。</param>
        ///// <returns>被移除的任务。</returns>
        //public bool RemoveAsync(int serialId)
        //{
        //    foreach (IAssetAsync waitingAsync in WaitingAsyncs)
        //    {
        //        if (waitingAsync.SerialId == serialId)
        //        {
        //            WaitingAsyncs.Remove(waitingAsync);
        //            return true;
        //        }
        //    }

        //    foreach (IAssetAsyncAgent workingAgent in WorkingAgents)
        //    {
        //        if (workingAgent.Task.SerialId == serialId)
        //        {
        //            workingAgent.Stop();
        //            m_FreeAgents.Push(workingAgent);
        //            WorkingAgents.Remove(workingAgent);
        //            return true;
        //        }
        //    }
        //    return false;
        //}

        private void ProcessWaitingAsync()
        {
            LinkedListNode<IAssetAsync> current = WaitingAsyncs.First;
            IAssetAsyncAgent agent;
            while (current != null && (agent = GetFreeAgent()) != null)
            {
                LinkedListNode<IAssetAsync> next = current.Next;
                AgentStatus status = agent.Start(current.Value);
                if (status == AgentStatus.CanResume)
                {//设置监听
                    current.Value.On(OnAsyncCompleted);
                    WorkingAgents.Add(current.Value, agent);
                }
                else
                {
                    agent.Stop();
                    FreeAgents.Push(agent);
                    if (status == AgentStatus.Done || status == AgentStatus.UnknownError)
                    {
                        AssetAsyncMap.Remove(current.Value.AddressableName);
                    }
                }
                if (status != AgentStatus.HasToWait)
                {
                    WaitingAsyncs.Remove(current);
                }
                current = next;
            }
        }

        void OnAsyncCompleted(IAsync aAsync)
        {
            IAssetAsync assetAsync = aAsync as IAssetAsync;
            if (assetAsync != null)
            {
                if (WorkingAgents.TryGetValue(assetAsync, out IAssetAsyncAgent agent))
                {
                    agent.Stop();
                    WorkingAgents.Remove(assetAsync);
                    FreeAgents.Push(agent);
                }
                AssetAsyncMap.Remove(assetAsync.AddressableName);
            }
            ProcessWaitingAsync();
        }
        /// <summary>
        /// 移除所有任务。
        /// </summary>
        public void Clear()
        {
            WaitingAsyncs.Clear();
            AssetAsyncMap.Clear();
            foreach (var it in WorkingAgents)
            {
                it.Value.Stop();
                FreeAgents.Push(it.Value);
            }
            WorkingAgents.Clear();
        }
        public void Dispose()
        {
            WaitingAsyncs.Clear();

            while (FreeAgentCount > 0)
            {
                FreeAgents.Pop().Dispose();
            }
            foreach (var it in WorkingAgents)
            {
                it.Value.Dispose();
            }
            WorkingAgents.Clear();

            AssetAsyncMap.Clear();
        }
    }
}
