using HslCommunication.Core;
using HslCommunication.Core.Net;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace HslCommunication.Enthernet
{
    /// <summary>
    /// 高性能的异步网络服务器类,适合搭建局域网聊天程序,消息推送程序
    /// </summary>
    /// <remarks>
    /// 详细的使用说明,请参照博客<a href="http://www.cnblogs.com/dathlin/p/8097897.html">http://www.cnblogs.com/dathlin/p/8097897.html</a>
    /// </remarks>
    /// <example>
    /// 此处贴上了Demo项目的服务器配置的示例代码
    /// <code lang="cs" source="TestProject\ComplexNetServer\FormServer.cs" region="NetComplexServer" title="NetComplexServer示例" />
    /// </example>
    public class NetComplexServer : NetworkServerBase
    {
        #region Constructor

        /// <summary>
        /// 实例化一个网络服务器类对象
        /// </summary>
        public NetComplexServer()
        {
            appSessions = new List<AppSession>();
            lockSessions = new SimpleHybirdLock();
        }

        #endregion

        #region Private Member

        private int connectMaxClient = 1000;                                   // 允许同时登录的最大客户端数量
        private List<AppSession> appSessions = null;                           // 所有客户端连接的对象信息   
        private SimpleHybirdLock lockSessions = null;                          // 对象列表操作的锁

        #endregion

        #region Public Properties

        /// <summary>
        /// 所支持的同时在线客户端的最大数量,商用限制1000个,最小10个
        /// </summary>
        public int ConnectMax
        {
            get { return connectMaxClient; }
            set
            {
                if (value >= 10 && value < 1001)
                {
                    connectMaxClient = value;
                }
            }
        }

        /// <summary>
        /// 获取或设置服务器是否记录客户端上下线信息
        /// </summary>
        public bool IsSaveLogClientLineChange { get; set; } = true;

        /// <summary>
        /// 所有在线客户端的数量
        /// </summary>
        public int ClientCount => appSessions.Count;

        #endregion

        #region NetworkServerBase Override

        /// <summary>
        /// 初始化操作
        /// </summary>
        protected override void StartInitialization()
        {
            Thread_heart_check = new Thread(new ThreadStart(ThreadHeartCheck))
            {
                IsBackground = true,
                Priority = ThreadPriority.AboveNormal
            };
            Thread_heart_check.Start();
            base.StartInitialization();
        }

        /// <summary>
        /// 关闭网络时的操作
        /// </summary>
        protected override void CloseAction()
        {
            Thread_heart_check?.Abort();
            ClientOffline = null;
            ClientOnline = null;
            AcceptString = null;
            AcceptByte = null;

            //关闭所有的网络
            appSessions.ForEach(m => m.WorkSocket?.Close());
            base.CloseAction();
        }


        /// <summary>
        /// 异常下线
        /// </summary>
        /// <param name="session">会话信息</param>
        /// <param name="ex">异常</param>
        internal override void SocketReceiveException(AppSession session, Exception ex)
        {
            if (ex.Message.Contains(StringResources.Language.SocketRemoteCloseException))
            {
                //异常掉线
                TcpStateDownLine(session, false);
            }
        }


        /// <summary>
        /// 正常下线
        /// </summary>
        /// <param name="session">会话信息</param>
        internal override void AppSessionRemoteClose(AppSession session)
        {
            TcpStateDownLine(session, true);
        }



        #endregion

        #region Client Online Offline

        private void TcpStateUpLine(AppSession state)
        {
            lockSessions.Enter();
            appSessions.Add(state);
            lockSessions.Leave();

            // 提示上线
            ClientOnline?.Invoke(state);

            AllClientsStatusChange?.Invoke(ClientCount);
            // 是否保存上线信息
            if (IsSaveLogClientLineChange)
            {
                LogNet?.WriteInfo(ToString(), $"[{state.IpEndPoint}] Name:{ state?.LoginAlias } { StringResources.Language.NetClientOnline }");
            }
        }

        private void TcpStateClose(AppSession state)
        {
            state?.WorkSocket?.Close();
        }

        private void TcpStateDownLine(AppSession state, bool is_regular, bool logSave = true)
        {
            lockSessions.Enter();
            bool success = appSessions.Remove(state);
            lockSessions.Leave();

            if (!success) return;
            // 关闭连接
            TcpStateClose(state);
            // 判断是否正常下线
            string str = is_regular ? StringResources.Language.NetClientOffline : StringResources.Language.NetClientBreak;
            ClientOffline?.Invoke(state, str);
            AllClientsStatusChange?.Invoke(ClientCount);
            // 是否保存上线信息
            if (IsSaveLogClientLineChange && logSave)
            {
                LogNet?.WriteInfo(ToString(), $"[{state.IpEndPoint}] Name:{ state?.LoginAlias } { str }");
            }
        }

        #endregion

        #region Event Handle


        /// <summary>
        /// 客户端的上下限状态变更时触发,仅作为在线客户端识别
        /// </summary>
        public event Action<int> AllClientsStatusChange;

        /// <summary>
        /// 当客户端上线的时候,触发此事件
        /// </summary>
        public event Action<AppSession> ClientOnline;
        /// <summary>
        /// 当客户端下线的时候,触发此事件
        /// </summary>
        public event Action<AppSession, string> ClientOffline;
        /// <summary>
        /// 当接收到文本数据的时候,触发此事件
        /// </summary>
        public event Action<AppSession, NetHandle, string> AcceptString;
        /// <summary>
        /// 当接收到字节数据的时候,触发此事件
        /// </summary>
        public event Action<AppSession, NetHandle, byte[]> AcceptByte;



        #endregion

        #region Login Server

        /// <summary>
        /// 当接收到了新的请求的时候执行的操作
        /// </summary>
        /// <param name="socket">异步对象</param>
        /// <param name="endPoint">终结点</param>
        protected override void ThreadPoolLogin(Socket socket, IPEndPoint endPoint)
        {
            // 判断连接数是否超出规定
            if (appSessions.Count > ConnectMax)
            {
                socket?.Close();
                LogNet?.WriteWarn(ToString(), StringResources.Language.NetClientFull);
                return;
            }

            // 接收用户别名并验证令牌
            OperateResult result = new OperateResult();
            OperateResult<int, string> readResult = ReceiveStringContentFromSocket(socket);
            if (!readResult.IsSuccess) return;

            // 登录成功
            AppSession session = new AppSession()
            {
                WorkSocket = socket,
                LoginAlias = readResult.Content2,
            };


            try
            {
                session.IpEndPoint = (IPEndPoint)socket.RemoteEndPoint;
                session.IpAddress = ((IPEndPoint)socket.RemoteEndPoint).Address.ToString();
            }
            catch (Exception ex)
            {
                LogNet?.WriteException(ToString(), StringResources.Language.GetClientIpaddressFailed, ex);
            }

            if (readResult.Content1 == 1)
            {
                // 电脑端客户端
                session.ClientType = "Windows";
            }
            else if (readResult.Content1 == 2)
            {
                // Android 客户端
                session.ClientType = "Android";
            }


            try
            {
                session.WorkSocket.BeginReceive(session.BytesHead, session.AlreadyReceivedHead,
                    session.BytesHead.Length - session.AlreadyReceivedHead, SocketFlags.None,
                    new AsyncCallback(HeadBytesReceiveCallback), session);
                TcpStateUpLine(session);
                Thread.Sleep(100);// 留下一些时间进行反应
            }
            catch (Exception ex)
            {
                // 登录前已经出错
                TcpStateClose(session);
                LogNet?.WriteException(ToString(), StringResources.Language.NetClientLoginFailed, ex);
            }
        }

        #endregion

        #region SendAsync Support

        /// <summary>
        /// 服务器端用于数据发送文本的方法
        /// </summary>
        /// <param name="session">数据发送对象</param>
        /// <param name="customer">用户自定义的数据对象,如不需要,赋值为0</param>
        /// <param name="str">发送的文本</param>
        public void Send(AppSession session, NetHandle customer, string str)
        {
            SendBytes(session, HslProtocol.CommandBytes(customer, Token, str));
        }
        /// <summary>
        /// 服务器端用于发送字节的方法
        /// </summary>
        /// <param name="session">数据发送对象</param>
        /// <param name="customer">用户自定义的数据对象,如不需要,赋值为0</param>
        /// <param name="bytes">实际发送的数据</param>
        public void Send(AppSession session, NetHandle customer, byte[] bytes)
        {
            SendBytes(session, HslProtocol.CommandBytes(customer, Token, bytes));
        }

        private void SendBytes(AppSession session, byte[] content)
        {
            SendBytesAsync(session, content);
        }


        /// <summary>
        /// 服务端用于发送所有数据到所有的客户端
        /// </summary>
        /// <param name="customer">用户自定义的命令头</param>
        /// <param name="str">需要传送的实际的数据</param>
        public void SendAllClients(NetHandle customer, string str)
        {
            for (int i = 0; i < appSessions.Count; i++)
            {
                Send(appSessions[i], customer, str);
            }
        }

        /// <summary>
        /// 服务端用于发送所有数据到所有的客户端
        /// </summary>
        /// <param name="customer">用户自定义的命令头</param>
        /// <param name="data">需要群发客户端的字节数据</param>
        public void SendAllClients(NetHandle customer, byte[] data)
        {
            for (int i = 0; i < appSessions.Count; i++)
            {
                Send(appSessions[i], customer, data);
            }
        }

        /// <summary>
        /// 根据客户端设置的别名进行发送消息
        /// </summary>
        /// <param name="Alias">客户端上线的别名</param>
        /// <param name="customer">用户自定义的命令头</param>
        /// <param name="str">需要传送的实际的数据</param>
        public void SendClientByAlias(string Alias, NetHandle customer, string str)
        {
            for (int i = 0; i < appSessions.Count; i++)
            {
                if (appSessions[i].LoginAlias == Alias)
                {
                    Send(appSessions[i], customer, str);
                }
            }
        }


        /// <summary>
        /// 根据客户端设置的别名进行发送消息
        /// </summary>
        /// <param name="Alias">客户端上线的别名</param>
        /// <param name="customer">用户自定义的命令头</param>
        /// <param name="data">需要传送的实际的数据</param>
        public void SendClientByAlias(string Alias, NetHandle customer, byte[] data)
        {
            for (int i = 0; i < appSessions.Count; i++)
            {
                if (appSessions[i].LoginAlias == Alias)
                {
                    Send(appSessions[i], customer, data);
                }
            }
        }


        #endregion

        #region DataProcessingCenter

        /// <summary>
        /// 数据处理中心
        /// </summary>
        /// <param name="session">会话对象</param>
        /// <param name="protocol">消息的代码</param>
        /// <param name="customer">用户消息</param>
        /// <param name="content">数据内容</param>
        internal override void DataProcessingCenter(AppSession session, int protocol, int customer, byte[] content)
        {
            if (protocol == HslProtocol.ProtocolCheckSecends)
            {
                BitConverter.GetBytes(DateTime.Now.Ticks).CopyTo(content, 8);
                SendBytes(session, HslProtocol.CommandBytes(HslProtocol.ProtocolCheckSecends, customer, Token, content));
                session.HeartTime = DateTime.Now;
            }
            else if (protocol == HslProtocol.ProtocolClientQuit)
            {
                TcpStateDownLine(session, true);
            }
            else if (protocol == HslProtocol.ProtocolUserBytes)
            {
                //接收到字节数据
                AcceptByte?.Invoke(session, customer, content);
            }
            else if (protocol == HslProtocol.ProtocolUserString)
            {
                //接收到文本数据
                string str = Encoding.Unicode.GetString(content);
                AcceptString?.Invoke(session, customer, str);
            }
            else
            {
                // 其他一概不处理
            }
        }



        #endregion

        #region Heart Check

        private Thread Thread_heart_check { get; set; } = null;

        private void ThreadHeartCheck()
        {
            while (true)
            {
                Thread.Sleep(2000);

                try
                {
                    for (int i = appSessions.Count - 1; i >= 0; i--)
                    {
                        if (appSessions[i] == null)
                        {
                            appSessions.RemoveAt(i);
                            continue;
                        }

                        if ((DateTime.Now - appSessions[i].HeartTime).TotalSeconds > 1 * 8)//8次没有收到失去联系
                        {
                            LogNet?.WriteWarn(ToString(), StringResources.Language.NetHeartCheckTimeout + appSessions[i].IpAddress.ToString());
                            TcpStateDownLine(appSessions[i], false, false);
                            continue;
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogNet?.WriteException(ToString(), StringResources.Language.NetHeartCheckFailed, ex);
                }


                if (!IsStarted) break;
            }
        }



        #endregion

        #region Object Override

        /// <summary>
        /// 获取本对象的字符串表示形式
        /// </summary>
        /// <returns>字符串</returns>
        public override string ToString()
        {
            return "NetComplexServer";
        }

        #endregion

    }
}