using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using SystemX.Common;
using SystemX.Common.Serialization;
using SystemX.Net.BaseProtocol;
using SystemX.Net.Comm;
using SystemX.Net.XAdaptor;
using SystemX.Net.XAdaptor.PC.O2Sensor.NgCode;
using SystemX.Net.Schedule;
using static SystemX.Net.Platform.Common.Util.LogMessage;
using WTimer = System.Windows.Forms.Timer;
using System.IO.Compression;
using SystemX.Net.Comm.IIS_FTP;

namespace SystemX.Net.XAdaptor.PC
{
    public partial class XAdaptorPC : XAdaptorSystem, IXPCAdaptor, IUIM, IComXPCAdaptor, IDisposable
    {
        private IPEndPoint ClientCommandEP;
        private IPEndPoint ClientStreamEP;

        private const int DISP_CONNECT_TIME_OUT = 5000;

        private const int CONNECT_TIME_OUT = 10000;
        private const int COMMAND_TIME_OUT = 20000;

        private const int FILE_SCAN_TIME = 250;
        private const int DEVICE_SCAN_TIME = 250;

        private const int RETRY_TIME = 5000;

        private const int LOGIN_TIMEOUT = 90000;

        private const int OBJECT_ACCESS_WAITTIME = 5000;

        private const int FILE_ACCESS_WAITTIME = 100;

        private const int OBJECT_ACCESS_CHECKTIME = 250;

        //1분 파일 전송 간격 
        private const int FILE_SEND_INTERVAL_TIME = 10000;

        //TODO : For Debug Test
        //5분 이상 파일전송이 없을경우
        private const int FILE_SEND_MAX_WAITTIME = 300000; // 300000;

        private const int COMM_RETRY = 3;
        //private const double SESSION_TIMEOUT_SECOND = 300.0;

#if FALSE
        private AsyncComClientSocket ClientCommandSock;
        private AsyncComClientSocket ClientStreamSock;
#else
        private AsyncClientSocket ClientCommandSock;
        private AsyncClientSocket ClientStreamSock;
#endif

        private Task taskRecvCommandProcess;
        private bool m_bTaskCommandBlock;

        private Task taskRecvStreamProcess;
        private bool m_bTaskStreamBlock;

        private Task taskSubProcess;
        private bool m_bTaskSubBlock;
        //
        private CancellationTokenSource CommandCTS;
        private CancellationToken CommandCT;

        private CancellationTokenSource StreamCTS;
        private CancellationToken StreamCT;

        private CancellationTokenSource SubCTS;
        private CancellationToken SubCT;
        //
        private bool bConnectCommandSocketFlag;
        private bool bConnectStreamSocketFlag;
        //
        private PacketFlowControl FlowCommandControl;
        private PacketFlowControl FlowStreamControl;
    
        private event RecvWaitEventHandler RecvWaitEvent;

        private WaitParameter waitCommandParam;
        private WaitParameter waitStreamParam;

        internal class QueryDataSetResult
        {
            public XData xData;

            public bool bQueryResult;

            public DataSet QueryDataSet;

            public string strVarParam1;
            public string strVarParam2;
            public string strVarParam3;

            public bool bFileRecvResult;
            public bool bFileProcessResult;

            public void Initialize()
            {
                xData = null;

                bQueryResult = false;

                QueryDataSet = null;

                strVarParam1 = string.Empty;
                strVarParam2 = string.Empty;
                strVarParam3 = string.Empty;

                bFileRecvResult = false;
                bFileProcessResult = false;
            }
        }

        private QueryDataSetResult QueryResult = new QueryDataSetResult();

        //Command Socket Retry Time
        private Stopwatch streCommandConnectWatch;
        //Stream Socket Retry Time
        private Stopwatch streStreamConnectWatch;
        //File Send & File Backup Time
        private Stopwatch stFileSendTime;
        private Stopwatch stBackupFileSendTime;

        private Task tkReCommandConnect;
        private Task tkReStreamConnect;

        private WTimer reConnectTimer;
        //
        private bool[] START_EVENT_TRIGGER;

