using System;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SystemX.Common.Serialization;
using SystemX.Net.BaseProtocol;
using SystemX.Net.Schedule;

namespace SystemX.Common.Protocol
{
    using static SystemX.Net.Platform.Common.Util.LogMessage;

    public class PacketProgressChangedEventArgs : EventArgs
    {
        public PacketProgressChangedEventArgs(int progressPercentage, object userState)
        {
            this.ProgressPercentage = progressPercentage;

            this.UserState = userState;
        }

        public int ProgressPercentage { get; private set; }

        public object UserState { get; private set; }
    }

    public class PacketAnalyzerCompletedEventArgs : AsyncCompletedEventArgs
    {
        private object result;

        public PacketAnalyzerCompletedEventArgs(object result, Exception error, bool cancelled, object userState) : base(error, cancelled, userState)
        {
            this.result = result;
        }

        public object Result
        {
            get
            {
                base.RaiseExceptionIfNecessary();

                return this.result;
            }
        }

        [EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
        public new object UserState
        {
            get
            {
                return base.UserState;
            }
        }
    }
    public class PacketCalculate
    {
        public event EventHandler<PacketAnalyzerCompletedEventArgs> PacketAnalyzerCompleted;
        public event EventHandler<PacketProgressChangedEventArgs> ProgressChanged;

        /// <summary>
        /// ThreadContext에서 실행하기 위한 AsyncOperation.
        /// </summary>
        private AsyncOperation asyncOperation;

        /// <summary>
        /// AsyncOperation 로 Completed 호출을 위한 델리게이트.
        /// </summary>
        private SendOrPostCallback operationCompleted;

        /// <summary>
        /// AsyncOperation 로 ProgressChanged 호출을 위한 델리게이트.
        /// </summary>
        private SendOrPostCallback progressReporter;

        public bool IsBusy { get; private set; }
        public bool CancellationPending { get; private set; }

        private readonly Action<object> startDelegate;
        public bool WorkerReportsProgress { get; set; }

        public bool WorkerSupportsCancellation { get; set; }

        #region CREATE
        public PacketCalculate()
        {
            this.startDelegate = this.WorkerThreadStart;
            this.operationCompleted = this.SendOperationCompletedEvent;
            this.progressReporter = this.SendProgressChangedEvent;
        }
        #endregion
        private void SendOperationCompletedEvent(object arg)
        {
            this.IsBusy = false;

            this.CancellationPending = false;

            var eventArgs = arg as PacketAnalyzerCompletedEventArgs;

            if (eventArgs == null)
                return;

            var completed = this.PacketAnalyzerCompleted;

            if (completed != null)
                completed(this, eventArgs);
        }
        private void SendProgressChangedEvent(object arg)
        {
            var eventArgs = arg as PacketProgressChangedEventArgs;

            if (eventArgs == null)
                return;

            var progressChanged = this.ProgressChanged;

            if (progressChanged != null)
                progressChanged(this, eventArgs);
        }
        private void WorkerThreadStart(object argument)
        {
            object result = null;

            Exception error = null;

            try
            {
                result = this.Calculate(argument, true);
            }
            catch (Exception ex)
            {
                error = ex;
            }

            this.asyncOperation.PostOperationCompleted(this.operationCompleted, new PacketAnalyzerCompletedEventArgs(result, error, this.CancellationPending, null));
        }
        private void ReportProgress(int percentProgress)
        {
            if (!this.WorkerReportsProgress)
            {
                //throw new InvalidOperationException("BackgroundWorker_WorkerDoesntReportProgress.[SystemX.Common.AsyncEvent : PacketCalculate.ReportProgress]");
                MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @"BackgroundWorker_WorkerDoesntReportProgress.[SystemX.Common.AsyncEvent : PacketCalculate.ReportProgress]", ConsoleColor.Yellow, LogMessageLevel.DEBUG);
            }

            var arg = new PacketProgressChangedEventArgs(percentProgress, null);

            if (this.asyncOperation != null)
            {
                this.asyncOperation.Post(this.progressReporter, arg);
            }
            else
            {
                this.progressReporter(arg);
            }
        }
        public void CalculateAsync(object argument)
        {
            this.IsBusy = true;

            this.CancellationPending = false;

            this.asyncOperation = AsyncOperationManager.CreateOperation(null);

            this.startDelegate.BeginInvoke(argument, null, null);
        }

