using DevExpress.Data.Helpers;
using DevExpress.XtraBars;
using DevExpress.XtraBars.Navigation;

using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml.Linq;
using System.IO;
using System.Diagnostics;

using SystemX.Net;
using SystemX.Common;
using SystemX.Net.BaseProtocol;
using SystemX.Net.Comm;
using SystemX.Net.Schedule;
using SystemX.Net.DB;
using SystemX.Common.Serialization;
using SystemX.Common.Archive;
using System.Threading.Tasks;

using SystemX.Common.Protocol.SIA;

using static SystemX.Net.Platform.Common.Util.LogMessage;
using System.Threading;
using SystemX.Net.Middleware.Commons;
using SystemX.Net.Platform.SystemX.Common;
using CpTesterPlatform.CpLogUtil;

namespace SystemX.Net.MiddlewareUI
{
    public partial class MainForm : DevExpress.XtraBars.FluentDesignSystem.FluentDesignForm
    {
        private async void WatchRecvStreamQueue()
        {
            await Task.Delay(250);

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

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

                    break;
                }
                //
                try
                {
                    if (bTaskStreamWaitLock == false) QueryRecvStreamQueue();
                }
                catch (Exception e)
                {
                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @"General Queue Process failed.[SystemX.Net.MiddlewareUI : MainForm.WatchRecvStreamQueue]\r\n" + e.Message, ConsoleColor.Yellow, LogMessageLevel.DEBUG);
                }

