using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SystemX.Common;
using SystemX.Common.Serialization;

namespace SystemX.Net.Comm
{
    //
    // 요약:
    //     Socket error information
    public class CommonSocketException : Exception
    {
        public CommonSocketException()
        {
        }

        public CommonSocketException(string message)
            : base(message)
        {
        }

        public CommonSocketException(string message, Exception inner)
            : base(message, inner)
        {
        }
    }

    public enum SOCKET_RULE
    {
        NONE = -1,
        SERVER = 0,
        CLIENT = 1
    }

    public enum SOCKET_TYPE
    { 
        NONE = -1,
        TCP = 0,
        UDP = 1
    }

    public class SystemXSocket : IDisposable
    {
        public virtual event SocketCallEvent Comm_Connect_Event;
        public virtual event SocketCallEvent Socket_Error_Event;

        public virtual event SendRecvCallEvent Comm_Send_Event;
        public virtual event SendRecvCallEvent Comm_Recv_Event;

        public virtual event EventHandler<ScheduleEvent> AwaitSendEvent;
        public virtual event EventHandler<ScheduleEvent> AwaitRecvEvent;

        public int nSocketNumber;

        private bool bDisposed;

        public SOCKET_RULE SOCK_RULE { set; get; }

        public SOCKET_TYPE SOCK_TYPE { set; get; }


        public SystemXSocket(SOCKET_RULE GET_SOCK_RULE, SOCKET_TYPE GET_SOCK_TYPE) {
            SOCK_RULE = GET_SOCK_RULE;

            SOCK_TYPE = GET_SOCK_TYPE;

            bDisposed = false; 
        }

        public void Dispose()
        {
            Dispose(true);

            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool bDisposing)
        {
            if(bDisposed == false)
            {
                if(bDisposing)
                {
                    //Manage
                }

                //Unmanage

                bDisposed = true;
            }
        }

        ~SystemXSocket()
        {
            Dispose(false);
        }

        public virtual bool BeginSend(byte[] ucDatas) { return true; }
        public virtual bool BeginSend(Socket SetSock, byte[] ucDatas) { return true; }
        public virtual bool BeginSend(string strMessage) { return true; }

        public virtual void SetRefSocketPacketClear() { return; }
    }

    public class CPacketDataInfo
    {
        public Guid ID;
        public DateTime dtPacket;
        public byte[] ucData;
        public int nDataSize;

        public CPacketDataInfo(DateTime dateTime, byte[] ucGetData, int nGetSize)
        {
            if (ID == default(Guid))
                ID = Guid.NewGuid();

            dtPacket = dateTime;

            nDataSize = nGetSize;

            ucData = new byte[nGetSize];

            Buffer.BlockCopy(ucGetData, 0, ucData, 0, nGetSize);
        }
        public CPacketDataInfo(Guid SetID, DateTime dateTime, byte[] ucGetData, int nGetSize)
        {
            ID = SetID;

            dtPacket = dateTime;

            nDataSize = nGetSize;

            ucData = new byte[nGetSize];

            Buffer.BlockCopy(ucGetData, 0, ucData, 0, nGetSize);
        }
    }

    public class AsyncSocketObject
    {
        public byte[] recvBuffer;
        public Socket WorkSocket;
        public readonly int iBufferSize;

        public AsyncSocketObject(int ibufferSize)
        {
            iBufferSize = ibufferSize;

            recvBuffer = new byte[iBufferSize];
        }

        public void ClearBuffer()
        {
            Array.Clear(recvBuffer, 0, iBufferSize);
        }
    }

    public class TcpKeepAlive
    {
        public uint OnOff { get; set; }
        public uint KeepAliveTime { get; set; }
        public uint KeepAliveInterval { get; set; }

        public byte[] GetBytes()
        {
            return BitConverter.GetBytes(OnOff).Concat(BitConverter.GetBytes(KeepAliveTime)).Concat(BitConverter.GetBytes(KeepAliveInterval)).ToArray();
        }
    }

