using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using SystemX.Common;
using SystemX.Common.Archive;
using SystemX.Common.Serialization;
using SystemX.Net.BaseProtocol;

namespace SystemX.Net.Schedule
{
    public class ScheduleLabelManage
    {
        private byte ucLabelSize = 128;

        // 해당 위치 토큰 사용 여부
        private bool[] bLabelUse;
        // 해당 위치 처리 여부
        private bool[] bLabelComplete;

        private XData[] XLabelData;

        public byte GetLabelSize()
        {
            return ucLabelSize;
        }

        public void SetManageLabel()
        {
            bLabelUse = new bool[ucLabelSize];
            bLabelComplete = new bool[ucLabelSize];

            XLabelData = new XData[ucLabelSize];

            bLabelUse.ToArray().Initialize();
            bLabelComplete.ToArray().Initialize();
        }

        public byte GetLabel(int nLabelPos = -1)
        {
            byte ucLabelPos = 0;

            if (nLabelPos == -1)
            {
                for (int i = 1; i < ucLabelSize - 1; i++)
                {
                    if (bLabelUse[i] == false)
                    {
                        ucLabelPos = (byte)i;

                        break;
                    }
                }
            }
            else
                ucLabelPos = (byte)nLabelPos;

            if (ucLabelPos != 0)
            {
                bLabelComplete[ucLabelPos] = false;
                bLabelUse[ucLabelPos] = true;
            }

            return ucLabelPos;
        }

        public bool SetLabel(byte ucCheckLabelPos, XData setXLabelData)
        {
            if (ucCheckLabelPos < 0 || ucCheckLabelPos >= ucLabelSize)
                return false;

            if (bLabelUse[ucCheckLabelPos])
            {
                XLabelData[ucCheckLabelPos] = setXLabelData;

                bLabelComplete[ucCheckLabelPos] = true;
            }

            return bLabelComplete[ucCheckLabelPos];
        }

        public bool ClearLabel(byte ucCheckLabelPos)
        {
            if (ucCheckLabelPos < 0 || ucCheckLabelPos >= ucLabelSize)
                return false;

            if (bLabelUse[ucCheckLabelPos])
            {
                bLabelUse[ucCheckLabelPos] = false;

                bLabelComplete[ucCheckLabelPos] = false;
            }

            return bLabelUse[ucCheckLabelPos];
        }

        public XData CheckLabel(byte ucCheckLabelPos, ref bool bLabelProcessResult)
        {
            if (ucCheckLabelPos < 0 || ucCheckLabelPos >= ucLabelSize)
                return null;

            if (bLabelComplete[ucCheckLabelPos])
            {
                bLabelComplete[ucCheckLabelPos] = false;
                bLabelUse[ucCheckLabelPos] = false;

                bLabelProcessResult = true;

                return XLabelData[ucCheckLabelPos];
            }
            else
            {
                bLabelProcessResult = false;

                return null;
            }
        }
    }

    /// <summary>
    /// Packet Time, Retrty Num, Replay Packet, Packet Data, Packet Event, Label
    /// </summary>

    /// <summary>
    /// Data Time, Data Replay Result, Packet Protocol Info, Packet Header Info, Result Data Object, Label
    /// </summary>
    public class XPacket
    {
        public DateTime dtPacketTime;
        public int iRetryNum;
        public bool bReplayPacket;
        public byte[] ucPacketBytes;
        public SendRecvCallEvent deleCallEvt;
        public byte nLabel;
    }
    public class XData
    {
        public DateTime dtTime;
        public bool bReplayResult;
        public BASE_PROTOCOL BaseProtocol;
        public HEADER_PACKET HeaderPacket;
        public object objData;
        public byte nLabel;
    }

    public enum FlowControlType
    {
        Commnad = 0,
        Stream,
    }

    [Serializable]
    public class XDataSchedule
    {
        public List<byte[]> SendQueue;
        public List<byte[]> ReplySendQueue;
        public List<byte[]> SendRawSetQueue;

        public List<byte[]> RecvQueue;

        public XDataSchedule()
        {
            SendQueue = new List<byte[]>();
            ReplySendQueue = new List<byte[]>();
            SendRawSetQueue = new List<byte[]>();

            RecvQueue = new List<byte[]>();
        } 
    }
    [Serializable]
    public class XSchedule
    {
        public ConcurrentQueue<XPacket> SendQueue { get; }
        public ConcurrentQueue<XPacket> ReplySendQueue { get; }
        public ConcurrentQueue<XPacket> SendRawSetQueue { get; }

        public ConcurrentQueue<XPacket> RecvQueue { get; }
        //
        public ConcurrentQueue<XData> RecvResultData { get; }

        public XSchedule()
        {
            SendQueue = new ConcurrentQueue<XPacket>();
            ReplySendQueue = new ConcurrentQueue<XPacket>();
            SendRawSetQueue = new ConcurrentQueue<XPacket>();

            RecvQueue = new ConcurrentQueue<XPacket>();
            RecvResultData = new ConcurrentQueue<XData>();
        }
    }
    public class EventAwaiter<TEventArgs>
    {
        private readonly TaskCompletionSource<TEventArgs> _eventArrived = new TaskCompletionSource<TEventArgs>();

        private readonly Action<EventHandler<TEventArgs>> _unsubscribe;

        public EventAwaiter(Action<EventHandler<TEventArgs>> subscribe, Action<EventHandler<TEventArgs>> unsubscribe)
        {
            subscribe(Subscription);
            _unsubscribe = unsubscribe;
        }

        public Task<TEventArgs> Task => _eventArrived.Task;

        private EventHandler<TEventArgs> Subscription => (s, e) =>
        {
            _eventArrived.TrySetResult(e);
            _unsubscribe(Subscription);
        };

        /*
        var valueChangedEventAwaiter = new EventAwaiter<YourEventArgs>(
                            h => example.YourEvent += h,
                            h => example.YourEvent -= h);
        await valueChangedEventAwaiter.Task;
        */
    }
}