                await Task.Delay(10);
            }

            //return m_bTaskStreamBlock;
        }
        //private async void SendStreamEvent(byte[] senderData, ScheduleEvent e)
        private void SendStreamEvent(byte[] senderData, ScheduleEvent e)
        {
            try
            {
                int iGetCall = e.CALL_NUMBER;

                if (thisConnInfo[e.CALL_NUMBER].m_ucSendStreamToken == 0x00)
                    thisConnInfo[e.CALL_NUMBER].m_ucSendStreamToken = 0x01;
                else if (thisConnInfo[e.CALL_NUMBER].m_ucSendStreamToken == 0x01)
                    thisConnInfo[e.CALL_NUMBER].m_ucSendStreamToken = 0x02;
                else
                    thisConnInfo[e.CALL_NUMBER].m_ucSendStreamToken = 0x01;

                thisConnInfo[e.CALL_NUMBER].m_iSendStreamCnt++;

                if (thisConnInfo[e.CALL_NUMBER].m_iSendStreamCnt + 1 == int.MaxValue)
                    thisConnInfo[e.CALL_NUMBER].m_iSendStreamCnt = 0;

                if (FlowStreamControl[iGetCall] == null)
                    return;

                //소켓 샌드 행위 내부 결과
                if (e.PROCESS_RESULT == false)
                {
                    //해당 패킷 횟수 상승 일단 회 차시 기록남기고 드랍
                    FlowStreamControl[iGetCall].SendPacketCycle();
                }
            }
            catch (Exception ex)
            {
                MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @"General <SendStreamEvent> failed.[SystemX.Net.MiddlewareUI : MainForm.SendStreamEvent]\r\n" + ex.Message, ConsoleColor.Yellow, LogMessageLevel.DEBUG);
            }
        }
        //private async void RecvStreamEvent(byte[] senderData, ScheduleEvent e)
        private void RecvStreamEvent(byte[] senderData, ScheduleEvent e)
        {
            try
            {
                int iGetCall = e.CALL_NUMBER;

                if (thisConnInfo[e.CALL_NUMBER].m_ucRecvStreamToken == 0x00)
                    thisConnInfo[e.CALL_NUMBER].m_ucRecvStreamToken = 0x01;
                else if (thisConnInfo[e.CALL_NUMBER].m_ucRecvStreamToken == 0x01)
                    thisConnInfo[e.CALL_NUMBER].m_ucRecvStreamToken = 0x02;
                else
                    thisConnInfo[e.CALL_NUMBER].m_ucRecvStreamToken = 0x01;

                thisConnInfo[e.CALL_NUMBER].m_iRecvStreamCnt++;

                if (thisConnInfo[e.CALL_NUMBER].m_iRecvStreamCnt + 1 == int.MaxValue)
                    thisConnInfo[e.CALL_NUMBER].m_iRecvStreamCnt = 0;

                if (FlowStreamControl[iGetCall] == null)
                    return;

                int iStoreCnt = senderData.Count();
                byte[] recvStoreBuffer = senderData;

                //소켓 리시브 행위 결과 전송
                if (e.PROCESS_RESULT == false)
                {
                    //받기 실패 단순 실패 응답
                    FlowStreamControl[iGetCall].InsertSendQueue(DateTime.Now, XCommons.SetSimpleResponsPacket(new BASE_PROTOCOL(BASE_PROTOCOL.PROTOCOL_CODE.SIMPLE_RESPONSE), false), null, false, false);
                }
                else
                {
                    BASE_PROTOCOL GET_PROTOCOL = XCommons.GetHeaderProtocol(iStoreCnt, recvStoreBuffer);
                    BASE_PROTOCOL.PROTOCOL_CODE GET_CODE = GET_PROTOCOL.GET_CURRENT_PROTOCOL;

                    //응답
                    if (GET_CODE == BASE_PROTOCOL.PROTOCOL_CODE.SIMPLE_RESPONSE)
                    {
                        if (XCommons.GetSimpleResponsResult(iStoreCnt, recvStoreBuffer))
                            FlowStreamControl[iGetCall].SetSendPacketDrop();
                        else
                            FlowStreamControl[iGetCall].SendPacketCycle();
                    }
                    //파일 끝 응답
                    else if (GET_CODE == BASE_PROTOCOL.PROTOCOL_CODE.RAW_END)
                    {
                        if (XCommons.GetSimpleResponsResult(iStoreCnt, recvStoreBuffer))
                            FlowStreamControl[iGetCall].RawStoreQueuePOP();
                        else
                            FlowStreamControl[iGetCall].RawStoreQueuePOP();
                    }
                    //일반 응답
                    else
                    {
                        //성공 응답 
                        FlowStreamControl[iGetCall].InsertSendQueue(DateTime.Now, XCommons.SetSimpleResponsPacket(new BASE_PROTOCOL(BASE_PROTOCOL.PROTOCOL_CODE.SIMPLE_RESPONSE), true), null, false, false);

                        //받은 큐에 넣어놓기
                        FlowStreamControl[iGetCall].InsertRecvQueue(senderData, null, e.nLABEL);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @"General <RecvStreamEvent> failed.[SystemX.Net.MiddlewareUI : MainForm.RecvStreamEvent]\r\n" + ex.Message, ConsoleColor.Yellow, LogMessageLevel.DEBUG);
            }
        }
        private void SendMiddlewareMessage(int iPos, string strMessage)
        {
            if (thisConnInfo[iPos].StreamConnectState == false)
                return;

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

                MakeMessagePacket.objMessage[0].Data = strMessage;

                byte[] ucSendByteInfo = XCommons.SpecialObjectToByteStream(new BASE_PROTOCOL(BASE_PROTOCOL.PROTOCOL_CODE.MIDDLEWARE_MESSAGE), MakeMessagePacket);

                FlowStreamControl[iPos].InsertSendQueue(DateTime.Now, ucSendByteInfo, null);
            }
            catch (Exception ex)
            {
                MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") +
                   @"Send error <Middleware> message.[SystemX.Net.MiddlewareUI : MainForm.SendMiddlewareMessage]\r\n" +
                   ex.Message, ConsoleColor.Yellow, LogMessageLevel.DEBUG);
            }
        }

        private byte[] MakeTransferResult(bool bSetRecvResult, bool bSetProcessResult)
        {
            TRANSFER_PACKET TransferResult = new TRANSFER_PACKET();
            int iSendSize = Marshal.SizeOf(TransferResult);
            byte[] ucSendArray = new byte[iSendSize];
            TransferResult = (TRANSFER_PACKET)SystemXNetSerialization.RawDeSerialize(ucSendArray, TransferResult.GetType());

            TransferResult.bRecvResult = bSetRecvResult;
            TransferResult.bProcessResult = bSetProcessResult;

            byte[] ucSendByteInfo = XCommons.SpecialObjectToByteStream(new BASE_PROTOCOL(BASE_PROTOCOL.PROTOCOL_CODE.TRANSFER_RESULT), TransferResult);

            return ucSendByteInfo;
        }

        private bool QueryRecvStreamProcess(int iFlowPos, string strProcessInfo)
        {
            int i = iFlowPos;

            bool bState = true;
            //
            XData getXData = FlowStreamControl[i].GetResultPacketData();

            if (getXData == null)
                return false;

            try
            {
                thisConnInfo[i].stStreamProcessTime.Restart();

                BASE_PROTOCOL GET_PROTOCOL = getXData.BaseProtocol;
                BASE_PROTOCOL.PROTOCOL_CODE CODE = GET_PROTOCOL.GET_CURRENT_PROTOCOL;

                HEADER_PACKET getHeader = getXData.HeaderPacket;
                object objData = getXData.objData;

                byte ucGetLabel = getXData.nLabel;

                switch (CODE)
                {
                    case BASE_PROTOCOL.PROTOCOL_CODE.CONNECT_STATE:
                        {
                            PING_PACKET GetPingPacket = (PING_PACKET)objData;

                            string strGetMsg = GetPingPacket.objCheckMsg[0].Data;

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

                            PingPacketMake.objCheckMsg[0].Data = "SystemX";

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

                            if (ServerStreamSock[i].CLIENT_CONNECT)
                            {
                                if (ucSendByteInfo != null)
                                    FlowStreamControl[i].InsertSendQueue(DateTime.Now, ucSendByteInfo, null, ucGetLabel);
                            }
                        }
                        break;
                    case BASE_PROTOCOL.PROTOCOL_CODE.SYNC_TIME_SERVER:
                        {
                            TIME_PACKET GetTimePacket = (TIME_PACKET)objData;
                            string strGetMsg = GetTimePacket.objMsg[0].Data;

                            TIME_PACKET timePacketMake = new TIME_PACKET();
                            int iSendSize = Marshal.SizeOf(timePacketMake);
                            byte[] ucSendArray = new byte[iSendSize];
                            timePacketMake = (TIME_PACKET)SystemXNetSerialization.RawDeSerialize(ucSendArray, timePacketMake.GetType());

                            TimeControl.GetSyncTime(ref timePacketMake.objTime[0]);

                            timePacketMake.objMsg[0].Data = "SyncTimeSend";

                            byte[] ucSendByteInfo = XCommons.SpecialObjectToByteStream(new BASE_PROTOCOL(BASE_PROTOCOL.PROTOCOL_CODE.SYNC_TIME_SERVER), timePacketMake);

                            if (ServerStreamSock[i].CLIENT_CONNECT)
                            {
                                if (ucSendByteInfo != null)
                                    FlowStreamControl[i].InsertSendQueue(DateTime.Now, ucSendByteInfo, null, ucGetLabel);
                            }
                        }
                        break;
                    case BASE_PROTOCOL.PROTOCOL_CODE.FILE_TRANSFER:
                        {
                            string strFileExtension = getHeader.objOptionExtension[0].Data;

                            bool bGetResult = false;

                            if (strFileExtension.IndexOf(".CpLog") >= 0 ||
                                strFileExtension.IndexOf(".csv") >= 0)
                            {
                                //if (bUseMapLogProcess)
                                MappedLogQueueData.Enqueue(new Tuple<string, int, HEADER_PACKET, byte[]>("FILE_TRANSFER", i, getHeader, objData as byte[]));

                                bGetResult = true;
                            }

                            FlowStreamControl[i].InsertSendQueue(DateTime.Now, MakeTransferResult(true, bGetResult), null, PacketFlowControl.TransferResultToken, false);
                        }
                        break;
                    case BASE_PROTOCOL.PROTOCOL_CODE.RAW_SIZE:
                        {
                            if (getHeader.uiSourDataNum > 1)
                            {
                                if (thisConnInfo[i].nFileRecvPos > 0)
                                {
                                    if ((thisConnInfo[i].nFileRecvPos + 1) != getHeader.uiSourDataNum ||
                                       thisConnInfo[i].nFileRecvEndPos == 0)
                                    {
                                        thisConnInfo[i].nFileRecvPos = 0;
                                        thisConnInfo[i].nFileRecvEndPos = 0;

                                        FlowStreamControl[i].InsertSendQueue(DateTime.Now, XCommons.SetSimpleResponsPacket(new BASE_PROTOCOL(BASE_PROTOCOL.PROTOCOL_CODE.RAW_END), false), null, false, false);

                                        FlowStreamControl[i].InsertSendQueue(DateTime.Now, MakeTransferResult(false, false), null, PacketFlowControl.TransferResultToken, false);

                                        break;
                                    }
                                }
                            }
                            //
                            if (getHeader.uiSourDataNum == 1)
                            {
                                thisConnInfo[i].lstRecvFileBytes = new List<Tuple<int, byte[]>>();
                                thisConnInfo[i].lstRecvFileBytes.Clear();

                                thisConnInfo[i].lstRecvFileBytes.Add(new Tuple<int, byte[]>((int)getHeader.uiSourDataNum, objData as byte[]));

                                thisConnInfo[i].nFileRecvPos = (int)getHeader.uiSourDataNum;
                                thisConnInfo[i].nFileRecvEndPos = (int)getHeader.uiDestDataNum;

                                thisConnInfo[i].strRecvFileName = getHeader.objOptionName[0].Data;
                                thisConnInfo[i].strRecvFileExtension = getHeader.objOptionExtension[0].Data;
                            }
                            else
                            {
                                thisConnInfo[i].lstRecvFileBytes.Add(new Tuple<int, byte[]>((int)getHeader.uiSourDataNum, objData as byte[]));

                                thisConnInfo[i].nFileRecvPos++;
                            }
                            //
                            if (thisConnInfo[i].nFileRecvPos >= thisConnInfo[i].nFileRecvEndPos)
                            {
                                int iSize = 0;

                                thisConnInfo[i].lstRecvFileBytes.Sort();

                                foreach (Tuple<int, byte[]> tData in thisConnInfo[i].lstRecvFileBytes)
                                    iSize += tData.Item2.Count();

                                byte[] ucGetDatas = new byte[iSize];
                                int iCopyPos = 0;

                                foreach (Tuple<int, byte[]> tData in thisConnInfo[i].lstRecvFileBytes)
                                {
                                    Array.Copy(tData.Item2, 0, ucGetDatas, iCopyPos, tData.Item2.Count());

                                    iCopyPos += tData.Item2.Count();
                                }

                                bool bGetResult = false;

                                if (thisConnInfo[i].strRecvFileExtension.IndexOf(".CpLog") >= 0 ||
                                    thisConnInfo[i].strRecvFileExtension.IndexOf(".csv") >= 0)
                                {
                                    //if (bUseMapLogProcess)
                                    MappedLogQueueData.Enqueue(new Tuple<string, int, HEADER_PACKET, byte[]>("RAW_SIZE", i, getHeader, objData as byte[]));

                                    bGetResult = true;
                                }

                                thisConnInfo[i].nFileRecvPos = 0;
                                thisConnInfo[i].nFileRecvEndPos = 0;

                                FlowStreamControl[i].InsertSendQueue(DateTime.Now, XCommons.SetSimpleResponsPacket(new BASE_PROTOCOL(BASE_PROTOCOL.PROTOCOL_CODE.RAW_END), true), null, false, false);

                                FlowStreamControl[i].InsertSendQueue(DateTime.Now, MakeTransferResult(true, bGetResult), null, PacketFlowControl.TransferResultToken, false);
                            }
                        }
                        break;
                }

                thisConnInfo[i].lStreamTime = thisConnInfo[i].stStreamProcessTime.ElapsedMilliseconds;
            }
            catch (Exception e)
            {
                MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + strProcessInfo + @" Recv queue stream process fail.[SystemX.Net.MiddlewareUI : MainForm.QueryRecvStreamQueue]\r\n" + e.Message, ConsoleColor.Yellow, LogMessageLevel.DEBUG);

                bState = false;
            }

            return bState;
        }

        private bool QueryRecvStreamQueue()
        {
            //StackTrace st = new StackTrace(true);

            bool bState = true;

            try
            {
                bTaskStreamWaitLock = true;

                for (int i = PORT_DISTRIBUTION_NUM; i < ALL_MANAGE_NUM && !m_bTaskStreamBlock; i++)
                {
                    if (ServerStreamSock[i] == null)
                        continue;
                    //
                    if (ServerStreamSock[i] != null)
                    {
                        if (ServerStreamSock[i].CLIENT_CONNECT == false)
                            continue;
                    }
                    //
                    if (FlowStreamControl[i] == null)
                        continue;
                    if (FlowStreamControl[i].GetCurrentRecvProcessData() != null)
                        continue;
                    //
                    thisConnInfo[i].m_iSendStreamQueueSize = FlowStreamControl[i].GetSendQueueSize();
                    thisConnInfo[i].m_iRecvStreamQueueSize = FlowStreamControl[i].GetRecvQueueSize();

                    if ((thisConnInfo[i].m_iSendStreamQueueSize + 1) == int.MaxValue)
                        thisConnInfo[i].m_iSendStreamQueueSize = 0;
                    if ((thisConnInfo[i].m_iRecvStreamQueueSize + 1) == int.MaxValue)
                        thisConnInfo[i].m_iRecvStreamQueueSize = 0;
                    //
                    string strProcessDebugInfo = string.Empty;
                    strProcessDebugInfo = "[" + (i).ToString("D2") + "]" +
                        "[" + ConnPool[i].nUseStreamPort + "]" +
                        "[" + thisConnInfo[i].strConnectHostID + "]" +
                        "[" + thisConnInfo[i].strConnectSection + "]";

                    bState = QueryRecvStreamProcess(i, strProcessDebugInfo);
                }
            }
            catch (Exception e)
            {
                /*
                MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + CpLogProcessInfo.strProcessDebugInfo + @" CpLog - " + $"ST Frame Count {st.FrameCount} [SystemX.Net.MiddlewareUI : MainForm.SetMemoryMappedFile] " + e.Message, ConsoleColor.Red, LogMessageLevel.FATAL);

                for (int n = 0; n < st.FrameCount; n++)
                {
                    StackFrame sf = st.GetFrame(n);
                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + CpLogProcessInfo.strProcessDebugInfo + @" CpLog - " + $"ST Method To String {sf.GetMethod()} [SystemX.Net.MiddlewareUI : MainForm.SetMemoryMappedFile] " + e.Message, ConsoleColor.Red, LogMessageLevel.FATAL);
                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + CpLogProcessInfo.strProcessDebugInfo + @" CpLog - " + $"ST Method Name {sf.GetMethod().Name} [SystemX.Net.MiddlewareUI : MainForm.SetMemoryMappedFile] " + e.Message, ConsoleColor.Red, LogMessageLevel.FATAL);
                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + CpLogProcessInfo.strProcessDebugInfo + @" CpLog - " + $"ST Line Number {sf.GetFileLineNumber()} [SystemX.Net.MiddlewareUI : MainForm.SetMemoryMappedFile] " + e.Message, ConsoleColor.Red, LogMessageLevel.FATAL);
                    MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + CpLogProcessInfo.strProcessDebugInfo + @" CpLog - " + $"ST Column Number {sf.GetFileColumnNumber()} [SystemX.Net.MiddlewareUI : MainForm.SetMemoryMappedFile] " + e.Message, ConsoleColor.Red, LogMessageLevel.FATAL);
                }
                */

                MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @" Recv queue stream process unknown fail.[SystemX.Net.MiddlewareUI : MainForm.QueryRecvStreamQueue]\r\n" + e.Message, ConsoleColor.Yellow, LogMessageLevel.DEBUG);

                bState = false;
            }
            finally
            {
                bTaskStreamWaitLock = false;

                nStreamNumber++;

                if (nStreamNumber >= ALL_MANAGE_NUM)
                    nStreamNumber = 0;
            }

            return bState;
        }
    }
}