        private string strSetAdaptorConfigFilePos;
        private string strCheckCommandMsg;
        private string strCheckStreamMsg;

        private static SemaphoreSlim _smpSlim = new SemaphoreSlim(1, 1);

        private static bool bTaskSubLockState = false;

        //private static readonly object objFileSendLock = new object();

        private static bool bTaskCommandWaitLock = false;
        private static bool bTaskStreamWaitLock = false;

        private bool bReqConnectFailed;
        private int nReqConnectNum;
        //
        private NGCodeMgr dbNgCodeFinder;

        public NGCodeMgr GetNgCodeMgr()
        {
            if (dbNgCodeFinder != null)
                return dbNgCodeFinder;
            else
                return null;
        }

        private enum TIMER_LOCK
        {
            RECONNECT_TIMER = 0
        }

        private bool[] m_bTimerLock;

        private void InitMember()
        {
            GetMiddlewareMessage = "OFF";

            CommandCTS = new CancellationTokenSource();
            CommandCT = CommandCTS.Token;

            StreamCTS = new CancellationTokenSource();
            StreamCT = StreamCTS.Token;

            SubCTS = new CancellationTokenSource();
            SubCT = SubCTS.Token;
            //
            streCommandConnectWatch = new Stopwatch();
            streCommandConnectWatch.Start();

            streStreamConnectWatch = new Stopwatch();
            streStreamConnectWatch.Start();

            stFileSendTime = new Stopwatch();
            stFileSendTime.Start();

            stBackupFileSendTime = new Stopwatch();
            stBackupFileSendTime.Start();
            //
            tkReCommandConnect = null;
            tkReStreamConnect = null;

            strSetAdaptorConfigFilePos = string.Empty;

            AdaptorInformation = new ClientConnInfo();
            //
            bConnectCommandSocketFlag = false;
            bConnectStreamSocketFlag = false;
        }

        public XAdaptorPC()
        {
            InitMember();

            /* For LeakTest */
            if (COMMON.FIX_INFO_MODE)
            {
                IPEndPoint CommandEp = null;
                IPEndPoint StreamEp = null;

                CommandEp = new IPEndPoint(IPAddress.Parse("200.200.200.48"), 5124);
                StreamEp = new IPEndPoint(IPAddress.Parse("200.200.200.48"), 7124);

                //TempCode
                //CommandEp = new IPEndPoint(IPAddress.Loopback, 5124);
                //StreamEp = new IPEndPoint(IPAddress.Loopback, 7124);

                Initialize(CommandEp, StreamEp);
            }
            else
            {
                //For Normal Process
                Initialize();
            }
        }

        public XAdaptorPC(IPEndPoint SetCommandEp = null, IPEndPoint SetStreamEp = null)
        {
            InitMember();

            Initialize(SetCommandEp, SetStreamEp);
        }

        ~XAdaptorPC()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool bDisposing)
        {
            RecvWaitEvent -= RecvWaitEventCall;

            if (bDisposing)
            {
                ClearCommandWatchTask();
                ClearStreamWatchTask();
            }

            ClearCommandClientInstance();
            ClearStreamClientInstance();

            // do releasing unmanaged resource (종결자가 없는 객체의 자원 해제)
            // i.e. close file handle of operating systems

            // suppress calling of Finalizer
            GC.SuppressFinalize(this);
        }

        private void SetRetryTimer()
        {
            reConnectTimer = new WTimer();
            reConnectTimer.Enabled = false;
            reConnectTimer.Interval = 1500;
            reConnectTimer.Tick += ReConnectTimer_Tick;
            reConnectTimer.Start();
            reConnectTimer.Enabled = true;
        }
        
