using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;


namespace FObjBase
{
    public delegate bool ParsePacketHandler(byte[] packet, IFConn conn);
    public class TCPConn:IFConn
    {
        public bool HasCRC = false;

        TimeSpan Heartbeat_Interval = TimeSpan.FromSeconds(3);         // heartbeat包发送间隔时间，3秒
        TimeSpan Silent_Time = TimeSpan.FromSeconds(10);                // 单位ms没有收到任何东西的时间，10秒
        const int MAX_BUFFER = 20 * 0x4000;//20个大包， 大包的尺寸在FObjSys 定义
        protected List<byte> in_buffer = new List<byte>(MAX_BUFFER);
        protected int packet_start;
        protected int packet_len;

        protected List<byte> out_buffer = new List<byte>(MAX_BUFFER);
        protected DateTime comm_time;
        protected DateTime heartbeat_time = DateTime.Now;

        public ParsePacketHandler ParsePacket = null;

        public Socket sock;
      
        protected bool first_poll=true;

        public TCPConn() 
        {
        
        }
        public TCPConn(Socket sock) 
        {
            this.sock = sock;
        }

        public virtual int SendPacket(byte[] buffer)
        {
            int len = buffer.Length;
            out_buffer.AddRange(buffer);
            return len;
        }
        protected virtual int GetRecvInfoPacket()   // return length of the packet
        {
            int len = in_buffer.Count() - packet_start;
            if (len < 2) return 0;
            byte[] bs = new byte[2];
            
            int plen = BitConverter.ToUInt16(in_buffer.GetRange(packet_start, 2).ToArray(), 0);

            if (plen > 20000)
            { 
                //包太大，不正常！！！！！！
                //断开重新连接
                sock.Close();
                
                return -1;
            }

            if (len < plen)
            {
                return 0;
            }

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



            if (len >= plen)
            {
                return plen;
            }
            return 0;
        }
        protected virtual bool Parse_Packet()
        {
            byte[] packet = in_buffer.GetRange(packet_start, packet_len).ToArray();
            return ParsePacket(packet, this);
        }

        protected virtual int Clear_Packet()
        {
            if (packet_start > 0)
            {
                in_buffer.RemoveRange(0, packet_start);
            }
            return in_buffer.Count();
        }

        void Send_HeartBeat()
        {
            byte[] buf = new byte[2];
            buf[0] = 0x02;
            buf[1] = 0x00;
            SendPacket(buf);
            return;
        }
        int Send_Poll2() 
        {
            if (out_buffer.Count() == 0)
            {
                if ((DateTime.Now - heartbeat_time) > Heartbeat_Interval)
                {
                    Send_HeartBeat();
                }
                else
                    return 0;
            }

            heartbeat_time = DateTime.Now;
            return Send_Poll();
        }
        int Send_Poll()
        {
            int cnt_total = 0;
            if (out_buffer.Count() == 0)
                return 0;

            while (out_buffer.Count() > 0)
            {
                int cnt;
                try
                {
                    cnt = sock.Send(out_buffer.ToArray());
                }
                catch (System.Net.Sockets.SocketException e)
                {
                    if (e.ErrorCode == 10035)
                        break;
                    return -1;
                }

                if (cnt > 0)
                {
                    out_buffer.RemoveRange(0, cnt);
                    cnt_total+=cnt;
                }
                else
                {
                    break;
                }
            }
            return cnt_total;
        }
        
        int Receive_Poll()
        {
            int reclen_total = 0;
            while (true)
            {
                int reclen;
                try
                {
                    reclen = sock.Available;
                }
                catch (System.Net.Sockets.SocketException e)
                {
                    //FDEBUG.Debug.LogMessage(this, 10, "Receive_Poll e=" + e.ToString());
                    if (reclen_total == 0)
                        return -1;
                    else
                        return reclen_total;
                }

                if (reclen == 0)
                    return reclen_total;

                byte[] buf = new byte[reclen];
                try
                {
                    reclen = sock.Receive(buf);
                }
                catch (System.Net.Sockets.SocketException e)
                {
                    //FDEBUG.Debug.LogMessage(this, 10, "Receive_Poll e=" + e.ToString());
                    if (reclen_total == 0)
                        return -1;
                    else
                        return reclen_total;
                }
                if (reclen > 0)
                {
                    in_buffer.AddRange(buf);
                    reclen_total += reclen;

                    comm_time = DateTime.Now;
                }
            }
        }
        protected void Init() 
        {
            if (first_poll)
            {
                comm_time = DateTime.Now;
                heartbeat_time = DateTime.Now;
                first_poll = false;
            }
        }

        public virtual int OnPoll()
        {
            int ret=0;
            Init();

            int reclen = Receive_Poll();
            if (reclen < 0)
            {
                ret = -1;
                goto end;
            }
            else if (reclen == 0)
            {
                if ((DateTime.Now-comm_time) > Silent_Time)
                {
                    //FDEBUG.Debug.LogMessage(this, 10, "ERROR 超时出错!");
                    ret = -2;
                    goto end;
                }
            }
            else
            {
                packet_start = 0;
                while (true)
                {
                    packet_len = GetRecvInfoPacket();
                    if (packet_len > 0)
                    {
                        Parse_Packet();
                        packet_start += packet_len;
                    }
                    else if (packet_len == 0)
                    {
                        Clear_Packet();
                        break;
                    }
                    else 
                    {
                        Clear_Packet();
                        ret = -1;
                        goto end;
                    }
                }
            }
            int sendlen = Send_Poll2();

            if (sendlen < 0)
            {

                ret = -1;
                goto end;
            }
        end:

            if (ret != 0)
            { 
                //连接断开
                Enable = false;
            }
            return ret;
        }

        #region IFConn 成员

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


        protected bool enable=false;
        public bool Enable
        {
            get
            {
                return enable;
            }
            set
            {
                enable = value;
                if (enable == false) 
                {
                    Stop();
                }
            }
        }
        protected virtual void Stop() 
        {
            if (sock != null)
                sock.Close();
            IsConnected = false;
        } 
        #endregion

        #region IFConn 成员


        public uint TranID
        {
            get;
            set;
        }

        #endregion
    }
}
