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.Runtime.Remoting.Messaging;
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.PC.O2Sensor.NgCode;
using SystemX.Net.Schedule;
using static SystemX.Net.Platform.Common.Util.LogMessage;
using WTimer = System.Windows.Forms.Timer;

namespace SystemX.Net.XAdaptor.PC
{
    public partial class XAdaptorPC
    {
        protected override void OnCommandConnectAlarm(object sender, EventArgs e)
        {
            // Safely raise the event for all subscribers
            base.OnCommandConnectAlarm(sender, e);
        }
        protected override void OnStreamConnectAlarm(object sender, EventArgs e)
        {
            // Safely raise the event for all subscribers
            base.OnStreamConnectAlarm(sender, e);
        }

        protected override void OnConnectionInfoRecv(object sender, EventArgs e)
        {
            // Safely raise the event for all subscribers
            base.OnConnectionInfoRecv(sender, e);
        }

        protected override void OnCommandDisconnectAlarm(object sender, EventArgs e)
        {
            // Safely raise the event for all subscribers
            base.OnCommandDisconnectAlarm(sender, e);
        }
        protected override void OnStreamDisconnectAlarm(object sender, EventArgs e)
        {
            // Safely raise the event for all subscribers
            base.OnStreamDisconnectAlarm(sender, e);
        }

        protected override void OnLoginAlarm(object sender, EventArgs e)
        {
            // Safely raise the event for all subscribers
            base.OnLoginAlarm(sender, e);
        }

        protected override void OnQueryCall(object sender, ResultEventArgs e)
        {
            // Safely raise the event for all subscribers
            base.OnQueryCall(sender, e);
        }
        protected override void OnBeginQueryCall(object sender, ResultEventArgs e)
        {
            // Safely raise the event for all subscribers
            base.OnBeginQueryCall(sender, e);
        }

