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

namespace XGame.Framework.Nodes
{
    public class NodeGroup : INodeGroup, IUpdate, ILateUpdate, IDisposable
    {
        private Dictionary<string, Node> _openedMap;
        /// <summary>
        /// 加载中，卸载中，关闭的都在这里
        /// </summary>
        private Dictionary<string, Node> _closedMap;
        private List<Node> _updateNodes;
        /// <summary>
        /// 每个Group单独一个Context
        /// </summary>
        private NodeContext _context;
        public NodeGroup(NodeContext context)
        {
            _context = context;
            _openedMap = new Dictionary<string, Node>();
            _closedMap = new Dictionary<string, Node>();
            _updateNodes = new List<Node>();
        }
        #region 接口实现
        bool INodeGroup.IsActive(NodeKey nodeKey)
        {
            if (_openedMap.TryGetValue(nodeKey, out var node))
            {
                return node.Active;
            }
            return false;
        }
        IAsync INodeGroup.AddAsync(NodeKey nodeKey, object intent)
        {
            var key = nodeKey.Key;
            if (_openedMap.ContainsKey(key))
            {
                Log.Warn($"重复开启Node. Key:{key}");
                var asyncGroup = new AsyncGroup();
                asyncGroup.End();
                return asyncGroup;
            }
            if (_closedMap.TryGetValue(key, out var node))
            {
                if (node.State == Loadable.LoadState.Loading)
                {
                    var loadingAsync = node.LoadingAsync;
                    loadingAsync.On(_ =>
                    {
                        ShowNode(key, node, intent);
                    });
                    return loadingAsync;
                }
                if (node.State == Loadable.LoadState.Unloading)
                {
                    Log.Error($"卸载中的Node又开始加载.Key:{key}");
                    node.ClearAsyncs();
                }
            }
            else
            {
                node = Activator.CreateInstance(nodeKey.NodeType) as Node;
                node.Init(_context);
                _closedMap.Add(key, node);
            }
            var loadAsync = node.LoadAsync();
            loadAsync.On(_ =>
            {
                ShowNode(key, node, intent);
            });
            return loadAsync;
        }
        void INodeGroup.Remove(NodeKey nodeKey, bool isDestroy)
        {
            var key = nodeKey.Key;
            if (_closedMap.TryGetValue(key, out var node))
            {
                if (node.State == Loadable.LoadState.Unloading)
                {
                    Log.Debug($"重复移除Node. Key:{key} isDestroy:{isDestroy}");
                    if (isDestroy)
                    { // 保证能执行destroy
                        node.UnloadingAsync.On(_ =>
                        {
                            _closedMap.Remove(key);
                            DestroyNode(node);
                        });
                    }
                    return;
                }
                if (node.State == Loadable.LoadState.Unloaded)
                { // 已经卸载完成
                    if (isDestroy)
                    {
                        _closedMap.Remove(key);
                        DestroyNode(node);
                    }
                    return;
                }
            }
            else if (_openedMap.TryGetValue(key, out node))
            {
                _openedMap.Remove(key);
                node.Disable();
                _closedMap.Add(key, node);
            }
            else
            { // 没有数据
                return;
            }
            var unloadAsync = node.UnloadAsync();
            unloadAsync.On(_ =>
            {
                if (isDestroy)
                {
                    _closedMap.Remove(key);
                    DestroyNode(node);
                }
            });
        }
        IAsync INodeGroup.Preload(NodeKey nodeKey)
        {
            var key = nodeKey.Key;
            if (_openedMap.ContainsKey(key) || _closedMap.ContainsKey(key))
            {
                return null;
            }
            var node = Activator.CreateInstance(nodeKey.NodeType) as Node;
            node.Init(_context);
            _closedMap.Add(key, node);
            return node.LoadAsync();
        }


        void IUpdate.Update(int millisecond)
        {
            if (_openedMap.Count == 0)
                return;
            _updateNodes.Clear();
            _updateNodes.AddRange(_openedMap.Values);
            for (var i = 0; i < _updateNodes.Count;)
            {
                var node = _updateNodes[i];
                node.Update(millisecond);
                if (node.Active)
                {
                    i++;
                }
                else
                {
                    _updateNodes.RemoveAt(i);
                }
            }
        }
        void ILateUpdate.LateUpdate(int millisecond)
        {
            if (_updateNodes.Count == 0)
                return;
            foreach (var node in _updateNodes)
            {
                node.LateUpdate(millisecond);
            }
            _updateNodes.Clear();
        }
        void IDisposable.Dispose()
        {
            foreach (var node in _openedMap.Values)
            {
                DestroyNode(node);
            }
            _openedMap.Clear();
            foreach (var node in _closedMap.Values)
            {
                DestroyNode(node);
            }
            _closedMap.Clear();
            _updateNodes.Clear();
            (_context as IDisposable)?.Dispose();
            _context = null;
        }
        #endregion

        public IAsync RemoveAll(bool isDestroy)
        {
            var asyncGroup = new AsyncGroup();
            var copyMap = new Dictionary<string, Node>(_openedMap);
            foreach(var item in copyMap)
            {
                var key = item.Key;
                var node = item.Value;
                _openedMap.Remove(key);
                node.Disable();
                if (!isDestroy)
                {
                    _closedMap.Add(key, node);
                }
                var unloadAsync = node.UnloadAsync();
                unloadAsync.Join(asyncGroup);
                unloadAsync.On(_ =>
                {
                    if (isDestroy)
                    {
                        DestroyNode(node);
                    }
                });
            }
            copyMap.Clear();
            if (isDestroy)
            {
                foreach (var item in _closedMap)
                {
                    var key = item.Key;
                    var node = item.Value;
                    if (node.State == Loadable.LoadState.Unloading)
                    {
                        var unloadingAsync = node.UnloadingAsync;
                        unloadingAsync.Join(asyncGroup);
                        // 保证能执行destroy
                        unloadingAsync.On(_ =>
                        {
                            DestroyNode(node);
                        });
                        continue;
                    }
                    if (node.State == Loadable.LoadState.Unloaded)
                    { // 已经卸载完成
                        DestroyNode(node);
                        continue;
                    }
                    var unloadAsync = node.UnloadAsync();
                    unloadAsync.Join(asyncGroup);
                    unloadAsync.On(_ =>
                    {
                        DestroyNode(node);
                    });
                }
                _closedMap.Clear();
            }
            asyncGroup.End();
            return asyncGroup;
        }

        #region 内部函数
        private void ShowNode(string key, Node node, object intent)
        {
            if (_openedMap.ContainsKey(key))
            {
                Log.Debug($"重复激活Node. Key:{key}");
                return;
            }
            _closedMap.Remove(key);
            _openedMap.Add(key, node);
            node.Enable(intent);
        }
        private void DestroyNode(Node node)
        {
            (node as IDisposable)?.Dispose();
        }
        #endregion
    }
}