        private bool CheckConfigFileBuildEndPoint()
        {
            bool bCheckResult = true;

            var sFilePath = "";

            if (strSetAdaptorConfigFilePos.Length <= 0)
                sFilePath = Directory.GetCurrentDirectory() + @"\Configure\" + "ClientInformation.xml";
            else
                sFilePath = strSetAdaptorConfigFilePos;

            if (File.Exists(sFilePath) == false)
            {
                MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") +
                    @"SystemX.Net.XAdaptor.PC Configuration file does not exist. " +
                    "[SystemX.Net.XAdaptor.PC : XPCAdaptor.CheckConfigFileBuildEndPoint]", ConsoleColor.Yellow, LogMessageLevel.FATAL);

                bCheckResult = false;
            }
            else
            {
                LoadInfo = new ClientInfo(sFilePath);

                if (LoadInfo.Load() == false)
                {
                    bCheckResult = false;

                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") +
                        @"SystemX.Net.XAdaptor.PC Configuration file loading Error. " +
                        "[SystemX.Net.XAdaptor.PC : XPCAdaptor.CheckConfigFileBuildEndPoint]", ConsoleColor.Yellow, LogMessageLevel.FATAL);
                }
                else
                {
                    AdaptorInformation.CONNECT_IP = LoadInfo.CONNECT_IP;
                    AdaptorInformation.SECTION = LoadInfo.SECTION;
                    AdaptorInformation.HOST_ID = LoadInfo.HOST_ID;
                    AdaptorInformation.TEST_CODE = LoadInfo.TEST_CODE;

                    AdaptorInformation.COMMAND_PORT = LoadInfo.COMMAND_PORT;
                    AdaptorInformation.STREAM_PORT = LoadInfo.STREAM_PORT;

                    AdaptorInformation.LOGIN_RESULT = false;
                    AdaptorInformation.LOGIN_MESSAGE = string.Empty;
                }
            }

            StateAdaptorConnect = bCheckResult;

            if (ClientReadyEndPoint && bCheckResult)
                SetEpCommandStream();

            return bCheckResult;
        }

        private void SetDefaultConfig()
        {
            LoadInfo = new ClientInfo();
            LoadInfo.LOOP_BACK = false;

            LoadInfo.CONNECT_IP = ClientCommandEP.Address.ToString();

            LoadInfo.COMMAND_PORT = ClientCommandEP.Port;
            LoadInfo.STREAM_PORT = ClientStreamEP.Port;

            LoadInfo.READ_INFO_STATE = true;

            StateAdaptorConnect = true;
        }

        private void SetEpCommandStream()
        {
            if (LoadInfo.LOOP_BACK)
            {
                ClientCommandEP = new IPEndPoint(IPAddress.Loopback, LoadInfo.COMMAND_PORT);
                ClientStreamEP = new IPEndPoint(IPAddress.Loopback, LoadInfo.STREAM_PORT);
            }
            else
            {
                ClientCommandEP = new IPEndPoint(IPAddress.Parse(LoadInfo.CONNECT_IP), LoadInfo.COMMAND_PORT);
                ClientStreamEP = new IPEndPoint(IPAddress.Parse(LoadInfo.CONNECT_IP), LoadInfo.STREAM_PORT);
            }
        }

        private void AdaptorAbort(bool bForceClear = false)
        {
            ClearCommonVar();

            ClearCommandWatchTask();
            ClearStreamWatchTask();

            if (bForceClear == true)
            {
                try
                {
                    XAdaptorLogWrite("AdaptorAbort - Command Socket Disconnect");
                    XAdaptorLogWrite("AdaptorAbort - Stream Socket Disconnect");

                    streCommandConnectWatch.Restart();

                    streStreamConnectWatch.Restart();

                    thisConnInfo.ConnectCommandState = false;
                    thisConnInfo.ConnectStreamState = false;

                    SubscribeConnectInfo.Initialize();

                    StateClientSocketConnect = false;
                    StateClientGetInformation = false;
                    StateClientStreamSocketConnect = false;
                }
                catch (Exception ex)
                {
                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") +
                                           @"Abort socket all-clear information error. [SystemX.Net.XAdaptor.PC : XPCAdaptor.AdaptorAbort]\r\n" +
                                           ex.Message, ConsoleColor.Yellow, LogMessageLevel.DEBUG);
                }
            }

            OnCommandDisconnectAlarm(null, null);
            OnStreamDisconnectAlarm(null, null);

            ClearCommandClientInstance();
            ClearStreamClientInstance();
        }

        private async void ReConnectTimer_Tick(object sender, EventArgs e)
        {
            if (m_bTimerLock[(int)TIMER_LOCK.RECONNECT_TIMER] == false)
            {
                m_bTimerLock[(int)TIMER_LOCK.RECONNECT_TIMER] = true;

                if(SubscribeConnectInfo.bSetChangeConnect)
                {
                    if (SubscribeConnectInfo.bReqConnectAbort == false)
                        SubscribeConnectInfo.bReqConnectAbort = true;
                    else
                    {
                        if (bReqConnectFailed)
                        {
                            bReqConnectFailed = false;

                            if (nReqConnectNum < 2)
                            {
                                SubscribeConnectInfo.bSetChangeConnect = false;

                                await ComClientTryDistributionConnet();
                            }
                            else
                                SubscribeConnectInfo.bSetChangeConnect = false;
                        }
                        else
                        {
                            if (ConnectStartStyleAuto == false)
                                SubscribeConnectInfo.bSetChangeConnect = false;
                            else
                            {
                                if (await ComClientTryConnet(SubscribeConnectInfo.nChangeCommandPort, SubscribeConnectInfo.nChangeStreamPort))
                                    SubscribeConnectInfo.bSetChangeConnect = false;
                                else
                                {
                                    AdaptorAbort();

                                    bReqConnectFailed = true;
                                    nReqConnectNum++;

                                    SubscribeConnectInfo.bReqConnectAbort = false;
                                }
                            }
                        }
                    }
                }
                if (SubscribeConnectInfo.bUIMUseMode && SubscribeConnectInfo.bSessionStarted)
                {
                    if (LoadInfo.SESSION_TIMEOUT_TIME_S > 0.0)
                    {
                        if (SubscribeConnectInfo.GetSessionTime().TotalSeconds >= LoadInfo.SESSION_TIMEOUT_TIME_S)
                        {
                            AdaptorAbort();
                        }
                    }
                }

                //
                /*
                if (START_EVENT_TRIGGER[0])
                {
                    START_EVENT_TRIGGER[0] = false;

                    //CommandConnectAlarmEvent?.BeginInvoke(null, null, null, null);
                    //CommandConnectAlarmEvent?.Invoke(null, null);

                    OnCommandConnectAlarm(null, null);
                }
                if (START_EVENT_TRIGGER[1])
                {
                    START_EVENT_TRIGGER[1] = false;

                    //StreamConnectAlarmEvent?.BeginInvoke(null, null, null, null);
                    //StreamConnectAlarmEvent?.Invoke(null, null);

                    OnStreamConnectAlarm(null, null);
                }
                */
                //
                /*
                //Command Stream Conn Check
                if (StateClientSocketConnect == false)
                    goto ROUTINE_OUT;
                if (thisConnInfo.stCommandCheckTime.ElapsedMilliseconds >= 60000)
                {
                    SendCommandConnectState();

                    thisConnInfo.OnCommandCheckTime();
                }
                //
                if (StateClientStreamSocketConnect == false)
                    goto ROUTINE_OUT;
                if (thisConnInfo.stStreamCheckTime.ElapsedMilliseconds >= 60000)
                {
                    SendStreamConnectState();

                    thisConnInfo.OnStreamCheckTime();
                }

                ROUTINE_OUT:
                */

                m_bTimerLock[(int)TIMER_LOCK.RECONNECT_TIMER] = false;
            }
        }

        private void Initialize(IPEndPoint SetCommandEp = null, IPEndPoint SetStreamEp = null)
        {
            waitCommandParam = new WaitParameter();
            waitStreamParam = new WaitParameter();

            mgrPRODTestList = CreateTestListMgr();

            bReqConnectFailed = false;
            nReqConnectNum = 0;

            //Ng code decoder
            dbNgCodeFinder = null;

            ClientReadyEndPoint = false;

            if (SetCommandEp != null && SetStreamEp != null)
            {
                ClientCommandEP = SetCommandEp;
                ClientStreamEP = SetStreamEp;

                ClientReadyEndPoint = true;
            }

            MessageOutput.PrintLogLevel = LogMessageLevel.INFO;

            StateAdaptorConnect = true;
            //
            StateClientSocketConnect = false;
            StateClientStreamSocketConnect = false;
            //
            StateClientGetInformation = false;
            //
            ClientCommandSock = null;
            ClientStreamSock = null;

            FlowCommandControl = null;
            FlowStreamControl = null;

            RecvWaitEvent += RecvWaitEventCall;

            thisConnInfo = new ClientInfoStore();
            //
            m_bTimerLock = new bool[10];
            Array.Clear(m_bTimerLock, 0, 10);
            //
            if (ClientReadyEndPoint == false)
                CheckConfigFileBuildEndPoint();
            else
                SetDefaultConfig();
            //
            if (StateAdaptorConnect)
            {
                ;//
            }
            //
            thisConnInfo.OnCommandTime();
            thisConnInfo.OnCommandCheckTime();

            thisConnInfo.OnStreamTime();
            thisConnInfo.OnStreamCheckTime();
            //
            //if (CLIENT_AUTO_CONNECT)
            SetRetryTimer();
        }

        // TODO : FTP ALIS
        internal eFTPServiceStatus StartFTPService()
        {
            if (LoadInfo.FTP_Use)
            {
                ControlFTP = new CtrlFTP(LoadInfo.FTP_Use,
                    IPAddress.Parse(LoadInfo.FTP_IPAddress), Convert.ToInt32(LoadInfo.FTP_Port), 
                    LoadInfo.FTP_Account, LoadInfo.FTP_Password);

                if (ControlFTP.FTPConnState)
                    return eFTPServiceStatus.Connected;
                else
                    return eFTPServiceStatus.Disconnected;
            }
            else
                return eFTPServiceStatus.Unused;
        }

        public DataSet WaitSystemQuery(string strQuery)
        {
            if (thisConnInfo.ConnectCommandState == false)
                return null;
            if (thisConnInfo.InitialInfoState == false)
                return null;
            if (LoadInfo.SIMPLE_LOOKUP_OPTION == true)
                return null;

            DataSet dsReturnSet = null;

            try
            {
                thisConnInfo.OnCommandTime();

                try
                {
                    QUERY_PACKET QueryPacket = new QUERY_PACKET();
                    int iSizeQueryPacket = Marshal.SizeOf(QueryPacket);
                    byte[] ucQueryPacketArray = new byte[iSizeQueryPacket];
                    QueryPacket = (QUERY_PACKET)SystemXNetSerialization.RawDeSerialize(ucQueryPacketArray, QueryPacket.GetType());
                    QueryPacket.objQueryText[0].Data = strQuery;
                    ucQueryPacketArray = XCommons.SpecialObjectToByteStream(new BASE_PROTOCOL(BASE_PROTOCOL.PROTOCOL_CODE.SYSTEM_QUERY), QueryPacket);

                    byte ucGetLabel = FlowCommandControl.InsertSendQueue(DateTime.Now, ucQueryPacketArray, null, true);

                    WaitThreadJoin(waitCommandParam, FlowCommandControl, ucGetLabel);

                    if (QueryResult.QueryDataSet != null)
                    {
                        if (QueryResult.bQueryResult)       //Query 문 실행 성공
                            dsReturnSet = QueryResult.QueryDataSet;
                        else                                //Query 문 실행 실패(구문 오류 또는 연결 오류)
                            dsReturnSet = null;
                    }
                }
                catch (Exception ex)
                {
                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") +
                      @"Internal use program query failure. [" + strQuery + "][SystemX.Net.XAdaptor.PC - IXPCAdaptor : XPCAdaptor.WaitSystemQuery]\r\n" +
                      ex.Message, ConsoleColor.Yellow, LogMessageLevel.FATAL);
                }
                finally
                {
                }
            }
            finally
            {
                ;
            }

            return dsReturnSet;
        }
    }
}