        private void ClientCommandConnectEvent(object sender, ScheduleEvent e)
        {
            string strGetMsg = string.Empty;
            if (e != null)
                strGetMsg = e.STATE_MSG;

            XAdaptorLogWrite("Command Socket Connect");

            streCommandConnectWatch.Restart();

            thisConnInfo.ConnectCommandState = true;

            //ConnectCommandWaitEvent.Set();
            bConnectCommandSocketFlag = true;

            thisConnInfo.OnCommandTime();
            thisConnInfo.OnCommandCheckTime();

            StateClientSocketConnect = true;

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

        private void ClientCommandErrorEvent(object sender, ScheduleEvent e)
        {
            string strGetMsg = string.Empty;
            if (e != null)
                strGetMsg = e.STATE_MSG;

            XAdaptorLogWrite("Command Socket Disconnect");

            ClearCommandWatchTask();

            streCommandConnectWatch.Restart();

            thisConnInfo.ConnectCommandState = false;

            //ConnectCommandWaitEvent.Reset();
            bConnectCommandSocketFlag = false;

            StateClientSocketConnect = false;
            StateClientGetInformation = false;

            //SubscribeConnectInfo.Initialize();

            //CommandDisconnectAlarmEvent?.BeginInvoke(null, null, null, null);
            //CommandDisconnectAlarmEvent?.Invoke(null, null);
            OnCommandDisconnectAlarm(null, null);
        }

        private void ClientStreamConnectEvent(object sender, ScheduleEvent e)
        {
            string strGetMsg = string.Empty;

            if (e != null)
                strGetMsg = e.STATE_MSG;

            XAdaptorLogWrite("Stream Socket Connect");

            stFileSendTime.Restart();
            stBackupFileSendTime.Restart();

            streStreamConnectWatch.Restart();

            thisConnInfo.ConnectStreamState = true;

            //ConnectStreamWaitEvent.Set();
            bConnectStreamSocketFlag = true;

            thisConnInfo.OnStreamTime();
            thisConnInfo.OnStreamCheckTime();

            StateClientStreamSocketConnect = true;

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

        private void ClientStreamErrorEvent(object sender, ScheduleEvent e)
        {
            string strGetMsg = string.Empty;
            if (e != null)
                strGetMsg = e.STATE_MSG;

            XAdaptorLogWrite("Stream Socket Disconnect");

            ClearStreamWatchTask();

            streStreamConnectWatch.Restart();

            thisConnInfo.ConnectStreamState = false;

            //ConnectStreamWaitEvent.Reset();
            bConnectStreamSocketFlag = false;

            StateClientStreamSocketConnect = false;

            //SubscribeConnectInfo.Initialize();

            //StreamDisconnectAlarmEvent?.BeginInvoke(null, null, null, null);
            //StreamDisconnectAlarmEvent?.Invoke(null, null);
            OnStreamDisconnectAlarm(null, null);
        }

        private async Task<bool> SendCommandConnectState(bool bWaitRespons = false)
        {
            if (thisConnInfo.ConnectCommandState == false)
                return false;

            if (thisConnInfo.stCommandCheckTime.ElapsedMilliseconds < 60000)
                return true;

            bool bResult = true;

            try
            {
                thisConnInfo.OnCommandTime();

                try
                {
                    PING_PACKET MakePingPacket = new PING_PACKET();
                    int iSendSize = Marshal.SizeOf(MakePingPacket);
                    byte[] ucSendArray = new byte[iSendSize];
                    MakePingPacket = (PING_PACKET)SystemXNetSerialization.RawDeSerialize(ucSendArray, MakePingPacket.GetType());

                    MakePingPacket.objCheckMsg[0].Data = "Who?";

                    byte[] ucSendByteInfo = XCommons.SpecialObjectToByteStream(new BASE_PROTOCOL(BASE_PROTOCOL.PROTOCOL_CODE.CONNECT_STATE), MakePingPacket);

                    byte ucGetLabel = FlowCommandControl.InsertSendQueue(DateTime.Now, ucSendByteInfo, null, bWaitRespons);

                    thisConnInfo.OnCommandCheckTime();

                    if (bWaitRespons)
                    {
                        await WaitTaskJoin(waitCommandParam, FlowCommandControl, ucGetLabel, false).ConfigureAwait(false);

                        if (QueryResult.bQueryResult)
                            bResult = true;
                        else
                            bResult = false;
                    }
                }
                catch (Exception ex)
                {
                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") +
                       @"Command Socket ping check error. [SystemX.Net.XAdaptor.PC : XPCAdaptor.SendCommandConnectState]\r\n" +
                       ex.Message, ConsoleColor.Yellow, LogMessageLevel.DEBUG);
                }
                finally { }
            }
            finally
            {
                ;
            }

            return bResult;
        }

        private async Task<bool> SendStreamConnectState(bool bWaitRespons = false)
        {
            if (thisConnInfo.ConnectStreamState == false)
                return false;

            if (thisConnInfo.stStreamCheckTime.ElapsedMilliseconds < 60000)
                return true;

            bool bResult = true;

            try
            {
                thisConnInfo.OnStreamTime();

                try
                {
                    PING_PACKET MakePingPacket = new PING_PACKET();
                    int iSendSize = Marshal.SizeOf(MakePingPacket);
                    byte[] ucSendArray = new byte[iSendSize];
                    MakePingPacket = (PING_PACKET)SystemXNetSerialization.RawDeSerialize(ucSendArray, MakePingPacket.GetType());

                    MakePingPacket.objCheckMsg[0].Data = "Who?";

                    byte[] ucSendByteInfo = XCommons.SpecialObjectToByteStream(new BASE_PROTOCOL(BASE_PROTOCOL.PROTOCOL_CODE.CONNECT_STATE), MakePingPacket);

                    byte ucGetLabel = FlowStreamControl.InsertSendQueue(DateTime.Now, ucSendByteInfo, null, bWaitRespons);

                    thisConnInfo.OnStreamCheckTime();

                    if (bWaitRespons)
                    {
                        await WaitTaskJoin(waitStreamParam, FlowStreamControl, ucGetLabel, false).ConfigureAwait(false);

                        if (QueryResult.bQueryResult)
                            bResult = true;
                        else
                            bResult = false;
                    }
                }
                catch (Exception ex)
                {
                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") +
                       @"Stream Socket ping check error. [SystemX.Net.XAdaptor.PC : XPCAdaptor.SendStreamConnectState]\r\n" +
                       ex.Message, ConsoleColor.Yellow, LogMessageLevel.DEBUG);
                }
                finally { }
            }
            finally
            {
                ;
            }

            return bResult;
        }

        // The delegate must have the same signature as the method
        // it will call asynchronously.
        public delegate string AsyncMethodCaller(int callDuration, out int threadId);
        //public delegate void RecvWaitEventHandler(WaitParameter sender, EventArgs e);
        public delegate void RecvWaitEventHandler(WaitParameter sender, EventArgs e);

        private void RecvWaitEventCall(WaitParameter sender, EventArgs e)
        {
            try
            {
            }
            catch (Exception ex)
            {
                MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @"RecvWaitEventCall failed. [SystemX.Net.XAdaptor.PC.XPCAdaptor : XPCAdaptor.EventCall()]\r\n" + ex.Message, ConsoleColor.Yellow, LogMessageLevel.DEBUG);
            }
            finally
            {
                sender.nSignaled = 1;
            }
        }

        private void RecvWaitEnd(IAsyncResult ar)
        {
            // Retrieve the delegate.
            AsyncResult result = (AsyncResult)ar;
        }

