using System;
using System.Buffers;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Sockets;
using System.Net.Sockets.Kcp;
using System.Threading;

using Fort23.Core;
using Fort23.UTool;

namespace Core.KCPTool
{
    public class KCPClient : IKcpCallback, IDisposable, IClientConnection
    {
        private Socket socket;

        private Thread _udpClientThread;

        public bool IsConnection { get; set; }
        public bool disconnect { get; set; }
        public long playerId;

        private byte[] buffer = new byte[2048];
        private byte[] sendBuffer = new byte[2048];


        private Thread _updateThread;
        private Thread _receiveThread;

        private IClient client;

        /// <summary>
        /// 上次心跳时间
        /// </summary>
        private long lasetGetHandshakeTime = 0;

        public SimpleSegManager.Kcp kcp
        {
            get { return _kcp; }
        }

        public IPEndPoint EndPoint { get; set; }

        private SimpleSegManager.Kcp _kcp;
        private bool isUpdate;
        public bool isAwaitReConnect { get; set; }

        private CTask _cTask;
        // private long _connectTime;

        public KCPClient()
        {
        }

        public async CTask Connect(IPEndPoint endPoint, long playerId, IClient client)
        {
            try
            {
                _cTask = CTask.Create(false);
                this.client = client;
                this.playerId = playerId;
                isUpdate = true;
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                socket.Connect(endPoint);
                _kcp = new SimpleSegManager.Kcp(0, this);
                kcp.NoDelay(1, 40, 2, 1);
                this.EndPoint = endPoint;
                _udpClientThread = new Thread(BeginRecv);
                _udpClientThread.Start();
                IsConnection = false;
                disconnect = false;
                // _connectTime = System.DateTime.Now.Ticks;
                byte[] data = SocketTool.LongToByte(playerId);
                socket.Send(data);
                // SendToServer(SendDataType.ShakeHands, data);
                _updateThread = new Thread(Update);
                _receiveThread = new Thread(ReceiveAsyncThread);
                _updateThread.Start();
                _receiveThread.Start();
                lasetGetHandshakeTime = 0;
                await _cTask;
            }
            catch (Exception e)
            {
                disconnect = true;
                LogTool.Exception(e);
                throw;
            }
        }

        public void ReConnect(IPEndPoint endPoint, int gameFrame)
        {
            // LogTool.Log("重新链接");
            // if (socket != null)
            // {
            //     socket.Dispose();
            //     socket = null;
            // }
            //
            // lasetGetHandshakeTime = 0;
            // // _udpClientThread = null;
            // Thread.Sleep(10);
            // socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            // socket.Connect(endPoint);
            // this.EndPoint = endPoint;
            // byte[] data = SocketTool.LongToByte(playerId);
            // socket.Send(data);
            // // SendToServer(SendDataType.ShakeHands, data);
            // IsConnection = false;
            // disconnect = false;
            // isAwaitReConnect = true;

            byte[] data = SocketTool.LongToByte(playerId);
            byte[] gameFrameByte = SocketTool.IntToByte(gameFrame);
            byte[] buffer = new byte[data.Length + gameFrameByte.Length];
            Array.Copy(data, 0, buffer, 0, data.Length);
            Array.Copy(gameFrameByte, 0, buffer, data.Length, gameFrameByte.Length);
            SendToServer(SendDataType.GetGameFrameByte, buffer);
        }


        public void Output(IMemoryOwner<byte> buffer, int avalidLength)
        {
            if (EndPoint == null || disconnect)
            {
                return;
            }

            var s = buffer.Memory.Span.Slice(0, avalidLength).ToArray();
            if (socket == null)
            {
                return;
            }

            try
            {
                socket.Send(s);
            }
            catch (Exception e)
            {
                // disconnect = true;
                // disconnect = true;
                // IsConnection = false;
                if (socket != null)
                {
                    socket.Dispose();
                    socket = null;
                }
            }

            buffer.Dispose();
        }

        public void SendToServer(SendDataType sendDataType, byte[] buffer)
        {
            byte[] sendBuff = new byte[buffer.Length + 1];
            sendBuff[0] = (byte)sendDataType;
            Array.Copy(buffer, 0, sendBuff, 1, buffer.Length);
            int c = kcp.Send(sendBuff.AsSpan().Slice(0, sendBuff.Length));
        }

        public void Finish()
        {
        }


        public byte[] ReceiveAsync()
        {
            var (buffer, avalidLength) = kcp.TryRecv();
            while (buffer == null)
            {
                Thread.Sleep(5);
                if (_udpClientThread == null)
                {
                    return null;
                }

                (buffer, avalidLength) = kcp.TryRecv();
            }

            var s = buffer.Memory.Span.Slice(0, avalidLength).ToArray();
            return s;
        }

