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

namespace XGame.Framework.Nodes
{
    public class NodeTree : INodeTree, IUpdate, ILateUpdate, IDisposable
    {
        public INode Root { get; private set; }
        private NodeContext _context;
        private Dictionary<uint, NodeGroup> _nodeGroupMap;

        private List<IUpdate> _updates;
        private List<ILateUpdate> _lateUpdate;
        public NodeTree(NodeContext context, INode root)
        {
            _context = context;
            Root = root;
            _nodeGroupMap = new Dictionary<uint, NodeGroup>();
            _updates = new List<IUpdate>();
            _lateUpdate = new List<ILateUpdate>();

            if (root is IUpdate update)
            {
                _updates.Add(update);
            }
            if (root is ILateUpdate lateUpdate)
            {
                _lateUpdate.Add(lateUpdate);
            }
        }
        #region 接口实现
        void INodeTree.RemoveGroup(uint groupId, bool isDestroy)
        {
            if (_nodeGroupMap.TryGetValue(groupId, out var group))
            {
                var removeAsync = group.RemoveAll(isDestroy);
                if (isDestroy)
                {
                    _nodeGroupMap.Remove(groupId);
                    removeAsync.On(_ =>
                    {
                        Log.Debug($"NodeTree RemoveGroup Callback. GroupId:{groupId}");
                        (group as IDisposable)?.Dispose();
                    });
                }
            }
        }
        public IAsync AddAsync(NodeKey nodeKey, object intent)
        {
            return GetGroup(nodeKey.GroupId).AddAsync(nodeKey, intent);
        }

        public bool IsActive(NodeKey nodeKey)
        {
            return GetGroup(nodeKey.GroupId).IsActive(nodeKey);
        }

        public IAsync Preload(NodeKey nodeKey)
        {
            return GetGroup(nodeKey.GroupId).Preload(nodeKey);
        }

        public void Remove(NodeKey nodeKey, bool isDestroy)
        {
            GetGroup(nodeKey.GroupId).Remove(nodeKey, isDestroy);
        }
        void IUpdate.Update(int millisecond)
        {
            foreach(var impl in _updates)
            {
                impl.Update(millisecond);
            }
        }

        void ILateUpdate.LateUpdate(int millisecond)
        {
            foreach (var impl in _lateUpdate)
            {
                impl.LateUpdate(millisecond);
            }
        }
        void IDisposable.Dispose()
        {
            foreach(var item in _nodeGroupMap)
            {
                (item.Value as IDisposable)?.Dispose();
            }
            _nodeGroupMap.Clear();
            _updates.Clear();
            _lateUpdate.Clear();
            Root = null;
            (_context as IDisposable)?.Dispose();
            _context = null;
        }
        #endregion

        #region 内部方法
        private INodeGroup GetGroup(uint id)
        {
            if (!_nodeGroupMap.TryGetValue(id, out var group))
            {
                group = new NodeGroup(_context.Clone());
                _nodeGroupMap.Add(id, group);
                _updates.Add(group);
                _lateUpdate.Add(group);
            }
            return group;
        }

        #endregion
    }
}

