﻿using System;
using System.Collections.Generic;

namespace JLGames.RocketDriver.Games.NetManager
{
    /// <summary>
    /// Connection instance management
    /// 连接实例管理
    /// </summary>
    public sealed class NetManager
    {
        private class EntityCache<T> where T : class, INetEntity
        {
            private T m_DefaultEntity;
            private readonly List<T> m_EntityList;
            private readonly List<T> m_Temp;

            public T DefaultEntity => m_DefaultEntity;

            public EntityCache()
            {
                m_EntityList = new List<T>();
                m_Temp = new List<T>();
            }

            public void RegisterEntity(T entity, bool @default)
            {
                if (null == entity) return;
                CacheToList(entity);
                if (@default) m_DefaultEntity = entity;
            }

            public void SetDefault(string serverName)
            {
                if (null != m_DefaultEntity && m_DefaultEntity.Name == serverName) return;
                var server = m_EntityList.Find(netServer => netServer.Name == serverName);
                if (null == server) return;
                m_DefaultEntity = server;
            }

            public T FindEntity(string clientName)
            {
                return m_EntityList.Find(client => client.Name == clientName);
            }

            public T[] GetEntities(Predicate<T> match)
            {
                m_Temp.Clear();
                m_EntityList.ForEach(obj =>
                {
                    if (match.Invoke(obj)) m_Temp.Add(obj);
                });
                return m_Temp.ToArray();
            }

            public void Clear()
            {
                for (var index = m_EntityList.Count - 1; index >= 0; index--)
                {
                    m_EntityList[index].Destroy();
                }

                m_EntityList.Clear();
                m_DefaultEntity = null;
            }

            public void Clear(T entity)
            {
                if (!m_EntityList.Contains(entity)) return;
                m_EntityList.Remove(entity);
                entity.Destroy();
            }

            private void CacheToList(T ele)
            {
                if (m_EntityList.Contains(ele)) return;
                var index = m_EntityList.FindIndex(obj => obj.Name == ele.Name);
                if (index == -1)
                {
                    m_EntityList.Add(ele);
                }
                else
                {
                    var old = m_EntityList[index];
                    old.Destroy();
                    m_EntityList[index] = ele;
                }
            }
        }

        public static NetManager Shared { get; } = new NetManager();

        private readonly EntityCache<INetClient> m_ClientCache;
        private readonly EntityCache<INetServer> m_ServerCache;

        public INetClient DefaultClient => m_ClientCache.DefaultEntity;
        public INetServer DefaultServer => m_ServerCache.DefaultEntity;

        private NetManager()
        {
            m_ClientCache = new EntityCache<INetClient>();
            m_ServerCache = new EntityCache<INetServer>();
        }

        /// <summary>
        /// Add the instance of INetEntity to the management
        /// Currently, instance management of INetClient and INetServer is supported.
        /// 把INetEntity的实例加入到管理中
        /// 当前支持INetClient与INetServer的实例管理
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="default"></param>
        public void Register(INetEntity entity, bool @default = false)
        {
            if (null == entity) return;
            if (entity is INetClient)
                m_ClientCache.RegisterEntity(entity as INetClient, @default);
            if (entity is INetServer)
                m_ServerCache.RegisterEntity(entity as INetServer, @default);
        }

        public void SetClientDefault(string clientName)
        {
            m_ClientCache.SetDefault(clientName);
        }

        public void SetServerDefault(string serverName)
        {
            m_ServerCache.SetDefault(serverName);
        }

        public T GetClient<T>(string clientName) where T : class, INetClient
        {
            return m_ClientCache.FindEntity(clientName) as T;
        }

        public T GetServer<T>(string serverName) where T : class, INetServer
        {
            return m_ServerCache.FindEntity(serverName) as T;
        }

        public INetClient[] GetClientsByGroup(string groupName)
        {
            return m_ClientCache.GetEntities(client => client.GroupName == groupName);
        }

        public INetServer[] GetServersByGroup(string groupName)
        {
            return m_ServerCache.GetEntities(server => server.GroupName == groupName);
        }

        public void ClearClient<T>(T client) where T : class, INetClient
        {
            m_ClientCache.Clear(client);
        }

        public void ClearClient(string clientName)
        {
            var client = m_ClientCache.FindEntity(clientName);
            if (null == client) return;
            m_ClientCache.Clear(client);
        }

        public void ClearServer<T>(T server) where T : class, INetServer
        {
            m_ServerCache.Clear(server);
        }

        public void ClearServer(string serverName)
        {
            var server = m_ServerCache.FindEntity(serverName);
            if (null == server) return;
            m_ServerCache.Clear(server);
        }

        public void Clear()
        {
            m_ServerCache.Clear();
            m_ClientCache.Clear();
        }
    }
}