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

namespace FObjBase
{
    //客户端长时间不通讯，必须发送 Send_HeartBeat
    public class TCPCConn : IFConn
    {
        static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
               // 单位ms没有收到任何东西的时间，10秒

        public ParsePacketHandler ParsePacket { get; set; }
        public ConnectHandler ConnectAction { get; set; }

        public event ClosedHandler Closed;

        private bool hasCrc;
        public bool HasCRC {
            get { return hasCrc; }
            set {
                hasCrc = value;
                if (conn != null) {
                    conn.HasCRC = hasCrc;
                }
            }
        }

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


        public bool IsRunning { get; private set; }

        Socket socket;

        /// <summary>
        /// 强制 stop
        /// </summary>
        CancellationTokenSource cancellation;


        enum STATE 
        {
            Init,
            Connected,
            Connect,
            Err_Connect    
        }

        private STATE state;
        private DateTime conn_delay_time;

        private IPEndPoint ep = new IPEndPoint(IPAddress.Any, 20006);
        /// <summary>
        /// 远端地址
        /// </summary>
        public IPEndPoint RemoteEP
        {
            get
            {
                return new IPEndPoint(ep.Address, ep.Port);
            }
            set
            {
                ep = new IPEndPoint(value.Address, value.Port);
            }
        }




        TCPConn conn;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="ipep"></param>
        public TCPCConn(IPEndPoint ipep)
        {
            RemoteEP = ipep;

        }

        public void Stop()
        {
            if (!IsRunning)
                return;

            cancellation.Cancel();

            socket.Close();//就算是 真实conn 也会退出

            IsConnected = false;

            IsRunning = false;

            Closed?.Invoke(this);
        }
        
        public void Start()
        {
            if (IsRunning)
                return;
            cancellation = new CancellationTokenSource();
            state = STATE.Init;

            Task.Factory.StartNew(() =>
            {
                while (!cancellation.IsCancellationRequested)
                {
                    OnPoll();
                    if (state == STATE.Connected) {
                        //连接成功
                        //已经创建了真实通信 conn
                        //这个connect动作的线程结束
                        break;
                    }
                }

                if (cancellation.IsCancellationRequested) {
                    //退出是由于被强制关闭
                    //socket已经释放资源了
                    //IsRunning = false;
                }
            }, cancellation.Token);
        }
        void Restart() {
            state = STATE.Init;
            Task.Factory.StartNew(() =>
            {
                while (cancellation.IsCancellationRequested)
                {
                    OnPoll();
                    if (state == STATE.Connected)
                    {
                        //连接成功
                        //已经创建了真实通信 conn
                        //这个connect动作的线程结束
                        break;
                    }
                }

                if (cancellation.IsCancellationRequested)
                {
                    //退出是由于被强制关闭
                    //socket已经释放资源了
                    IsRunning = false;
                }
            }, cancellation.Token);
        }

        public void OnPoll()
        {
            switch (state)
            {
                case STATE.Init:
                    {
                        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        state = STATE.Connect;
                    }
                    break;
                case STATE.Connect:
                    try
                    {
                        socket.Connect(RemoteEP);
                        state = STATE.Connected;
                        //连接成功
                        //创建真实通信的 conn, 把socket传递给它，整个模式与listen一样
                        conn = new TCPConn(socket)
                        {
                           ParsePacket = ParsePacket,
                           ConnectAction = null,
                           IsConnected = true,
                           HasCRC = HasCRC
                        };
                        conn.Closed += Conn_Closed;
                        conn.Start();

                        IsConnected = true;
                    }
                    catch (SocketException e)
                    {
                        state = STATE.Err_Connect;
                        break;
                    }
                    break;
                case STATE.Err_Connect:
                    {
                        try
                        {
                            Task.Delay(TimeSpan.FromSeconds(5)).Wait(cancellation.Token);
                        }
                        catch {
                            
                        }
                        state = STATE.Connect;
                    }
                    break;
            }
        }

        private void Conn_Closed(IFConn conn)
        {
            IsConnected = false;

            //正常通信中， 连接断开了，判断是否 被按Stop() 导致的

            if (cancellation.IsCancellationRequested)
            {
                //被按 Stop() 了
                //什么都不需要干， socket 肯定也被是否资源了
                return;
            }

            //需要重连！！！！
            Restart();
        }

        public void SendPacket(byte[] buffer)
        {
            if (conn != null)
            {
                conn.SendPacket(buffer);
            }
        }
    }

   
}
