using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;
using Edge.Core.Processor.Communicator;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Dfs.WayneChina.HengshanPayTerminal
{
    public class StateMachineMessageCutter : IMessageCutter<byte[]>
    {
        #region Internal State enum

        private enum State
        {
            Uninitialized,
            LengthReady,
            BodyReady,
            EtxReady,
            CrcReady
        }

        #endregion

        #region Fields

        public byte[] Message { get; private set; }

        public event EventHandler OnMessageCut;
        public event EventHandler<MessageCutterInvalidMessageReadEventArg> OnInvalidMessageRead;

        private string loggerAppendix = "HengshanPay Terminal";
        private readonly SizableWindow<byte> window;

        private State nextState = State.Uninitialized;

        private const int STX = 0xFA;

        #endregion

        #region Logger

        static NLog.Logger innerLogger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("Communicator");
        
        #endregion

        public StateMachineMessageCutter()
        {
            window = new SizableWindow<byte>();

            window.OnWindowFull += (data) =>
            {
                switch (nextState)
                {
                    case State.Uninitialized:
                        if (data.First() == STX)
                        {
                            window.NewSize = 6;
                            nextState = State.LengthReady;
                        }
                        else
                        {
                            OnInvalidMessageRead?.Invoke(this, new MessageCutterInvalidMessageReadEventArg()
                            {
                                Message = "invalid byte[0]: 0x" + data.First().ToString("x2") + ", will skip"
                            });
                            window.Clear();
                        }
                        break;

                    case State.LengthReady:

                        var countSTX = window.Count(b => b == STX);
                        if (countSTX > 1)
                        {
                            //Console.WriteLine($"0xFA count: {countSTX}");
                            innerLogger.Info($"0xFA count: {countSTX}");
                            int index = window.ToList().LastIndexOf(STX);


                            var tempArray = window.Skip(index).ToArray();
                            window.Clear();
                            nextState = State.Uninitialized;
                            Feed(tempArray);

                            return;


                        }



                        int bodyLen = window[4] * 10 + window[5];

                        if (bodyLen > 256 || bodyLen < 2)
                        {
                            var safe8 = this.OnInvalidMessageRead;
                            safe8?.Invoke(this, new MessageCutterInvalidMessageReadEventArg()
                            {
                                Message = "Message body length is not valid, len is: " + bodyLen
                            });
                            nextState = State.Uninitialized;
                            window.Clear();
                            window.NewSize = 1;
                            return;
                        }

                        window.NewSize += bodyLen;
                        nextState = State.BodyReady;

                        break;

                    case State.BodyReady:

                        window.NewSize = window.Skip(4).Take(2).ToArray().ToInt32() + 6 + 3;
                        nextState = State.CrcReady;
                        break;

                    case State.CrcReady:

                        var stxCount = window.Count(b => b == STX);
                        if (stxCount > 1)
                        {
                            //Console.WriteLine($"0xFA count: {stxCount}");
                            innerLogger.Info($"0xFA count: {stxCount}");
                            int length = window.Count;
                            if (window[length - 3] == 0xFF)
                            {
                                //Console.WriteLine("ETX exists, consider it a complete message");
                                innerLogger.Info("ETX exists, consider it a complete message");
                            }
                            else
                            {
                                int index = window.ToList().LastIndexOf(STX);

                                if (index + 2 <= window.Count - 1)
                                {
                                    byte trailingByte1 = window[index + 1];
                                    byte trailingByte2 = window[index + 2];

                                    if (trailingByte1 == trailingByte2 && trailingByte1 <= 6 && trailingByte1 > 0)
                                    {
                                        //Console.WriteLine("Possible mix of incompleted messages");
                                        innerLogger.Info("Possible mix of incompleted messages");
                                        var tempArray = window.Skip(index).ToArray();
                                        window.Clear();
                                        nextState = State.Uninitialized;
                                        Feed(tempArray);

                                        return;
                                    }
                                }
                            }
                        }

                        Message = window.ToArray();
                        var safe = OnMessageCut;
                        safe?.Invoke(this, null);
                        nextState = State.Uninitialized;
                        window.Clear();
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            };
        }

        public void Feed(byte[] data)
        {
            for (int i = 0; i < data.Length; i++)
            {
                window.Add(data[i]);
            }
        }

        private int Get0xFAPairCountInWindow(IEnumerable<byte> data)
        {
            return (int)Math.Round(((double)(data.Count(w => w == 0xFA)) / 2), MidpointRounding.AwayFromZero);
        }

        public int Reduce0xFAPair(IList<byte> target, int startIndex)
        {
            int reducedCount = 0;
            var faAppearedPositions = new List<int>();
            for (int i = startIndex; i < target.Count; i++)
            {
                if (target[i] == 0xFA)
                {
                    if (i <= (target.Count - 2))
                    {
                        if (target[i + 1] == 0xFA)
                        {
                            faAppearedPositions.Add(i);
                            i++;
                        }
                    }
                }
            }

            for (int i = 0; i < faAppearedPositions.Count; i++)
            {
                target.RemoveAt(faAppearedPositions[i] - i);
                reducedCount++;
            }

            return reducedCount;
        }
    }
}