using WinRT.BxlComponent.Plugin.BxlMpXamarinSDK.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace WinRT.BxlComponent.Plugin.BxlMpXamarinSDK
{
    internal class MPosDataParser
    {
        //public event EventHandler<Object> ToastCompletedEvent;
        protected int _DeviceId = -1;
        protected event Action<int> _StatusDataEnQueueEvent = null;
        protected event Action<int> _NormalDataEnQueueEvent = null;
        protected int _statusIdxToCompare = 0;
        protected int _tempDataIdx = 0;
        protected int _nRepeatCount = 4;
        protected byte[] _dbAsBSave = new byte[4];
        protected byte[] _dbASBTemp = new byte[8];
        protected static readonly object _Locker = new object();
        public Queue<byte> _statusQueue { get; private set; } = new Queue<byte>();
        public Queue<byte> _dataQueue { get; private set; } = new Queue<byte>();
        protected Queue<byte> _tempDataQueue { get; set; } = new Queue<byte>();
        public MPosDataParser(Action<int> statusDataEvent = null, Action<int> normalDataEvent = null)
        {
            MPosDebug.WriteLine("MPosDataParser MPosDataParser : constructor");
            if (statusDataEvent != null)
                _StatusDataEnQueueEvent += statusDataEvent;
            if (normalDataEvent != null)
                _NormalDataEnQueueEvent += normalDataEvent;
        }
        protected bool is1stASB(byte dbData)
        {
            if (dbData != 0x14 && dbData != 0x5C && dbData != 0x3C && dbData != 0x1C &&
                dbData != 0x10 && dbData != 0x58 && dbData != 0x38 && dbData != 0x18 &&
                dbData != 0x30 && dbData != 0x34 && dbData != 0x54)
                return false;
            return true;
        } // 0xx1xx00 ASB 1st Value
        protected bool is2stASB(byte dbData)
        {
            //added 0x01 //mobile low battery 
            if (dbData != 0x00 && dbData != 0x20 && dbData != 0x40 && dbData != 0x60
                && dbData != 0x02 && dbData != 0x08 && dbData != 0x04 && dbData != 0x01)
                return false;
            return true;
        }
        protected bool is3stASB(byte dbData)
        {
            if (dbData != 0x00 && dbData != 0x03 && dbData != 0x0C && dbData != 0x0F && dbData != 0x80)
                return false;
            return true;
        }
        protected bool is4stASB(byte dbData)
        {
            //add 0x4F. restick black mark.
            //add 0x00. 275II fw bug.
            if (dbData != 0x0F && dbData != 0x4F && dbData != 0x00)
                return false;
            return true;
        }
        protected bool compareASB(byte dbByte, int np)
        {
            switch (np)
            {
                case 0: return is1stASB(dbByte);
                case 1: return is2stASB(dbByte);
                case 2: return is3stASB(dbByte);
                case 3: return is4stASB(dbByte);
                default:
                    return false;
            }
        }
        public virtual void dataEnqueue(byte[] readByteArray, bool isNormalData = false)
        {
            int readBufferIdx = 0;

            if (readByteArray != null){
                MPosDebug.WriteLine("MPosDataParser dataEnqueue : readByteArray.Length = " + readByteArray.Length);
            }

            if (isNormalData) {
                foreach (var d in readByteArray)
                    _dataQueue.Enqueue(d);
                _NormalDataEnQueueEvent?.Invoke(_DeviceId);
                return;
            }

            while (readBufferIdx < readByteArray.Length)
            {
                if (compareASB(readByteArray[readBufferIdx], _statusIdxToCompare)){
                    _dbAsBSave[_statusIdxToCompare++] = readByteArray[readBufferIdx];
                    _dbASBTemp[_tempDataIdx++] = readByteArray[readBufferIdx];
                    readBufferIdx++;
                    MPosDebug.WriteLine("MPosDataParser dataEnqueue : (1) _tempDataIdx = " + _tempDataIdx + ", _statusIdxToCompare = " + _statusIdxToCompare);
                }
                else if (readByteArray[readBufferIdx] == 0x08)
                {
                    byte[] pattern = new byte[] { 0x08, 0x7e, 0x50 };
                    byte[] pattern2 = new byte[] { 0x08, 0x7e, 0x44 };
                    byte[] pattern3 = new byte[] { 0x08, 0x7e, 0x52 };
                    if (readByteArray.Take(pattern.Length).SequenceEqual(pattern))
                    {
                        //_DeviceId = 0x00;
                        int length = readByteArray[4];
                        for (int i = 0; i < length; i++)
                            _dataQueue.Enqueue(readByteArray[5 + i]);
                        _NormalDataEnQueueEvent?.Invoke(0xff);
                        readBufferIdx += (5 + length);
                    }
                    else if (readByteArray.Take(pattern2.Length).SequenceEqual(pattern2))
                    {
                        int devID = readByteArray[3];
                        int length = readByteArray[4] + (readByteArray[5] * 256) + (readByteArray[6] * 256 * 256) + (readByteArray[7] * 256 * 256 * 256);
                        for (int i = 0; i < length; i++)
                            _dataQueue.Enqueue(readByteArray[8 + i]);
                        _NormalDataEnQueueEvent?.Invoke(devID);
                        readBufferIdx += (8 + length);
                    }
                    else if (readByteArray.Take(pattern3.Length).SequenceEqual(pattern3))
                    {
                        readBufferIdx += 6;
                    }
                    else
                        _tempDataQueue.Enqueue(readByteArray[readBufferIdx++]);
                }
                else
                {
                    // ASB로 판단하였던 데이터 + Non-ASB 데이터 결합 후, 큐에 추가
                    if (_statusIdxToCompare != 0)
                    {
                        for (int ic = 0; ic < _tempDataIdx; ic++){
                            MPosDebug.WriteLine("MPosDataParser dataEnqueue : (1) ic = " + ic);
                            _tempDataQueue.Enqueue(_dbASBTemp[ic]);
                        }
                        _statusIdxToCompare = 0;
                        _tempDataIdx = 0;
                        _nRepeatCount = 4;
                        MPosDebug.WriteLine("MPosDataParser dataEnqueue : continue ");
                        continue;
                    }

                    MPosDebug.WriteLine("MPosDataParser dataEnqueue : _tempDataQueue.Enqueue, readByteArray[" + readBufferIdx+"] = " + readByteArray[readBufferIdx].ToString("X2"));
                    _tempDataQueue.Enqueue(readByteArray[readBufferIdx++]);
                }

                if (_statusIdxToCompare != 0 && _statusIdxToCompare != _tempDataIdx)
                {
                    MPosDebug.WriteLine("MPosDataParser dataEnqueue : (2) _tempDataIdx = " + _tempDataIdx + ", _statusIdxToCompare = " + _statusIdxToCompare);
                    for (int ic = 0; ic < _tempDataIdx; ic++){
                        MPosDebug.WriteLine("MPosDataParser dataEnqueue : (2) ic = " + ic);
                        _tempDataQueue.Enqueue(_dbASBTemp[ic]);
                    }
                    _statusIdxToCompare = 0;
                    _tempDataIdx = 0;
                    _nRepeatCount = 4;
                }

                if (_statusIdxToCompare == 4)
                {
                    MPosDebug.WriteLine("MPosDataParser dataEnqueue : _statusIdxToCompare = 4");
                    _tempDataIdx = 0;
                    _nRepeatCount = 4;
                    _statusIdxToCompare = 0;

                    if (_statusQueue.Count > 12){
                        MPosDebug.WriteLine("MPosDataParser dataEnqueue : _statusQueue.clear()");
                        _statusQueue.Clear();
                    }
                    for (int ic = 0; ic < 4; ic++){
                        MPosDebug.WriteLine("MPosDataParser dataEnqueue : (3) ic = " + ic);
                        _statusQueue.Enqueue(_dbAsBSave[ic]);
                    }
                    var hexaString = (from d in _dbAsBSave select d.ToString("X2")).Aggregate((current, next) => current + " " + next);
                    MPosDebug.WriteLine("MPosDataParser dataEnqueue : _statusQueue Count = " + _statusQueue.Count + ", hexaString = " + hexaString);
                    _StatusDataEnQueueEvent?.Invoke(_DeviceId);
                }

                if (_statusIdxToCompare != 0)
                {
                    MPosDebug.WriteLine("MPosDataParser dataEnqueue : _statusIdxToCompare != 0, " + " _nRepeatCount = " + _nRepeatCount);
                    if (_nRepeatCount == 0){
                        _statusIdxToCompare = 0;
                        _nRepeatCount = 4;
                    }
                    else{
                        _nRepeatCount--;
                    }
                }

                while (_tempDataQueue.Count > 0)
                    _dataQueue.Enqueue(_tempDataQueue.Dequeue());
                _tempDataQueue.Clear();
            } // End While loop

            MPosDebug.WriteLine("MPosDataParser dataEnqueue : _dataQueue.Count = " + _dataQueue.Count + " _statusQueue.Count = " + _statusQueue.Count);
            if (_dataQueue.Count > 0){
                _NormalDataEnQueueEvent?.Invoke(_DeviceId);
            }
        }
    }
    internal class MPosBgateDataParser : MPosDataParser
    {
        private MemoryStream _MemStream;
        //private BinaryWriter _Writer;
        public MPosBgateDataParser(Action<int> statusDataEvent = null, Action<int> normalDataEvent = null) : base(statusDataEvent, normalDataEvent)
        {
            MPosDebug.WriteLine("MPosDataParser MPosBgateDataParser : constructor");
            _MemStream = new MemoryStream();
            //_Writer = new BinaryWriter(_MemStream);
        }
        public override void dataEnqueue(byte[] readByteArray, bool isNormalData = false)
        {
            MPosDebug.WriteLine("MPosBgateDataParser dataEnqueue : START");

            //int matchedIndex = -1;

            if (readByteArray != null)
                _MemStream.Write(readByteArray, 0, readByteArray.Length);

            if (_MemStream.Length == 0){
                MPosDebug.WriteLine("MPosBgateDataParser dataEnqueue : ZERO LENGTH");
                MPosDebug.WriteLine("MPosBgateDataParser dataEnqueue : END");
            }

            var dataStream  = _MemStream.ToArray();
            var positions   = MPosByteArrayHelper.searchBytePattern(new byte[] { 0x42, 0x58, 0x4C }, dataStream);

            MPosDebug.WriteLine("MPosBgateDataParser dataEnqueue : positions.Count = " + positions.Count);
            if (positions.Count == 0){
                MPosDebug.WriteLine("MPosBgateDataParser dataEnqueue : no patterns matched");
                MPosDebug.WriteLine("MPosBgateDataParser dataEnqueue : END");
                return;
            }

            MPosDebug.WriteLine("MPosBgateDataParser dataEnqueue : _MemStream.Position = " + _MemStream.Position);

            _MemStream.Dispose();
            _MemStream = new System.IO.MemoryStream();

            for (int i = 0; i < positions.Count; i++)
            {
                byte[] subArray = null;

                if (i + 1 < positions.Count){
                    subArray = MPosByteArrayHelper.getSubDataArray(dataStream, positions[i], positions[i+1]-positions[i]);
                }
                else{
                    // last item
                    subArray = MPosByteArrayHelper.getSubDataArray(dataStream, positions[i], dataStream.Length);
                    if (subArray.Length <= 6){
                        MPosDebug.WriteLine("MPosBgateDataParser dataEnqueue : NEED MORE DATA-1");
                        _MemStream.Write(subArray, 0, subArray.Length);
                        break;
                    }
                    if (subArray.Length < (subArray[4] * 256 + subArray[5]) + 6){
                        MPosDebug.WriteLine("MPosBgateDataParser dataEnqueue : subArray.Length = " + subArray.Length + ", size needed = " + (subArray[4] * 256 + subArray[5]) + 6);
                        MPosDebug.WriteLine("MPosBgateDataParser dataEnqueue : NEED MORE DATA-2");
                        _MemStream.Write(subArray, 0, subArray.Length);
                        break;
                    }
                }

                var deviceId = _DeviceId = subArray[3];
                var cmdLength = subArray[4] * 256 + subArray[5];
                MPosDebug.WriteLine("MPosBgateDataParser dataEnqueue : deviceId = " + deviceId + ", dataLength = " + cmdLength);

                switch (deviceId)
                {
                    case 0x00:
                        MPosDebug.WriteLine("MPosBgateDataParser dataEnqueue : Command");
                        base.dataEnqueue(subArray.Skip(6).Take(cmdLength).ToArray(), true);
                        break;
                    case 0xff:
                        MPosDebug.WriteLine("MPosBgateDataParser dataEnqueue : PnP");
                        base.dataEnqueue(subArray.Skip(6).Take(cmdLength).ToArray(), true);
                        break;
                    default:
                        base.dataEnqueue(subArray.Skip(6).Take(cmdLength).ToArray(), (deviceId >= 10 && deviceId < 20)? false : true);
                        break;
                }
            }

            MPosDebug.WriteLine("MPosBgateDataParser dataEnqueue : END");
        }
    }
}