        private async void BeginRecv()
        {
            while (isUpdate)
            {
                try
                {
                    if (socket == null)
                    {
                        Thread.Sleep(3);
                        continue;
                    }

                    if (socket.Receive(buffer, SocketFlags.Peek) <= 0)
                    {
                        Thread.Sleep(3);
                        continue;
                    }

                    int count = socket.Receive(buffer, SocketFlags.None);
                    if (count <= 0)
                    {
                        Thread.Sleep(3);
                        continue;
                    }

                    try
                    {
                        byte[] data = new byte[count];
                        Array.Copy(buffer, 0, data, 0, data.Length);
                        int ok = kcp.Input(data);
                    }
                    catch (Exception e)
                    {
                        LogTool.Exception(e);
                    }
                }
                catch (Exception e)
                {
                    LogTool.Log("链接断开");
                    // socket.Dispose();
                    socket = null;
                    disconnect = true;
                    IsConnection = false;
                }

                Thread.Sleep(3);
            }

            LogTool.Log("执行完成kcp_BeginRecv");
        }


        private void Update()
        {
            while (isUpdate)
            {
                try
                {
                    kcp.Update(DateTimeOffset.UtcNow);
                    if (IsConnection)
                    {
                        if (DateTime.Now.Ticks - lasetGetHandshakeTime > 70000000) //断开了链接
                        {
                            disconnect = true;
                            IsConnection = false;
                            return;
                        }
                    }
                    

                    Thread.Sleep(5);
                }
                catch (Exception e)
                {
                    LogTool.Error(e);
                }
            }

            LogTool.Log("执行完成kcp_Update");
        }

        private void ReceiveAsyncThread()
        {
            while (isUpdate)
            {
                try
                {
                    var res = ReceiveAsync();
                    if (res == null)
                    {
                        Thread.Sleep(10);
                        continue;
                    }

                    SendDataType sendDataType = (SendDataType)res[0];
                    byte[] decompressedByte = null;
                    if (sendDataType == SendDataType.Data)
                    {
                        byte[] jmData = new byte[res.Length - 1];
                        Array.Copy(res, 1, jmData, 0, jmData.Length);
                        using (MemoryStream compressedStream = new MemoryStream(jmData))
                        {
                            using (MemoryStream decompressedStream = new MemoryStream())
                            {
                                using (GZipStream gzipStream =
                                       new GZipStream(compressedStream, CompressionMode.Decompress))
                                {
                                    gzipStream.CopyTo(decompressedStream);
                                }

                                decompressedByte = decompressedStream.ToArray();
                            }
                        }
                    }
                    else
                    {
                        decompressedByte = res;
                    }


                    switch (sendDataType)
                    {
                        case SendDataType.ShakeHands:
                            byte[] playerData = new byte[8];
                            playerData[0] = decompressedByte[1];
                            playerData[1] = decompressedByte[2];
                            playerData[2] = decompressedByte[3];
                            playerData[3] = decompressedByte[4];
                            playerData[4] = decompressedByte[5];
                            playerData[5] = decompressedByte[6];
                            playerData[6] = decompressedByte[7];
                            playerData[7] = decompressedByte[8];
                            long playerId = SocketTool.ByteToInt(playerData);
                            if (playerId != this.playerId)
                            {
                                LogTool.Error("握手消息不对");
                            }

                            IsConnection = true;
                            isAwaitReConnect = false;
                            if (lasetGetHandshakeTime == 0)
                            {
                                lasetGetHandshakeTime = DateTime.Now.Ticks;
                            }

                            _cTask.SetResult();
                            break;
                        case SendDataType.Heartbeat:
                            if (!IsConnection)
                            {
                                LogTool.Log("链接成功");
                            }

                            isAwaitReConnect = false;
                            IsConnection = true;
                            lasetGetHandshakeTime = DateTime.Now.Ticks;
                            // LogTool.Log("收到心跳包");
                            break;
                        case SendDataType.Data:
                            // CombatSynchronizeResponsePack combatSynchronizeResponse =
                            //     CombatSynchronizeResponsePack.Parser.ParseFrom(decompressedByte);
                            // if (combatSynchronizeResponse == null)
                            // {
                            //     LogTool.Error("错误的实例化战斗" + decompressedByte.Length);
                            // }
                            // else
                            // {
                            //     // 
                            //     long t = System.DateTime.Now.Ticks;
                            //     // = (int) ((t - lasetTime) / 10000);
                            //     // lasetTime = t;
                            //     if (!IsConnection)
                            //     {
                            //         LogTool.Log("链接成功");
                            //     }
                            //
                            //     if (lasetGetHandshakeTime == 0)
                            //     {
                            //         lasetGetHandshakeTime = DateTime.Now.Ticks;
                            //     }
                            //
                            //     isAwaitReConnect = false;
                            //     IsConnection = true;
                            //     // LogTool.Log("收包延迟" + (t - combatSynchronizeResponse.Time) / 10000);
                            //     client.AddCombatSynchronizeResponse(combatSynchronizeResponse);
                            // }

                            break;
                    }
                    // await Task.Delay(Random.Next(0,200));
                }
                catch (Exception e)
                {
                    LogTool.Error(e);
                }
            }
        }

        public void Dispose()
        {
            isUpdate = false;
            // _udpClientThread?.Abort();
            _udpClientThread = null;
            socket?.Dispose();
            kcp?.Dispose();
            socket = null;
            _kcp = null;
        }
    }
}