    public static class ConnectTokken
    {
        public enum Connect_Token
        {
            T010 = 0,
            T020 = 1,
            T030 = 2,
            T040 = 3,
            T050 = 4,
            T060 = 5,
            T070 = 6,
            T080 = 7,
            T090 = 8,
            T100 = 9,
            T110 = 10,
            T120 = 11,
            T130 = 12,
            T140 = 13,
            T150 = 14,
            T160 = 15,
            T170 = 16,
            T180 = 17,
            T190 = 18,
            T200 = 19,
            T210 = 20,
            T220 = 21,
            T230 = 22,
            T240 = 23,
            T250 = 24,
            T260 = 25,
            T270 = 26,
            T280 = 27,
            T290 = 28,
            T300 = 29,
            T310 = 30,
            T320 = 31,
            T330 = 32,
            T340 = 33,
            T350 = 34,
            T360 = 35,
            T370 = 36,
            T380 = 37,
            T390 = 38,
            T400 = 39,
            T410 = 40,
            T420 = 41,
            T430 = 42,
            T440 = 43,
            T450 = 44,
            T460 = 45,
            T470 = 46,
            T480 = 47,
            T490 = 48,
            T500 = 49,
            T510 = 50,
            T520 = 51,
            T530 = 52,
            T540 = 53,
            T550 = 54,
            T560 = 55,
            T570 = 56,
            T580 = 57,
            T590 = 58,
            T600 = 59,
            T610 = 60,
            T620 = 61,
            T630 = 62,
            T640 = 63,
            T650 = 64,
            T660 = 65,
            T670 = 66,
            T680 = 67,
            T690 = 68,
            T700 = 69,
            T710 = 70,
            T720 = 71,
            T730 = 72,
            T740 = 73,
            T750 = 74,
            T760 = 75,
            T770 = 76,
            T780 = 77,
            T790 = 78,
            T800 = 79,
            T810 = 80,
            T820 = 81,
            T830 = 82,
            T840 = 83,
            T850 = 84,
            T860 = 85,
            T870 = 86,
            T880 = 87,
            T890 = 88,
            T900 = 89,
            T910 = 90,
            T920 = 91,
            T930 = 92,
            T940 = 93,
            T950 = 94,
            T960 = 95,
            T970 = 96,
            T980 = 97,
            T990 = 98,
            T1000 = 99,
            T1010 = 100,
            T1020 = 101,
            T1030 = 102,
            T1040 = 103,
            T1050 = 104,
            T1060 = 105,
            T1070 = 106,
            T1080 = 107,
            T1090 = 108,
            T1100 = 109,
            T1110 = 110,
            T1120 = 111,
            T1130 = 112,
            T1140 = 113,
            T1150 = 114,
            T1160 = 115,
            T1170 = 116,
            T1180 = 117,
            T1190 = 118,
            T1200 = 119,
            T1210 = 120,
            T1220 = 121,
            T1230 = 122,
            T1240 = 123,
            T1250 = 124,
            T1260 = 125,
            T1270 = 126,
            T1280 = 127
        }

        public const int MAX_PROCESS_NUM = 128;

        public const int MAX_TOKEN_NUM = 128;

        public static bool[,] m_bTokenUseState = new bool[MAX_PROCESS_NUM, MAX_TOKEN_NUM];

        private static object objWait = new object();

        public static int GET_MAX_PROCESS
        {
            get { return MAX_PROCESS_NUM; }
        }

        public static void AllClearToken(Connect_Token thisToken)
        {
            lock (objWait)
            {
                for (int i = 0; i < MAX_TOKEN_NUM; i++)
                    m_bTokenUseState[(int)thisToken, i] = false;
            }
        }

        public static bool GetTokenPosState(Connect_Token thisToken, int iPos)
        {
            bool bValue = false;

            if (iPos >= 0 && iPos < MAX_TOKEN_NUM)
                bValue = m_bTokenUseState[(int)thisToken, iPos];

            return bValue;
        }

        public static void SetTokenState(Connect_Token thisToken, bool bValue)
        {
            lock (objWait)
            {
                for (int i = 0; i < MAX_TOKEN_NUM; i++)
                {
                    if (m_bTokenUseState[(int)thisToken, i] == !bValue)
                    {
                        m_bTokenUseState[(int)thisToken, i] = bValue;

                        break;
                    }
                }
            }
        }

        //사용 불가능 true 가능 false
        public static bool GetTokenState(Connect_Token thisToken)
        {
            bool bUnused = true;

            for (int i = 0; i < MAX_TOKEN_NUM; i++)
            {
                if (m_bTokenUseState[(int)thisToken, i] == false)
                {
                    //사용 가능
                    bUnused = false;

                    return bUnused;
                }

            }

            //사용 불가능
            bUnused = true;

            return bUnused;
        }
    }
}
