using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SystemX.Common;
using SystemX.Common.Archive;
using SystemX.Common.Serialization;
using SystemX.Net.Comm;
using SystemX.Net.BaseProtocol;
using SystemX.Common.Protocol;
using System.Collections.Concurrent;

using static SystemX.Net.Platform.Common.Util.LogMessage;

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

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

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

    public class PacketFlowControl : IDisposable
    {
        public const byte CommonErrCode = 128;

        public const byte TransferResultToken = 127;

        public const byte TestListResultToken = 126;

        public const byte CommInfoAutoToken = 125;

        public const byte CommInfoManualToken = 124;

        private const int m_iLmtCnt = 0;

        private enum QUEUE_TYPE
        {
            SEND = 0,
            RECEIVE = 1,
        }

        private string m_strLoadPos;
        private string m_strFilePos;

        private Task taskSendQueue;
        private bool m_bThreadSendBlock;

        private Task taskRecvQueue;
        private bool m_bThreadRecvBlock;

        private XSchedule xSchedule;
        private XDataSchedule xDataSchedule;

        private SystemXSocket refControlSocket;

        private XPacket LastSendInfo;
        private XPacket LastRecvInfo;

        private FlowControlType _fct;

        private DateTime LastDT;
        private byte[] LastByte;
        private int nLastLabel;

        private int m_iNumber;
        //
        private CancellationTokenSource CTS;
        private CancellationToken CT;
        //
        public ScheduleLabelManage STM;

        private PacketCalculate PacketAnalyzerWait;

        public bool bTargetListenFlow;

        public PacketFlowControl(FlowControlType fct, SystemXSocket refSocket)
        {
            bTargetListenFlow = false;

            CTS = new CancellationTokenSource();
            CT = CTS.Token;

            STM = new ScheduleLabelManage();
            STM.SetManageLabel();

            _fct = fct;

            refControlSocket = refSocket;

            m_iNumber = refControlSocket.nSocketNumber;

            m_strLoadPos = new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName;
            m_strLoadPos += @"\Restore\" + fct.ToString() + @"\";

            if (Directory.Exists(m_strLoadPos) == false)
                Directory.CreateDirectory(m_strLoadPos);

            m_strFilePos = m_strLoadPos + m_iNumber.ToString("D2") + ".QRC";

            taskSendQueue = null;
            m_bThreadSendBlock = false;

            taskRecvQueue = null;
            m_bThreadRecvBlock = false;

            xSchedule = new XSchedule();

            //패킷 복구 기능 임시 제거
            //ReadRestore();

            taskSendQueue = new Task(new Action(WatchSendQueue), CT);
            taskSendQueue.Start();

            taskRecvQueue = new Task(new Action(WatchRecvQueue), CT);
            taskRecvQueue.Start();

            LastSendInfo = null;
            LastRecvInfo = null;

            PacketAnalyzerWait = new PacketCalculate();
            PacketAnalyzerWait.WorkerReportsProgress = true;
            PacketAnalyzerWait.WorkerSupportsCancellation = true;
            PacketAnalyzerWait.PacketAnalyzerCompleted += calculatorPacket_CalculateCompleted;
        }

        ~PacketFlowControl()
        {
        }

        public void SetRefSocketPacketClear()
        {
            refControlSocket.SetRefSocketPacketClear();
        }

        public void Dispose()
        {
            CTS.Cancel();

            // dispose managed resource (종결자를 가진 객체의 자원 해제)      
            if (taskSendQueue != null)
            {
                taskSendQueue.ContinueWith(t =>
                {
                    if (bTargetListenFlow == false)
                    {
                        MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") +
                            @"taskSendQueue " + m_iNumber.ToString("D2") + " END. [Systemx.Net.Schedule : PacketFlowControl.Dispose(TRUE)]",
                            ConsoleColor.White,
                            LogMessageLevel.DEBUG);
                    }
                });
                m_bThreadSendBlock = true;

                taskSendQueue.ConfigureAwait(false);
                //taskSendQueue.Wait();
            }
            if (taskRecvQueue != null)
            {
                taskRecvQueue.ContinueWith(t =>
                {
                    if (bTargetListenFlow == false)
                    {
                        MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") +
                            @"taskRecvQueue " + m_iNumber.ToString("D2") + " END. [Systemx.Net.Schedule : PacketFlowControl.Dispose(TRUE)]",
                            ConsoleColor.White,
                            LogMessageLevel.DEBUG);
                    }
                });

                m_bThreadRecvBlock = true;

                taskRecvQueue.ConfigureAwait(false);
                //taskRecvQueue.Wait();
            }

            PacketAnalyzerWait.PacketAnalyzerCompleted -= calculatorPacket_CalculateCompleted;

            RawStoreQueueClear();

            //패킷 백엄 기능 임시 제거
            //WriteRestore();

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

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


        private void ReadRestore()
        {
            xDataSchedule = new XDataSchedule();

            using (FileStream fs = new FileStream(m_strFilePos, FileMode.OpenOrCreate, FileAccess.Read))
            {
                fs.Seek(0, SeekOrigin.Begin);

                byte[] ucCompressQueueData = new byte[fs.Length];
                int iReadCount = fs.Read(ucCompressQueueData, 0, (int)fs.Length);

                if (iReadCount > 0)
                {
                    byte[] ucDeCompressQueueData = XDataArchive.DecompressDeflateByteToByte(ucCompressQueueData);
                    xDataSchedule = SystemXNetSerialization.ByteArrayToObject<XDataSchedule>(ucDeCompressQueueData);
                }
            }

            File.Delete(m_strFilePos);

            //
            foreach (byte[] ucData in xDataSchedule.SendQueue)
            {
                XPacket tpNew = new XPacket();
                tpNew.dtPacketTime = DateTime.Now;
                tpNew.iRetryNum = 0;
                tpNew.bReplayPacket = true;
                tpNew.ucPacketBytes = ucData;
                tpNew.deleCallEvt = null;

                xSchedule.SendQueue.Enqueue(tpNew);
            }
            foreach (byte[] ucData in xDataSchedule.ReplySendQueue)
            {
                XPacket tpNew = new XPacket();
                tpNew.dtPacketTime = DateTime.Now;
                tpNew.iRetryNum = 0;
                tpNew.bReplayPacket = false;
                tpNew.ucPacketBytes = ucData;
                tpNew.deleCallEvt = null;

                xSchedule.ReplySendQueue.Enqueue(tpNew);
            }
            foreach (byte[] ucData in xDataSchedule.SendRawSetQueue)
            {
                XPacket tpNew = new XPacket();
                tpNew.dtPacketTime = DateTime.Now;
                tpNew.iRetryNum = 0;
                tpNew.bReplayPacket = false;
                tpNew.ucPacketBytes = ucData;
                tpNew.deleCallEvt = null;

                xSchedule.SendQueue.Enqueue(tpNew);
                xSchedule.SendRawSetQueue.Enqueue(tpNew);
            }
            foreach (byte[] ucData in xDataSchedule.RecvQueue)
            {
                XPacket tpNew = new XPacket();
                tpNew.dtPacketTime = DateTime.Now;
                tpNew.iRetryNum = 0;
                tpNew.bReplayPacket = true;
                tpNew.ucPacketBytes = ucData;
                tpNew.deleCallEvt = null;

                xSchedule.RecvQueue.Enqueue(tpNew);
            }
        }

        private void WriteRestore()
        {
            xDataSchedule = new XDataSchedule();

            xDataSchedule.SendQueue.Clear();
            xDataSchedule.ReplySendQueue.Clear();
            xDataSchedule.SendRawSetQueue.Clear();

            xDataSchedule.RecvQueue.Clear();

            foreach (XPacket XPData in xSchedule.SendQueue.ToList<XPacket>())
            {
                xDataSchedule.SendQueue.Add(XPData.ucPacketBytes);
            }
            foreach (XPacket XPData in xSchedule.ReplySendQueue.ToList<XPacket>())
            {
                xDataSchedule.ReplySendQueue.Add(XPData.ucPacketBytes);
            }
            foreach (XPacket XPData in xSchedule.SendRawSetQueue.ToList<XPacket>())
            {
                xDataSchedule.SendRawSetQueue.Add(XPData.ucPacketBytes);
            }
            foreach (XPacket XPData in xSchedule.RecvQueue.ToList<XPacket>())
            {
                xDataSchedule.RecvQueue.Add(XPData.ucPacketBytes);
            }

            using (FileStream fs = new FileStream(m_strFilePos, FileMode.OpenOrCreate, FileAccess.Write))
            {
                byte[] ucQueueData = SystemXNetSerialization.ObjectToByteArray(xDataSchedule);
                byte[] ucCompressQueueData = XDataArchive.CompressDeflateByteToByte(ucQueueData);

                fs.Seek(0, SeekOrigin.Begin);

                fs.Write(ucCompressQueueData, 0, ucCompressQueueData.Length);
            }
        }

        private void WriteRestore(string strFilePos, XPacket GetPacket)
        {
            using (FileStream fs = new FileStream(strFilePos, FileMode.OpenOrCreate, FileAccess.Write))
            {
                byte[] ucQueueData = SystemXNetSerialization.ObjectToByteArray(GetPacket);
                byte[] ucCompressQueueData = XDataArchive.CompressDeflateByteToByte(ucQueueData);

                fs.Seek(0, SeekOrigin.Begin);

                fs.Write(ucCompressQueueData, 0, ucCompressQueueData.Length);
            }
        }

        private async void WatchSendQueue()
        {
            await Task.Delay(250).ConfigureAwait(false);

            while (!m_bThreadSendBlock)
            {
                try
                {
                    CT.ThrowIfCancellationRequested();
                }
                catch (OperationCanceledException CancelEx)
                {
                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @" Work Canceled. [Systemx.Net.Schedule : PacketFlowControl.WatchSendQueue]\r\n" + CancelEx.Message, ConsoleColor.Yellow, LogMessageLevel.DEBUG);

                    break;
                }
                //
                try
                {
                    QuerySendFirstDetect();
                }
                catch (Exception e)
                {
                    //throw new CommonScheduleException("General Queue Schedule failed.[Systemx.Net.Schedule : PacketFlowControl.WatchSendQueue]");
                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @"General Queue Schedule failed.[Systemx.Net.Schedule : PacketFlowControl.WatchSendQueue]\r\n" + e.Message, ConsoleColor.Yellow, LogMessageLevel.DEBUG);
                }

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

        private async void WatchRecvQueue()
        {
           await Task.Delay(250).ConfigureAwait(false);

            while (!m_bThreadRecvBlock)
            {
                try
                {
                    CT.ThrowIfCancellationRequested();
                }
                catch (OperationCanceledException CancelEx)
                {
                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @" Work Canceled. [Systemx.Net.Schedule : PacketFlowControl.WatchRecvQueue]\r\n" + CancelEx.Message, ConsoleColor.Yellow, LogMessageLevel.DEBUG);

                    break;
                }
                //
                try
                {
                    XPacket getPacket = QueryRecvFirstDetect();

                    if (getPacket != null)
                        RecvCaculationRun(getPacket);
                }
                catch (Exception e)
                {
                    //throw new CommonScheduleException("General Queue Schedule failed.[Systemx.Net.Schedule : PacketFlowControl.WatchRecvQueue]");
                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @"General Queue Schedule failed.[Systemx.Net.Schedule : PacketFlowControl.WatchRecvQueue]\r\n" + e.Message, ConsoleColor.Yellow, LogMessageLevel.DEBUG);
                }

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

        public void RecvCaculationRun(XPacket Packet)
        {
            if (Packet != null)
            {
                PacketAnalyzerWait.WorkerReportsProgress = true;
                PacketAnalyzerWait.WorkerSupportsCancellation = true;

                PacketAnalyzerWait.CalculateAsync(Packet);
            }
        }

        void calculatorPacket_ProgressChanged(object sender, PacketProgressChangedEventArgs e)
        {
            ;//e.ProgressPercentage;
        }

        void calculatorPacket_CalculateCompleted(object sender, PacketAnalyzerCompletedEventArgs e)
        {
            //Cancel
            if (e.Cancelled)
            {
                RecvPacketCycle();
            }
            else
            {
                //Success
                if (e.Error == null)
                {
                    XData getXData = (XData)e.Result;

                    byte ngetLabel = getXData.nLabel;

                    xSchedule.RecvResultData.Enqueue(getXData);

                    if (getXData.nLabel >= 0 &&
                        getXData.nLabel < STM.GetLabelSize())
                        STM.SetLabel(ngetLabel, getXData);

                    SetRecvPacketDrop();
                }
                else
                {
                    RecvPacketCycle();
                }
            }
        }

        public XData GetResultPacketData()
        {
            XData getXData = null;

            if (xSchedule.RecvResultData.IsEmpty == false)
            {
                if (xSchedule.RecvResultData.TryDequeue(out getXData) == false)
                    getXData = null;
            }

            return getXData;
        }
        public XPacket GetCurrentSendProcessData()
        {
            if (LastSendInfo != null)
                return LastSendInfo;
            else
                return null;
        }

        public XPacket GetCurrentRecvProcessData()
        {
            if (LastRecvInfo != null)
                return LastRecvInfo;
            else
                return null;
        }

        public int GetSendQueueSize()
        {
            return xSchedule.SendQueue.Count();
        }

        public int GetRecvQueueSize()
        {
            return xSchedule.RecvQueue.Count();
        }

        private XPacket RetryDequeue(ConcurrentQueue<XPacket> cq)
        {
            XPacket getXPacket = null;

            for (int i = 0; i < 5; i++)
            {
                if (cq.TryDequeue(out getXPacket))
                    break;
                else
                    getXPacket = null;
            }

            return getXPacket;
        }

        private bool RetryPeek(ConcurrentQueue<XPacket> cq)
        {
            XPacket getXPacket = null;

            bool bResult = true;

            for (int i = 0; i < 5; i++)
            {
                if (cq.TryPeek(out getXPacket))
                {
                    bResult = true;

                    break;
                }
                else
                {
                    bResult = false;

                    getXPacket = null;
                }
            }

            return bResult;
        }

        private void RawStoreQueueClear()
        {
            XPacket GetxPacket;
            while (true)
            {
                if (xSchedule.SendRawSetQueue.IsEmpty)
                    break;

                if (xSchedule.SendRawSetQueue.TryDequeue(out GetxPacket))
                    ;//
                else
                {
                    GetxPacket = RetryDequeue(xSchedule.SendRawSetQueue);

                    if (GetxPacket != null)
                        ;//
                }
            }
        }

        public int getRawQueueCnt()
        {
            return xSchedule.SendRawSetQueue.Count;
        }

        public void RawStoreQueuePOP()
        {
            XPacket StorePacket;

            int iPopNum = 0;
            int iDequeueCnt = 0;

            DateTime dtSend;

            if (xSchedule.SendRawSetQueue.IsEmpty == false)
            {
                StorePacket = xSchedule.SendRawSetQueue.ElementAt(0);
                dtSend = StorePacket.dtPacketTime;

                for (int i = 0; i < xSchedule.SendRawSetQueue.Count; i++)
                {
                    StorePacket = xSchedule.SendRawSetQueue.ElementAt(i);

                    if (dtSend.Ticks == StorePacket.dtPacketTime.Ticks)
                        iPopNum++;
                    else
                        break;
                }
                while (true)
                {
                    if (xSchedule.SendRawSetQueue.IsEmpty)
                        break;

                    if (xSchedule.SendRawSetQueue.TryDequeue(out StorePacket))
                        iDequeueCnt++;
                    else
                    {
                        StorePacket = RetryDequeue(xSchedule.SendRawSetQueue);

                        if (StorePacket != null)
                            iDequeueCnt++;
                    }

                    if (iDequeueCnt >= iPopNum)
                        break;
                }

                if (xSchedule.SendRawSetQueue.IsEmpty == false)
                {
                    StorePacket = xSchedule.SendRawSetQueue.ElementAt(0);

                    if (dtSend.Ticks > StorePacket.dtPacketTime.Ticks)
                    {
                        while (true)
                        {
                            if (xSchedule.SendRawSetQueue.IsEmpty)
                                break;

                            if (xSchedule.SendRawSetQueue.TryPeek(out StorePacket))
                            {
                                if (dtSend.Ticks > StorePacket.dtPacketTime.Ticks)
                                {
                                    if (xSchedule.SendRawSetQueue.TryDequeue(out StorePacket) == false)
                                        RetryDequeue(xSchedule.SendRawSetQueue);
                                }
                                else
                                    break;
                            }
                            else
                            {
                                if (RetryPeek(xSchedule.SendRawSetQueue))
                                {
                                    if (dtSend.Ticks > StorePacket.dtPacketTime.Ticks)
                                    {
                                        if (xSchedule.SendRawSetQueue.TryDequeue(out StorePacket) == false)
                                            RetryDequeue(xSchedule.SendRawSetQueue);
                                    }
                                    else
                                        break;
                                }
                            }
                        }
                    }
                }
            }
        }

        public byte InsertSendQueue(DateTime dtPacket, byte[] ucData, SendRecvCallEvent CallProcess, byte ucSetSendLabel)
        {
            bool bInsertResult = true;

            byte ucLabel = ucSetSendLabel;

            XPacket tpNew;

            try
            {
                try
                {
                    tpNew = new XPacket();

                    if (dtPacket == null)
                        tpNew.dtPacketTime = DateTime.Now;
                    else
                        tpNew.dtPacketTime = dtPacket;

                    tpNew.iRetryNum = 0;
                    tpNew.bReplayPacket = true;
                    tpNew.ucPacketBytes = ucData;
                    tpNew.deleCallEvt = CallProcess;
                    tpNew.nLabel = ucLabel;

                    xSchedule.SendQueue.Enqueue(tpNew);
                }
                catch (Exception e)
                {
                    bInsertResult = false;

                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @"Data packet queue insert fail. [Systemx.Net.Schedule : PacketFlowControl.InsertSendQueue]\r\n" + e.Message, ConsoleColor.Red, LogMessageLevel.DEBUG);
                }
            }
            finally
            {
                if (bInsertResult == false) ucLabel = CommonErrCode;
            }

            return ucLabel;
        }

        public byte InsertSendQueue(DateTime dtPacket, byte[] ucData, SendRecvCallEvent CallProcess, bool bUseLabel = false, bool bResponsCall = true, bool bRawData = false)
        {
            bool bInsertResult = true;

            byte nLabel = 0;

            XPacket tpNew;

            try
            {
                try
                {
                    if (bUseLabel)
                        nLabel = STM.GetLabel();

                    tpNew = new XPacket();

                    if (dtPacket == null)
                        tpNew.dtPacketTime = DateTime.Now;
                    else
                        tpNew.dtPacketTime = dtPacket;

                    tpNew.iRetryNum = 0;
                    tpNew.bReplayPacket = bResponsCall;
                    tpNew.ucPacketBytes = ucData;
                    tpNew.deleCallEvt = CallProcess;
                    tpNew.nLabel = nLabel;

                    if (bResponsCall)
                    {
                        xSchedule.SendQueue.Enqueue(tpNew);

                        if (bRawData)
                            xSchedule.SendRawSetQueue.Enqueue(tpNew);
                    }
                    else
                        xSchedule.ReplySendQueue.Enqueue(tpNew);
                }
                catch (Exception e)
                {
                    bInsertResult = false;

                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @"Data packet queue insert fail. [Systemx.Net.Schedule : PacketFlowControl.InsertSendQueue]\r\n" + e.Message, ConsoleColor.Red, LogMessageLevel.DEBUG);
                }
            }
            finally
            {
                if (bInsertResult == false) nLabel = CommonErrCode;
            }

            return nLabel;
        }

        public byte InsertSendQueue(DateTime dtPacket, byte[] ucData, SendRecvCallEvent CallProcess, int nSelectSendLabel, bool bResponsCall = true, bool bRawData = false)
        {
            bool bInsertResult = true;

            byte nLabel = 0;

            XPacket tpNew;

            try
            {
                try
                {
                    nLabel = STM.GetLabel(nSelectSendLabel);

                    tpNew = new XPacket();

                    if (dtPacket == null)
                        tpNew.dtPacketTime = DateTime.Now;
                    else
                        tpNew.dtPacketTime = dtPacket;

                    tpNew.iRetryNum = 0;
                    tpNew.bReplayPacket = bResponsCall;
                    tpNew.ucPacketBytes = ucData;
                    tpNew.deleCallEvt = CallProcess;
                    tpNew.nLabel = nLabel;

                    if (bResponsCall)
                    {
                        xSchedule.SendQueue.Enqueue(tpNew);

                        if (bRawData)
                            xSchedule.SendRawSetQueue.Enqueue(tpNew);
                    }
                    else
                        xSchedule.ReplySendQueue.Enqueue(tpNew);
                }
                catch (Exception e)
                {
                    bInsertResult = false;

                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @"Data packet queue insert fail. [Systemx.Net.Schedule : PacketFlowControl.InsertSendQueue]\r\n" + e.Message, ConsoleColor.Red, LogMessageLevel.DEBUG);
                }
            }
            finally
            {
                if (bInsertResult == false) nLabel = CommonErrCode;
            }

            return nLabel;
        }

        public void InsertRecvQueue(byte[] ucData, SendRecvCallEvent CallProcess, byte nLabel, bool bResponsCall = true)
        {
            XPacket tpNew = new XPacket();
            tpNew.dtPacketTime = DateTime.Now;
            tpNew.iRetryNum = 0;
            tpNew.bReplayPacket = bResponsCall;
            tpNew.ucPacketBytes = ucData;
            tpNew.deleCallEvt = CallProcess;
            tpNew.nLabel = nLabel;

            xSchedule.RecvQueue.Enqueue(tpNew);
        }

        public XPacket QuerySendFirstDetect()
        {
            XPacket getInfo = null;

            if (xSchedule.SendQueue.IsEmpty == false && LastSendInfo == null)
            {
                if (xSchedule.SendQueue.TryPeek(out getInfo))
                {
                    try
                    {
                        byte[] ucSendByte = new byte[getInfo.ucPacketBytes.Count() + 1];
                        ucSendByte[0] = (byte)getInfo.nLabel;

                        Array.Copy(getInfo.ucPacketBytes, 0, ucSendByte, 1, getInfo.ucPacketBytes.Count());

                        if (refControlSocket.BeginSend(ucSendByte))
                        {
                            if (getInfo.bReplayPacket)
                            {
                                LastSendInfo = getInfo;

                                LastDT = DateTime.Now;
                                LastByte = LastSendInfo.ucPacketBytes;
                                nLastLabel = LastSendInfo.nLabel;
                            }
                        }
                        else
                        {
                            //소켓 자체가 전송 실패시(소켓 에러 - 연결 해제 및 기타 등등) 이전 내역 패킷 및 현재 큐 제거
                            SetSendPacketDrop();
                        }
                    }
                    catch (Exception ex)
                    {
                        MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @"SendQueue control failed.[Systemx.Net.Schedule : PacketFlowControl.QuerySendFirstDetect]\r\n" + ex.Message, ConsoleColor.Red, LogMessageLevel.FATAL);
                    }
                }
            }
            else
            {
                try
                {
                    if (LastSendInfo != null)
                    {
                        TimeSpan dateDiff = DateTime.Now - LastDT;
                        double dGetms = dateDiff.TotalMilliseconds;

                        if (dGetms >= 20000.0)
                            LastSendInfo = null;
                    }
                }
                catch (Exception ex)
                {
                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @"LastSendInfo control failed.[Systemx.Net.Schedule : PacketFlowControl.QuerySendFirstDetect]\r\n" + ex.Message, ConsoleColor.Red, LogMessageLevel.FATAL);
                }

                if (xSchedule.ReplySendQueue.IsEmpty == false)
                {
                    if (xSchedule.ReplySendQueue.TryPeek(out getInfo))
                    {
                        try
                        {
                            byte[] ucSendByte = new byte[getInfo.ucPacketBytes.Count() + 1];
                            ucSendByte[0] = (byte)getInfo.nLabel;

                            Array.Copy(getInfo.ucPacketBytes, 0, ucSendByte, 1, getInfo.ucPacketBytes.Count());

                            if (refControlSocket.BeginSend(ucSendByte))
                            {
                                if (xSchedule.ReplySendQueue.TryDequeue(out getInfo) == false)
                                    RetryDequeue(xSchedule.ReplySendQueue);
                            }
                        }
                        catch (Exception ex)
                        {
                            MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @"ReplySendQueue control failed.[Systemx.Net.Schedule : PacketFlowControl.QuerySendFirstDetect]\r\n" + ex.Message, ConsoleColor.Red, LogMessageLevel.FATAL);
                        }
                    }
                }
            }

            return getInfo;
        }

        public XPacket QueryRecvFirstDetect()
        {
            XPacket getInfo = null;

            if (xSchedule.RecvQueue.IsEmpty == false && LastRecvInfo == null)
            {
                if (xSchedule.RecvQueue.TryPeek(out getInfo))
                    LastRecvInfo = getInfo;
            }

            return getInfo;
        }

        public XPacket GetSendPacketPeek()
        {
            if (xSchedule.SendQueue.IsEmpty == false)
            {
                XPacket getInfo = null;

                if (xSchedule.SendQueue.TryPeek(out getInfo))
                    return getInfo;
            }

            return null;
        }

        public XPacket GetRecvPacketPeek()
        {
            if (xSchedule.RecvQueue.IsEmpty == false)
            {
                XPacket getInfo = null;

                if (xSchedule.RecvQueue.TryPeek(out getInfo))
                    LastRecvInfo = getInfo;

                return getInfo;
            }

            return null;
        }

        public XPacket SetSendPacketDrop()
        {
            XPacket getInfo = null;

            if (xSchedule.SendQueue.IsEmpty == false)
            {
                if (xSchedule.SendQueue.TryDequeue(out getInfo))
                    LastSendInfo = null;
                else
                    SetSendPacketDrop();
            }
            else
                LastSendInfo = null;

            return getInfo;
        }

        public XPacket SetRecvPacketDrop()
        {
            XPacket getInfo = null;

            if (xSchedule.RecvQueue.IsEmpty == false)
            {
                if (xSchedule.RecvQueue.TryDequeue(out getInfo))
                    LastRecvInfo = null;
                else
                    SetRecvPacketDrop();
            }
            else
                LastRecvInfo = null;

            return getInfo;
        }

        public void SendPacketCycle()
        {
            if (LastSendInfo != null)
            {
                XPacket getInfo = null;

                if (xSchedule.SendQueue.TryDequeue(out getInfo))
                {
                    if (ReferenceEquals(LastSendInfo, getInfo))
                    {
                        DateTime SetTime = getInfo.dtPacketTime;
                        int iCycle = getInfo.iRetryNum;
                        bool bGetResponsCall = getInfo.bReplayPacket;
                        byte[] ucData = getInfo.ucPacketBytes;
                        SendRecvCallEvent CallEvent = getInfo.deleCallEvt;
                        byte nGetLabel = getInfo.nLabel;

                        if (iCycle < m_iLmtCnt)
                        {
                            iCycle += 1;

                            XPacket setNewInfo = new XPacket();
                            setNewInfo.dtPacketTime = SetTime;
                            setNewInfo.iRetryNum = iCycle;
                            setNewInfo.bReplayPacket = bGetResponsCall;
                            setNewInfo.ucPacketBytes = ucData;
                            setNewInfo.deleCallEvt = CallEvent;
                            setNewInfo.nLabel = nGetLabel;

                            xSchedule.SendQueue.Enqueue(setNewInfo);
                        }
                        else
                        {
                            //실패한 패킷내용에 대해 기록 남기는 기능 추가
                            string strMakeDate = DateTime.Now.ToString(@"\\yyyy\\MM\\dd\\");
                            string strFilePos = m_strLoadPos + strMakeDate;

                            Directory.CreateDirectory(strFilePos);

                            strFilePos += @"SendProcessErrorPacket_" + DateTime.Now.ToString("yyMMddHHmmssfff") + "_" + m_iNumber.ToString("D2") + ".EPH";

                            ;// WriteRestore(strFilePos, getInfo);
                        }

                        LastSendInfo = null;
                    }
                }
            }
        }

        private void RecvPacketCycle()
        {
            if (LastRecvInfo != null)
            {
                XPacket getInfo = null;

                if (xSchedule.RecvQueue.TryDequeue(out getInfo))
                {
                    if (ReferenceEquals(LastRecvInfo, getInfo))
                    {
                        DateTime SetTime = getInfo.dtPacketTime;
                        int iCycle = getInfo.iRetryNum;
                        bool bGetResponsCall = getInfo.bReplayPacket;
                        byte[] ucData = getInfo.ucPacketBytes;
                        SendRecvCallEvent CallEvent = getInfo.deleCallEvt;
                        byte nGetLabel = getInfo.nLabel;

                        iCycle += 1;

                        if (iCycle < m_iLmtCnt)
                        {
                            iCycle += 1;

                            XPacket setNewInfo = new XPacket();
                            setNewInfo.dtPacketTime = SetTime;
                            setNewInfo.iRetryNum = iCycle;
                            setNewInfo.bReplayPacket = bGetResponsCall;
                            setNewInfo.ucPacketBytes = ucData;
                            setNewInfo.deleCallEvt = CallEvent;
                            setNewInfo.nLabel = nGetLabel;

                            xSchedule.RecvQueue.Enqueue(setNewInfo);
                        }
                        else
                        {
                            //실패한 패킷내용에 대해 기록 남기는 기능 추가
                            string strMakeDate = DateTime.Now.ToString(@"\\yyyy\\MM\\dd\\");
                            string strFilePos = m_strLoadPos + strMakeDate;

                            Directory.CreateDirectory(strFilePos);

                            strFilePos += @"RecvProcessErrorPacket_" + DateTime.Now.ToString("yyMMddHHmmssfff") + "_" + m_iNumber.ToString("D2") + ".EPH";

                            ;// WriteRestore(strFilePos, getInfo);
                        }

                        LastRecvInfo = null;
                    }
                }
            }
        }
    }
}