        public void CancelAsync()
        {
            if (!this.WorkerSupportsCancellation)
            {
                //throw new InvalidOperationException("BackgroundWorker_WorkerDoesntSupportCancellation.[SystemX.Common.AsyncEvent : PacketCalculate.CancelAsync]");
                MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @"General packet marshalling failed.[SystemX.Common : COMMON.GetHeaderProtocol]", ConsoleColor.Yellow, LogMessageLevel.DEBUG);
            }

            this.CancellationPending = true;
        }
        public XData Calculate(object objPacket, bool isAsync = false)
        {
            XData ResultData = null;

            XPacket getPacket = (XPacket)objPacket;

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

            BASE_PROTOCOL GET_PROTOCOL = new BASE_PROTOCOL();
            BASE_PROTOCOL.PROTOCOL_CODE GET_CODE = new BASE_PROTOCOL.PROTOCOL_CODE();
            HEADER_PACKET GET_HEADER = new HEADER_PACKET();

            byte ucSetLabel = getPacket.nLabel;

            bool bReplayResult = true;
            object objData = null;

            try
            {
                GET_PROTOCOL = XCommons.GetHeaderProtocol(iStoreCnt, recvStoreBuffer);
                GET_HEADER = XCommons.GetHeaderPacket(iStoreCnt, recvStoreBuffer);

                GET_CODE = GET_PROTOCOL.GET_CURRENT_PROTOCOL;

                if (GET_CODE == BASE_PROTOCOL.PROTOCOL_CODE.CONNECT_STATE)
                {
                    PING_PACKET PacketData = new PING_PACKET();
                    PacketData = XCommons.ByteStreamToSpecialObject<PING_PACKET>(GET_CODE, iStoreCnt, recvStoreBuffer);

                    objData = PacketData;
                }
                else if (GET_CODE == BASE_PROTOCOL.PROTOCOL_CODE.MIDDLEWARE_MESSAGE)
                {
                    MESSAGE_PACKET PacketData = new MESSAGE_PACKET();
                    PacketData = XCommons.ByteStreamToSpecialObject<MESSAGE_PACKET>(GET_CODE, iStoreCnt, recvStoreBuffer);

                    objData = PacketData;
                }
                else if (GET_CODE == BASE_PROTOCOL.PROTOCOL_CODE.HOST_INFO_CHECK)
                {
                    SYSTEM_HOST_PACKET PacketData = new SYSTEM_HOST_PACKET();
                    PacketData = XCommons.ByteStreamToSpecialObject<SYSTEM_HOST_PACKET>(GET_CODE, iStoreCnt, recvStoreBuffer);

                    objData = PacketData;
                }
                else if (GET_CODE == BASE_PROTOCOL.PROTOCOL_CODE.SYNC_TIME_SERVER)
                {
                    TIME_PACKET PacketData = new TIME_PACKET();
                    PacketData = XCommons.ByteStreamToSpecialObject<TIME_PACKET>(GET_CODE, iStoreCnt, recvStoreBuffer);

                    objData = PacketData;
                }
                else if (GET_CODE == BASE_PROTOCOL.PROTOCOL_CODE.FILE_TRANSFER)
                {
                    byte[] PacketData = XCommons.ByteStreamToObject<byte[]>(GET_CODE, iStoreCnt, recvStoreBuffer);

                    objData = PacketData;
                }
                else if (GET_CODE == BASE_PROTOCOL.PROTOCOL_CODE.DATASET_TRANSEFER)
                {
                    DataSet PacketData = XCommons.ByteStreamToObject<DataSet>(GET_CODE, iStoreCnt, recvStoreBuffer);
       
                    bReplayResult = GET_HEADER.bResponsState;

                    objData = PacketData;
                }
                else if (GET_CODE == BASE_PROTOCOL.PROTOCOL_CODE.RAW_SIZE)
                {
                    byte[] PacketData = XCommons.ByteStreamToObject<byte[]>(GET_CODE, iStoreCnt, recvStoreBuffer);

                    objData = PacketData;
                }
                else if (GET_CODE == BASE_PROTOCOL.PROTOCOL_CODE.INITILALIZE_INFO)
                {
                    COMM_INFO_PACKET PacketData = new COMM_INFO_PACKET();
                    PacketData = XCommons.ByteStreamToSpecialObject<COMM_INFO_PACKET>(GET_CODE, iStoreCnt, recvStoreBuffer);

                    objData = PacketData;
                }
                else if (GET_CODE == BASE_PROTOCOL.PROTOCOL_CODE.SYSTEM_QUERY)
                {
                    QUERY_PACKET PacketData = new QUERY_PACKET();
                    PacketData = XCommons.ByteStreamToSpecialObject<QUERY_PACKET>(GET_CODE, iStoreCnt, recvStoreBuffer);

                    objData = PacketData;
                }
                else if (GET_CODE == BASE_PROTOCOL.PROTOCOL_CODE.USER_QUERY)
                {
                    QUERY_PACKET PacketData = new QUERY_PACKET();
                    PacketData = XCommons.ByteStreamToSpecialObject<QUERY_PACKET>(GET_CODE, iStoreCnt, recvStoreBuffer);

                    objData = PacketData;
                }
                else if (GET_CODE == BASE_PROTOCOL.PROTOCOL_CODE.TRANSFER_RESULT)
                {
                    TRANSFER_PACKET PacketData = new TRANSFER_PACKET();
                    PacketData = XCommons.ByteStreamToSpecialObject<TRANSFER_PACKET>(GET_CODE, iStoreCnt, recvStoreBuffer);

                    objData = PacketData;
                }
                else if (GET_CODE == BASE_PROTOCOL.PROTOCOL_CODE.ETC)
                {
                    USER_PACKET PacketData = new USER_PACKET();
                    PacketData = XCommons.ByteStreamToSpecialObject<USER_PACKET>(GET_CODE, iStoreCnt, recvStoreBuffer);

                    objData = PacketData;          
                }
                else if (GET_CODE == BASE_PROTOCOL.PROTOCOL_CODE.PROCESS_QUERY)
                {
                    PROCESS_PACKET PacketData = new PROCESS_PACKET();
                    PacketData = XCommons.ByteStreamToSpecialObject<PROCESS_PACKET>(GET_CODE, iStoreCnt, recvStoreBuffer);

                    objData = PacketData;
                }
                else
                {      
                    GET_PROTOCOL = new BASE_PROTOCOL(BASE_PROTOCOL.PROTOCOL_CODE.NONE);

                    bReplayResult = false;

                    objData = null;
                }
            }
            catch (Exception e)
            {
                GET_PROTOCOL = new BASE_PROTOCOL(BASE_PROTOCOL.PROTOCOL_CODE.NONE);

                bReplayResult = false;

                objData = null;

                string strErrMsg = e.Message;
            }

            ResultData = new XData();
            ResultData.dtTime = DateTime.Now;
            ResultData.bReplayResult = bReplayResult;
            ResultData.BaseProtocol = GET_PROTOCOL;
            ResultData.HeaderPacket = GET_HEADER;
            ResultData.objData = objData;
            ResultData.nLabel = ucSetLabel;

            return ResultData;
        }
    }

    public class EventBaseAsyncPattern
    {
        public void Run(int p)
        {
            var PacketAnalyzerWait = new PacketCalculate
            {
                WorkerReportsProgress = true,

                WorkerSupportsCancellation = true
            };

            PacketAnalyzerWait.PacketAnalyzerCompleted += calculatorPacket_CalculateCompleted;
            PacketAnalyzerWait.ProgressChanged += calculatorPacket_ProgressChanged;

            var i = 0;
            while (i < 5)
            {
                if (PacketAnalyzerWait.IsBusy)
                    continue;

                PacketAnalyzerWait.CalculateAsync(p + i);

                i++;
            }
        }

        void calculatorPacket_ProgressChanged(object sender, PacketProgressChangedEventArgs e)
        {
            //"Calculating : {0}% Completed", e.ProgressPercentage;
        }

        void calculatorPacket_CalculateCompleted(object sender, PacketAnalyzerCompletedEventArgs e)
        {
            //e.Result;
        }
    }
}
