using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Threading.Channels;
using System.Threading.Tasks;
using ProtoDataBuff;
using pb = global::Google.Protobuf;

using MultiDup;
namespace BattleRoom
{
    internal class ServerPeer
    {

        /// <summary>
        /// socket连接
        /// </summary>
        public readonly Socket Sock;

        public Action OnSocketDisconnected;
        /// <summary>
        /// 消息分发
        /// </summary>
        private Dictionary<eProtocalCommand, Action<sSocketData>> callbacks = new Dictionary<eProtocalCommand, Action<sSocketData>>();
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="sock"></param>
        public ServerPeer(Socket sock)
        {
            this.Sock = sock;
            Sock.ReceiveTimeout = 1500;                                         // 接收等待超时时间设为1.5秒 

            callbacks.Add(eProtocalCommand.SsCreateBattleRoom, On_CreateBattleRoom);

            var t = Task.Run(() => recv(Sock));                                 // 新建接收线程 
            var tcs = Task.Run(WriteToClient);                                  // 向客户端发送消息线程
            var tmsglp = Task.Run(MsgLoop);
            OnSocketDisconnected += () => { 
                Console.WriteLine("socket连接已断开!"); 
                sock.Close();
                sock = null;
            };
        }

        #region 逻辑处理

        public void On_CreateBattleRoom(sSocketData _sData)
        {
            var msg = SS_CreateBattleServer.Parser.ParseFrom(_sData._data);
            RoomManager.Instance.CreateRoom(_sData);                           // 开辟一个处理对象, room, 然后后续逻辑由room对象等待连接过来. 
            this.SendEvent(eProtocalCommand.SsCreateBattleRoomOk, new SS_CreateBattleServerOK() { });// 返回成功
        }


        #endregion

        /// <summary>
        /// 关闭连接
        /// </summary>
        public void Close()
        {
            this.sendDataBuffer.Reader.Completion
                .ContinueWith(t =>
                Task.Delay(5000).ContinueWith(t1 => Sock.Close())
                );                                                               // 延迟5秒关闭套接字
        }

        #region 消息收发

        async void MsgLoop()
        {
            while (true)
            {
                var msg = await MsgChannel.Reader.ReadAsync();
                if (callbacks.ContainsKey(msg._protocallType))
                {
                    callbacks[msg._protocallType](msg);
                }
                else
                {
                    // 未找到消息处理逻辑
                }
            }
        }
        /// <summary>
        /// 客户端消息队列
        /// </summary>
        public Channel<sSocketData> MsgChannel = Channel.CreateBounded<sSocketData>(1000);

        /// <summary>
        /// 向客户端发送事件
        /// </summary>
        public void SendEvent(eProtocalCommand msgType, pb::IMessage msg)
        {
            SendToClient(msgType, msg);
        }

        /// <summary>
        /// 向客户端发送消息
        /// </summary>
        /// <param name="msgType"></param>
        /// <param name="msg"></param>
        void SendToClient(eProtocalCommand msgType, pb::IMessage msg)
        {
            using var ms = new MemoryStream();
            using var os = new pb::CodedOutputStream(ms);
            msg.WriteTo(os);
            os.Flush();
            ms.Seek(0, SeekOrigin.Begin);
            var sdata = sSocketData.FromBytes(msgType, ms.ToArray());
            sendDataBuffer.Writer.WriteAsync(sdata).AsTask().Wait();
        }


        /// <summary>
        /// 向客户端写入消息
        /// </summary>
        async void WriteToClient()
        {
            while (true)
            {
                try
                {
                    var msg = await sendDataBuffer.Reader.ReadAsync();
                    await Sock.SendAsync(new ArraySegment<byte>(msg.ToBytes()), SocketFlags.None);
                }
                catch (Exception)
                {
                    break;
                }

            }
            Close();
        }


        /// <summary>
        /// 发送buffer
        /// </summary>
        private Channel<sSocketData> sendDataBuffer = Channel.CreateUnbounded<sSocketData>();

        /// <summary>
        /// 接收客户端发来的信息，客户端套接字对象
        /// </summary>
        /// <param name="socketclientpara"></param>    
        async void recv(Socket socketServer)
        {
            var _databuffer = new DataBuffer();
            byte[] arrServerRecMsg = new byte[4096];                                       // 创建一个内存缓冲区，其大小为4k字节  
            while (true)
            {
                try
                {
                    var length = await socketServer.ReceiveAsync(new ArraySegment<byte>(arrServerRecMsg), SocketFlags.None);   // 将接收到的信息存入到内存缓冲区，并返回其字节数组的长度    
                    if (length <= 0)                                                       // 视为客户端已经close连接
                    {
                        OnSocketDisconnected?.Invoke();
                        break;

                    }
                    _databuffer.AddBuffer(arrServerRecMsg, length);                         //将收到的数据添加到缓存器中
                    while (_databuffer.GetData(out sSocketData _socketData))                //取出一条完整数据,转到room的消息队列,由room统一进行逻辑处理 
                    {
                        Console.WriteLine(" recv: " + _socketData._protocallType);
                        await MsgChannel.Writer.WriteAsync(_socketData);                    // 放入大厅处理队列
                    }
                    Array.Clear(arrServerRecMsg, 0, length);
                }
                catch (SocketException e)
                {
                    if (e.ErrorCode == 10060)                                                  // 超时的时候错误号码是10060
                    {
                        continue;                                                              // 继续等待  
                    }
                    break;
                }
                catch (Exception)
                {
                    break;
                }
            }

            Close();                                                                 // 关闭
        }
        #endregion
    }
}
