using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;

namespace FObjBase
{
    public delegate bool ParsePacketHandler(byte[] packet, IFConn conn);
    public class TCPConn:IFConn
    {
        static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
        static TimeSpan Heartbeat_Interval = TimeSpan.FromSeconds(600);         // heartbeat包发送间隔时间，3秒
        static TimeSpan Silent_Time = TimeSpan.FromSeconds(600);                // 单位ms没有收到任何东西的时间，10秒

        public ParsePacketHandler ParsePacket { get; set; }
        public ConnectHandler ConnectAction { get; set; }
        public event ClosedHandler Closed;

        public bool HasCRC { get; set; } = false;

        protected bool isconnected = false;
        public bool IsConnected
        {
            get
            {
                return isconnected;
            }
            set
            {
                if (isconnected != value)
                {
                    isconnected = value;
                    ConnectAction?.Invoke(this);
                }
            }
        }


        public bool IsRunning { get; set; }


        List<byte> in_buffer = new List<byte>();

        List<byte> out_buffer = new List<byte>();

        Socket socket;

        /// <summary>
        /// 强制 stop
        /// </summary>
        CancellationTokenSource cancellation;
        /// <summary>
        /// 等待 out_buffer 加入数据
        /// </summary>
        CancellationTokenSource waitOutBufCancellation;

        public TCPConn() 
        {
        
        }
        public TCPConn(Socket sock) 
        {
            socket = sock;
            socket.ReceiveTimeout = (int)Silent_Time.TotalMilliseconds;
        }



        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buffer"></param>
        public void SendPacket(byte[] buffer)
        {
            lock (out_buffer)
            {
                out_buffer.AddRange(buffer);
                if(waitOutBufCancellation!=null && waitOutBufCancellation.Token.CanBeCanceled)
                    waitOutBufCancellation.Cancel();
            }
        }

        int GetRecvInfoPacket()
        {
            int len = in_buffer.Count();
            if (len < 2) return 0;
            byte[] bs = new byte[2];

            int plen = BitConverter.ToUInt16(in_buffer.GetRange(0, 2).ToArray(), 0);

            if (plen > 0x4000 * 2)
            {
                //包太大，不正常！！！！！！
                //断开重新连接
                logger.Error("TCPConn GetRecvInfoPacket 包太大，不正常, 断开重新连接");
                socket.Close();

                return -1;
            }

            if (len < plen)
            {
                return 0;
            }

            if (HasCRC) //TODO
            {
                UInt16 crc = Misc.CRC.CRC16(in_buffer, 0, plen - 2);
                int packet_crc_idx = plen - 2;
                UInt16 packet_crc = BitConverter.ToUInt16(in_buffer.GetRange(packet_crc_idx, 2).ToArray(), 0);
                if (crc != packet_crc)
                {
                    logger.Error("TCPConn GetRecvInfoPacket CRC != packet_crc 断开重新连接");
                    //断开重新连接
                    socket.Close();
                    return -1;
                }
            }



            if (len >= plen)
            {
                return plen;
            }
            return 0;
        }
 
        bool Parse_Packet(int len)
        {
            byte[] packet = in_buffer.GetRange(0, len).ToArray();
            in_buffer.RemoveRange(0, len);
            return ParsePacket(packet, this);
        }
        
        void Send_HeartBeat()
        {
            byte[] buf = new byte[2];
            buf[0] = 0x02;
            buf[1] = 0x00;
            SendPacket(buf);
            return;
        }

        bool Send_Poll()
        {
            while (true)//只有有数据发送，且能发送，没有 block， 就会一直循环
            {
                byte[] buf;
                lock (out_buffer)
                {
                    if (out_buffer.Count() == 0)//没数据，直接返回
                    {
                        if (waitOutBufCancellation == null || waitOutBufCancellation.IsCancellationRequested)
                            waitOutBufCancellation = new CancellationTokenSource();
                        return true;
                    }
                    buf = out_buffer.ToArray();
                }

                int cnt;
                try
                {
                    cnt = socket.Send(buf);
                }
                catch (System.Net.Sockets.SocketException e)
                {
                    //if (e.SocketErrorCode == SocketError.WouldBlock)//当前发不了，退出循环，等下次！！！！
                    //    break;//不应该出现这样！！！！！ 现在是Block
                    return false;//异常，断开连接！！！
                }

                if (cnt > 0)
                {
                    lock (out_buffer) { 
                        out_buffer.RemoveRange(0, cnt);//发送成功，删除！！！
                    }
                }
            }  
        }
        
        bool Receive_Poll()
        {
            try
            {
                byte[] buf = new byte[socket.ReceiveBufferSize];
                int reclen = socket.Receive(buf);
                if (reclen > 0)
                {
                    in_buffer.AddRange(buf.Take(reclen));
                }
                return true;//长度不应该为0
            }
            catch(SocketException e)
            {
                if (e.SocketErrorCode == SocketError.TimedOut)
                {
                    //长时间没收到数据
                    //异常，断开连接
                    return false;
                }
                else {
                    //其它异常, 也是要断开连接
                    return false;
                }
            }
        }


        #region IFConn 成员


        public void Stop() 
        {
            if (socket != null)
                socket.Close();
            if (cancellation != null && cancellation.Token.CanBeCanceled) {
                cancellation.Cancel();
            }
            IsConnected = false;
        }

        public void Start()
        {
            if (IsRunning)
                return;

            IsRunning = true;
            cancellation = new CancellationTokenSource();
            waitOutBufCancellation = new CancellationTokenSource();
            Task.Factory.StartNew(() =>
            {
                while (!cancellation.IsCancellationRequested)
                {

                    bool ret = Send_Poll();//每发完一次, 出来
                    if (!ret)
                    {
                        //socket 异常！！！断开连接
                        break;
                    }
                    try
                    {
                        Task.Delay(Heartbeat_Interval).Wait(waitOutBufCancellation.Token);
                    }
                    catch
                    {
                        //到时间了，或者 有数据需要发送
                    }
                    if (waitOutBufCancellation.Token.IsCancellationRequested)
                    {
                        //有数据需要发送
                    }
                    else
                    {
                        //到时间了
                        Send_HeartBeat();
                    }
                }
                //结束！！！ socket 被关闭了
                // 通知外部！！！！！！！
                lock (this)
                {
                    if (IsRunning)
                    {
                        IsRunning = false;
                        Closed?.Invoke(this);
                    }
                }
            }, cancellation.Token);



            cancellation = new CancellationTokenSource();
            Task.Factory.StartNew(() =>
            {
                while (!cancellation.IsCancellationRequested)
                {
                    if(!Receive_Poll())//每接收一次, 出来
                        break;//异常


                    //信息处理
                    while (true)
                    {
                        int packet_len = GetRecvInfoPacket();
                        if (packet_len > 0)
                        {
                            Parse_Packet(packet_len);
                        }
                        else if (packet_len == 0)
                        {
                            break;
                        }
                        else
                        {
                            //异常
                            goto _end;
                        }
                    }
                }
                _end:

                //结束！！！ socket 被关闭了
                //通知外部！！！！！！！
                lock (this) 
                {
                    if (IsRunning)
                    {
                        IsRunning = false;
                        Closed?.Invoke(this);
                    }
                }
                
            }, cancellation.Token);
        }





        #endregion
    }

}