        /*
        private void WaitTskJoin(WaitParameter wp, PacketFlowControl getPF, byte ucGetLabel, bool bUseDataSet = true, long lTimeOut = COMMAND_TIME_OUT)
        {
            wp.Init(getPF);

            Thread t = new Thread(delegate () {
                QueryResult.Initialize();
                QueryResult.xData = wp.TskCheckResult(ucGetLabel, lTimeOut);
                if (QueryResult.xData != null)
                {
                    QueryResult.bQueryResult = QueryResult.xData.bReplayResult;

                    if (bUseDataSet)
                        QueryResult.QueryDataSet = QueryResult.xData.objData as DataSet;
                    else
                    {
                        QueryResult.QueryDataSet = null;

                        if (QueryResult.xData.objData is TRANSFER_PACKET)
                        {
                            TRANSFER_PACKET TransferResult = (TRANSFER_PACKET)QueryResult.xData.objData;

                            byte ucCheckLabel = QueryResult.xData.nLabel;

                            QueryResult.bFileRecvResult = TransferResult.bRecvResult;
                            QueryResult.bFileProcessResult = TransferResult.bProcessResult;
                        }
                        else if (QueryResult.xData.objData is COMM_INFO_PACKET)
                        {
                            COMM_INFO_PACKET Result = (COMM_INFO_PACKET)QueryResult.xData.objData;

                            byte ucCheckLabel = QueryResult.xData.nLabel;
                        }
                    }

                    QueryResult.strVarParam1 = QueryResult.xData.HeaderPacket.objVarParam1[0].Data;
                    QueryResult.strVarParam2 = QueryResult.xData.HeaderPacket.objVarParam2[0].Data;
                    QueryResult.strVarParam3 = QueryResult.xData.HeaderPacket.objVarParam3[0].Data;
                }
                else
                    getPF.STM.ClearLabel(ucGetLabel);
            });

            t.Start();
            t.Join();
        }
        */

        private void WaitThreadJoin(WaitParameter wp, PacketFlowControl getPF, byte ucGetLabel, bool bUseDataSet = true, long lTimeOut = COMMAND_TIME_OUT)
        {
            wp.Init(getPF);

            Thread t = new Thread(delegate ()
            {
                QueryResult.Initialize();
                QueryResult.xData = wp.ThreadWaitResult(ucGetLabel, lTimeOut);

                if (QueryResult.xData != null)
                {
                    try
                    {
                        QueryResult.bQueryResult = QueryResult.xData.bReplayResult;

                        if (bUseDataSet)
                            QueryResult.QueryDataSet = QueryResult.xData.objData as DataSet;
                        else
                        {
                            QueryResult.QueryDataSet = null;

                            if (QueryResult.xData.objData is TRANSFER_PACKET)
                            {
                                TRANSFER_PACKET TransferResult = (TRANSFER_PACKET)QueryResult.xData.objData;

                                byte ucCheckLabel = QueryResult.xData.nLabel;

                                QueryResult.bFileRecvResult = TransferResult.bRecvResult;
                                QueryResult.bFileProcessResult = TransferResult.bProcessResult;
                            }
                            else if (QueryResult.xData.objData is COMM_INFO_PACKET)
                            {
                                COMM_INFO_PACKET Result = (COMM_INFO_PACKET)QueryResult.xData.objData;

                                byte ucCheckLabel = QueryResult.xData.nLabel;
                            }
                        }

                        QueryResult.strVarParam1 = QueryResult.xData.HeaderPacket.objVarParam1[0].Data;
                        QueryResult.strVarParam2 = QueryResult.xData.HeaderPacket.objVarParam2[0].Data;
                        QueryResult.strVarParam3 = QueryResult.xData.HeaderPacket.objVarParam3[0].Data;
                    }
                    catch
                    {
                        QueryResult.bQueryResult = false;
                        QueryResult.QueryDataSet = null;
                    }
                    finally
                    {
                        ;//
                    }
                }
                else
                    getPF.STM.ClearLabel(ucGetLabel);
            });

            t.Start();
            t.Join();
        }

