using System;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace WS
{
    ///<summary>TCP Socket链接代理</summary>
    public sealed class TChannel : AChannel
    {
        private Socket socket;
        ///<summary>接收数据Socket异步操作</summary>
        private SocketAsyncEventArgs innArgs = new SocketAsyncEventArgs();
        ///<summary>发送Socket异步操作</summary>
        private SocketAsyncEventArgs outArgs = new SocketAsyncEventArgs();
        ///<summary>接收数据流</summary>
        private readonly CircularBuffer recvBuffer = new CircularBuffer();
        ///<summary>发送数据流</summary>
        private readonly CircularBuffer sendBuffer = new CircularBuffer();
        private bool isSending;
        ///<summary>是否正在发送</summary>
        public bool IsSending => isSending;
        ///<summary>是否正在接收</summary>
        private bool isRecving;
        ///<summary>是否链接</summary>
        private bool isConnected;
        ///<summary>包体解析器</summary>
        private readonly PacketParser parser;
        private readonly MemoryStream memoryStream;
        ///<summary>流操作</summary>
        public override MemoryStream Stream => memoryStream;
        ///<summary>获取TService</summary>
        private TService GetService { get { return (TService)Service; } }

        //客户端创建Socket链接
        public TChannel(IPEndPoint ipEndPoint, TService service) : base(service, ChannelType.Connect)
        {
            int packetSize = service.PacketSizeLength;//包头长度
            memoryStream = service.MemoryStreamManager.GetStream("message", ushort.MaxValue);//创建一个流操作
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//链接服务器
            socket.NoDelay = true;//使用 Nagle 算法（通过减少必须发送包的个数来增加网络软件系统的效率）
            parser = new PacketParser(packetSize, recvBuffer, memoryStream);//创建包解析器
            innArgs.Completed += OnComplete;//注册异步操作回调
            outArgs.Completed += OnComplete;//注册异步操作回调
            RemoteAddress = ipEndPoint;
            isConnected = false;//没有链接成功
            isSending = false;//没有在发送数据
        }

        //服务器接收到的客户端Socket链接
        public TChannel(Socket socket, TService service) : base(service, ChannelType.Accept)
        {
            int packetSize = service.PacketSizeLength;//包头长度
            memoryStream = service.MemoryStreamManager.GetStream("message", ushort.MaxValue);//创建一个流操作
            this.socket = socket;
            socket.NoDelay = true;//使用 Nagle 算法（通过减少必须发送包的个数来增加网络软件系统的效率）
            parser = new PacketParser(packetSize, recvBuffer, memoryStream);
            innArgs.Completed += OnComplete;//注册异步操作回调
            outArgs.Completed += OnComplete;//注册异步操作回调
            RemoteAddress = (IPEndPoint)socket.RemoteEndPoint;
            isConnected = true;//链接成功
            isSending = false;//没有在发送数据
        }

        public override void Dispose()
        {
            base.Dispose();

            socket?.Close();
            innArgs?.Dispose();
            outArgs?.Dispose();
            innArgs = null;
            outArgs = null;
            socket = null;
            memoryStream.Dispose();
        }

        ///<summary>初始化</summary>
        public override void Start()
        {
            if (!isConnected)//没有链接（客户端情况）
            {
                ConnectAsync(RemoteAddress);//链接服务器
                return;
            }

            if (!isRecving)//开始接收
            {
                isRecving = true;
                StartRecv();
            }
            GetService.MarkNeedStartSend(InstanceId);
        }

        public override void Send(MemoryStream stream)
        {
            if (IsDisposed)
                throw new Exception("TChannel已经被Dispose, 不能发送消息");

            switch (GetService.PacketSizeLength)
            {
                case Packet.PacketSizeLength4:
                    if (stream.Length > ushort.MaxValue * 16)
                        throw new Exception($"send packet too large: {stream.Length}");
                    break;
                case Packet.PacketSizeLength2:
                    if (stream.Length > ushort.MaxValue)
                        throw new Exception($"send packet too large: {stream.Length}");
                    break;
                default:
                    throw new Exception("packet size must be 2 or 4!");
            }
            sendBuffer.Write(stream);
            GetService.MarkNeedStartSend(InstanceId);//提交到发送消息列表
        }

        ///<summary>异步操作回调</summary>
        private void OnComplete(object sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Connect:
                    OneThreadSynchronizationContext.Instance.Post(OnConnectComplete, e);
                    break;
                case SocketAsyncOperation.Receive:
                    OneThreadSynchronizationContext.Instance.Post(OnRecvComplete, e);
                    break;
                case SocketAsyncOperation.Send:
                    OneThreadSynchronizationContext.Instance.Post(OnSendComplete, e);
                    break;
                case SocketAsyncOperation.Disconnect:
                    OneThreadSynchronizationContext.Instance.Post(OnDisconnectComplete, e);
                    break;
                default:
                    throw new Exception($"socket error: {e.LastOperation}");
            }
        }

        ///<summary>客户端链接服务器</summary>
        public void ConnectAsync(IPEndPoint ipEndPoint)
        {
            outArgs.RemoteEndPoint = ipEndPoint;
            if (socket.ConnectAsync(outArgs))//开始链接服务器=>如果返回值为true 表明挂起异步等待返回，如果返回值为false 表明回调完成跳过回调直接执行
            {
                return;
            }
            OnConnectComplete(outArgs);
        }

        ///<summary>>客户端链接服务器完成回调</summary>
        private void OnConnectComplete(object o)
        {
            if (socket == null) return;

            SocketAsyncEventArgs e = (SocketAsyncEventArgs)o;

            if (e.SocketError != SocketError.Success)//是否链接成功
            {
                OnError((int)e.SocketError);
                return;
            }

            e.RemoteEndPoint = null;
            isConnected = true;//Socket链接成功
            OnLink();//链接成功回调
            Start();//链接成功继续执行Start逻辑
        }

        private void OnDisconnectComplete(object o)
        {
            SocketAsyncEventArgs e = (SocketAsyncEventArgs)o;
            OnError((int)e.SocketError);
        }

        ///<summary>开始接收消息</summary>
        private void StartRecv()
        {
            try
            {
                int size = recvBuffer.ChunkSize - recvBuffer.LastIndex;//得到现在能使用的长度
                innArgs.SetBuffer(recvBuffer.Last, recvBuffer.LastIndex, size);//初始化要用于异步套接字方法的数据缓冲区
            }
            catch (Exception e)
            {
                throw new Exception("socket set buffer error", e);
            }

            if (socket.ReceiveAsync(innArgs))//接收Socket数据=>如果返回值为true 表明挂起异步等待返回，如果返回值为false 表明回调完成跳过回调直接执行
            {
                return;
            }
            OnRecvComplete(innArgs);
        }

        ///<summary>接收数据成功回调</summary>
        private void OnRecvComplete(object o)
        {
            if (socket == null) return;

            SocketAsyncEventArgs e = (SocketAsyncEventArgs)o;

            if (e.SocketError != SocketError.Success)//是否错误
            {
                OnError((int)e.SocketError);
                return;
            }

            if (e.BytesTransferred == 0)//获取到数据的字节数
            {
                OnError(ErrorCode.ERR_PeerDisconnect);
                return;
            }

            recvBuffer.LastIndex += e.BytesTransferred;//加上获取到的字节数
            if (recvBuffer.LastIndex == recvBuffer.ChunkSize)//如果LastIndex已经用完
            {
                recvBuffer.AddLast();
                recvBuffer.LastIndex = 0;
            }

            // 收到消息回调
            while (true)
            {
                try
                {
                    if (!parser.Parse()) break;//解析数据包 直到解析完成
                }
                catch (Exception ee)
                {
                    Log.LogError($"ip: {RemoteAddress} {ee}");
                    OnError(ErrorCode.ERR_SocketError);
                    return;
                }

                try
                {
                    OnRead(parser.GetPacket());//解析完成执行回调事件
                }
                catch (Exception ee)
                {
                    Log.LogError(ee);
                }
            }

            if (socket == null) return;

            StartRecv();//继续接收
        }

        ///<summary>开始发送消息</summary>
        public void StartSend()
        {
            if (!isConnected) return;//是否链接

            if (sendBuffer.Length == 0)// 没有数据需要发送
            {
                isSending = false;
                return;
            }

            isSending = true;//正在发送

            int sendSize = sendBuffer.ChunkSize - sendBuffer.FirstIndex;//得到需要发送的数据长度
            if (sendSize > sendBuffer.Length)//判断是否超出限制
            {
                sendSize = (int)sendBuffer.Length;
            }
            try
            {
                outArgs.SetBuffer(sendBuffer.First, sendBuffer.FirstIndex, sendSize);//初始化要用于异步套接字方法的数据缓冲区
            }
            catch (Exception e)
            {
                throw new Exception("socket set buffer error", e);
            }
            if (socket.SendAsync(outArgs))//开始发送数据=>如果返回值为true 表明挂起异步等待返回，如果返回值为false 表明回调完成跳过回调直接执行
            {
                return;
            }
            OnSendComplete(outArgs);
        }

        ///<summary>发送数据回调</summary>
        private void OnSendComplete(object o)
        {
            if (socket == null) return;

            SocketAsyncEventArgs e = (SocketAsyncEventArgs)o;

            if (e.SocketError != SocketError.Success)//是否成功
            {
                OnError((int)e.SocketError);
                return;
            }

            if (e.BytesTransferred == 0)//获取到数据的字节数
            {
                OnError(ErrorCode.ERR_PeerDisconnect);
                return;
            }

            sendBuffer.FirstIndex += e.BytesTransferred;
            if (sendBuffer.FirstIndex == sendBuffer.ChunkSize)
            {
                sendBuffer.FirstIndex = 0;
                sendBuffer.RemoveFirst();
            }

            StartSend();//继续发送
        }
    }
}