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

namespace WinRT.BxlComponent.Plugin.BxlMpXamarinSDK
{
    internal class MPosDeviceDataProcessor
    {
        private System.IO.MemoryStream _DataStream = new System.IO.MemoryStream();
        private readonly object _Locker = new object();
        public void clearDataStream()
        {
            lock (_Locker)
            {
                if (_DataStream != null)
                    _DataStream.Dispose();
                _DataStream = null;
            }
        }
        public void initDataStream()
        {
            MPosDebug.WriteLine("MPosDeviceDataProcessor initDataStream :  START");
            lock (_Locker)
            {
                if (_DataStream != null){
                    _DataStream.Dispose();
                    _DataStream = null;
                }
                _DataStream = new System.IO.MemoryStream();
                _DataStream.Position = 0;
            }
            MPosDebug.WriteLine("MPosDeviceDataProcessor initDataStream :  END");
        }
        public void writeData(byte[] data)
        {
            lock (_Locker)
            {
                MPosDebug.WriteLine("MPosDeviceDataProcessor onDataReadEvent : data in Hexa = " + MPosByteArrayHelper.hexaString(data));
                if (data != null && _DataStream != null){
                    _DataStream.Write(data, 0, data.Length);
                }
            }
        }
        public async Task<byte[]> getDataFromMemoryStream(byte[] expectedData, int timeout = 2000)
        {
            MPosDebug.WriteLine("MPosDeviceDataProcessor getDataFromMemoryStream : _DataStream.Length-1 = " + (_DataStream != null ? _DataStream.Length : 0));

            List<int> dataPatternIndexList = null;
            var stopwatch = new Stopwatch();
            stopwatch.Start();

            while (true)
            {
                await Task.Delay(10);
                stopwatch.Stop();
                dataPatternIndexList = MPosByteArrayHelper.searchBytePattern(expectedData, _DataStream.ToArray());
                if (dataPatternIndexList.Count > 0)
                    break;
                if (stopwatch.ElapsedMilliseconds > timeout)
                {
                    MPosDebug.WriteLine("MPosDeviceDataProcessor getDataFromMemoryStream : TIME-OUT ");
                    break;
                }
                stopwatch.Start();
            }

            byte[] data = null;

            lock (_Locker)
            {
                if (_DataStream.Length == 0)
                {
                    MPosDebug.WriteLine("MPosDeviceDataProcessor getDataFromMemoryStream : no data read");
                    return null;
                }

                var wholeData = _DataStream.ToArray();

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

                if (dataPatternIndexList.Count > 0){
                    data = wholeData.Take(dataPatternIndexList.First()).ToArray();
                    var remainder = wholeData.Skip(dataPatternIndexList.First() + expectedData.Length).Take(wholeData.Length).ToArray();
                    _DataStream.Write(remainder, 0, remainder.Length);
                }
                else{
                    data = wholeData;
                }
            }

            initDataStream();

            MPosDebug.WriteLine("MPosDeviceDataProcessor getDataFromMemoryStream : _DataStream.Length-2 = " + (_DataStream != null ? _DataStream.Length : 0));
            return data;
        }
        public async Task<byte[]> getDataFromMemoryStream(int expectedDataLength, Action<byte[]> readEvent = null, int timeout = 1500)
        {
            MPosDebug.WriteLine("MPosDeviceDataProcessor getDataFromMemoryStream : expectedDataLength = " + expectedDataLength + ", timeout = " + timeout);
            MPosDebug.WriteLine("MPosDeviceDataProcessor getDataFromMemoryStream : _DataStream.Length-1 = " + (_DataStream != null ? _DataStream.Length : 0));

            var stopwatch = new Stopwatch();
            stopwatch.Start();

            while (expectedDataLength <= 0 || _DataStream.Position < expectedDataLength)
            {
                await Task.Delay(10);
                stopwatch.Stop();
                if (stopwatch.ElapsedMilliseconds > timeout){
                    MPosDebug.WriteLine("MPosDeviceDataProcessor getDataFromMemoryStream : TIME-OUT ");
                    break;
                }
                stopwatch.Start();
            }

            byte[] data = null;

            lock (_Locker)
            {
                if (_DataStream.Length == 0){
                    MPosDebug.WriteLine("MPosDeviceDataProcessor getDataFromMemoryStream : no data read");
                    return null;
                }

                var wholeData = _DataStream.ToArray();
                _DataStream.Dispose();
                _DataStream = new System.IO.MemoryStream();

                if (expectedDataLength > 0){
                    data = wholeData.Take(expectedDataLength).ToArray();
                    var remainder = wholeData.Skip(expectedDataLength).Take(wholeData.Length).ToArray();
                    _DataStream.Write(remainder, 0, remainder.Length);
                }
                else{
                    data = wholeData;
                }
            }

            readEvent?.Invoke(data);

            initDataStream();

            MPosDebug.WriteLine("MPosDeviceDataProcessor getDataFromMemoryStream : _DataStream.Length-2 = " + (_DataStream != null ? _DataStream.Length : 0));
            return data;
        }
    }
}