        private async Task<bool> WaitTaskJoin(WaitParameter wp, PacketFlowControl getPF, byte ucGetLabel, bool bUseDataSet = true)
        {
            wp.Init(getPF);

            bool bSetResult = true;

            QueryResult.Initialize();
            QueryResult.xData = await wp.TaskWaitResult(ucGetLabel).ConfigureAwait(false);
            if (QueryResult.xData != null)
            {
                try
                {
                    QueryResult.bQueryResult = QueryResult.xData.bReplayResult;

                    if (bUseDataSet)
                        QueryResult.QueryDataSet = QueryResult.xData.objData as DataSet;
                    else
                    {
                        QueryResult.QueryDataSet = null;

                        if (QueryResult.xData.objData is TRANSFER_PACKET)
                        {
                            TRANSFER_PACKET TransferResult = (TRANSFER_PACKET)QueryResult.xData.objData;

                            byte ucCheckLabel = QueryResult.xData.nLabel;

                            QueryResult.bFileRecvResult = TransferResult.bRecvResult;
                            QueryResult.bFileProcessResult = TransferResult.bProcessResult;
                        }
                        else if (QueryResult.xData.objData is COMM_INFO_PACKET)
                        {
                            COMM_INFO_PACKET Result = (COMM_INFO_PACKET)QueryResult.xData.objData;

                            byte ucCheckLabel = QueryResult.xData.nLabel;
                        }
                    }

                    QueryResult.strVarParam1 = QueryResult.xData.HeaderPacket.objVarParam1[0].Data;
                    QueryResult.strVarParam2 = QueryResult.xData.HeaderPacket.objVarParam2[0].Data;
                    QueryResult.strVarParam3 = QueryResult.xData.HeaderPacket.objVarParam3[0].Data;
                }
                catch
                {
                    QueryResult.bQueryResult = false;
                    QueryResult.QueryDataSet = null;

                    bSetResult = false;
                }
                finally
                {
                    ;//
                }

                return bSetResult;
            }
            else
            {
                getPF.STM.ClearLabel(ucGetLabel);

                return false;
            }
        }

        private XData ComWaitResult(byte nCheckLabel)
        {
            XData getXData = null;
            bool bWaitLabel = false;

            if (nCheckLabel < 0 || nCheckLabel >= FlowCommandControl.STM.GetLabelSize())
                return getXData;

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

            while (stWaitTImeWatch.ElapsedMilliseconds <= COMMAND_TIME_OUT)
            {
                Application.DoEvents();

                getXData = FlowCommandControl.STM.CheckLabel(nCheckLabel, ref bWaitLabel);

                if (bWaitLabel)
                    break;

                Thread.Sleep(1);
            }

            return getXData;
        }

        public class WaitParameter
        {
            public int nSignaled;
            public bool tResult;

            private PacketFlowControl fc;

            public WaitParameter()
            {
                nSignaled = 0;
                tResult = false;
            }

            public void Init(PacketFlowControl getFc)
            {
                nSignaled = 0;
                tResult = false;

                fc = getFc;
            }

            /*
            public XData TskCheckResult(byte nCheckLabel, long lTimeOut = COMMAND_TIME_OUT)
            {
                XData getXData = null;
                bool bWaitLabel = false;

                if (nCheckLabel < 0 || nCheckLabel >= fc.STM.GetLabelSize())
                    return getXData;

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

                while (true)
                {
                    if (stWaitTImeWatch.ElapsedMilliseconds >= lTimeOut)
                        break;

                    if (nSignaled == 1)
                    {
                        getXData = fc.STM.CheckLabel(nCheckLabel, ref bWaitLabel);

                        if (bWaitLabel)
                        {
                            tResult = true;

                            break;
                        }
                    }

                    Thread.Sleep(1);
                }

                return getXData;
            }
            */

            public XData ThreadWaitResult(byte nCheckLabel, long lTimeOut = COMMAND_TIME_OUT)
            {
                XData getXData = null;
                bool bWaitLabel = false;

                if (nCheckLabel < 0 || nCheckLabel >= fc.STM.GetLabelSize())
                    return getXData;

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

                while (true)
                {
                    if (stWaitTImeWatch.ElapsedMilliseconds > lTimeOut)
                    {
                        fc.SetRefSocketPacketClear();

                        break;
                    }
                    if (nSignaled == 1)
                    {
                        getXData = fc.STM.CheckLabel(nCheckLabel, ref bWaitLabel);

                        if (bWaitLabel)
                        {
                            tResult = true;

                            break;
                        }
                    }

                    Thread.Sleep(1);
                }

                return getXData;
            }

            public async Task<XData> TaskWaitResult(byte nCheckLabel)
            {
                XData getXData = null;
                bool bWaitLabel = false;

                if (nCheckLabel < 0 || nCheckLabel >= fc.STM.GetLabelSize())
                    return getXData;

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

                while (true)
                {
                    if(stWaitTImeWatch.ElapsedMilliseconds > COMMAND_TIME_OUT)
                    {
                        fc.SetRefSocketPacketClear();

                        break;
                    }
                    if (nSignaled == 1)
                    {
                        getXData = fc.STM.CheckLabel(nCheckLabel, ref bWaitLabel);

                        if (bWaitLabel)
                        {
                            tResult = true;

                            break;
                        }
                    }

                    await Task.Delay(1).ConfigureAwait(false);
                }

                return getXData;
            }
        }
    }
}

