using Communicator;
using log4net;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WayneChina_IcCardReader_SinoChem
{
    public class StateMachineMessageCutter : IMessageCutter<byte[]>
    {
        public byte[] Message { get; private set; }

        public event EventHandler OnMessageCut;

        static ILog innerLogger = log4net.LogManager.GetLogger("StateMachine");
        private string loggerAppendix = "WayneChina_IcCardReader_SinoChem msgCutter ";
        private readonly SizableWindow<byte> window;
        private State nextState = State.Uninitialized;

        /// <summary>
        /// 
        /// </summary>
        public StateMachineMessageCutter()
        {
            this.window = new SizableWindow<byte>();
            this.window.OnWindowFull += (data) =>
            {
                switch (nextState)
                {
                    case State.Uninitialized:
                        if (data.First() == 0xFA)
                        {
                            this.window.NewSize = 2;
                            this.nextState = State.PrefixReady;
                            innerLogger.Debug(this.loggerAppendix + " state is State.Uninitialized and next is 0xFF, switch to LengthReady");
                        }
                        else
                            this.window.Clear();
                        break;
                    case State.PrefixReady:
                        if (this.window.Count(h => h == 0xFA) == 1)
                        {
                            innerLogger.Debug(this.loggerAppendix + " 1 time of 0xFA in header window, valid starter, switch to LengthReady");
                            this.nextState = State.LengthReady;
                            this.window.NewSize = 4;
                        }
                        else
                        {
                            innerLogger.Debug(this.loggerAppendix + " 2 times of 0xFA in potential header window, drop all and wait...");
                            // double 0xFA, not a starter.
                            this.nextState = State.Uninitialized;
                            this.window.Clear();
                        }
                        break;
                    case State.LengthReady:
                        //this.DumpWindowToQueue();
                        this.window.NewSize += this.window.Skip(2).First() * 256 + this.window.Skip(3).First() + 2;
                        this.nextState = State.BodyReady;
                        innerLogger.Debug(this.loggerAppendix + " MsgBodyLen caculated with: " + this.window.NewSize);
                        break;
                    case State.BodyReady:
                        //innerLogger.Debug(this.loggerAppendix + " Fire OnMessageConstructed with innerQueue: " + this.buffer.ToHexLogString());
                        this.Message = this.window.ToArray();
                        var safe = this.OnMessageCut;
                        safe?.Invoke(this, null);
                        this.nextState = State.Uninitialized;
                        this.window.Clear();
                        //this.window.NewSize = this.initWindowSize;
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            };
        }

        private enum State
        {
            Uninitialized,
            // single 0xFA
            PrefixReady,
            // 0xFA + 1 byte source address
            HeaderReady,
            // 0xFA + 1 byte source address +  2 bytes real length
            LengthReady,
            // 0xFA + 1 byte source address +  2 bytes real length + real data
            BodyReady,
        }

        public void Feed(byte[] next)
        {
            //innerLogger.Debug(this.loggerAppendix + " " + next.ToHexLogString() + " is feed in Window in state: " + nextState);
            for (int i = 0; i < next.Length; i++)
                this.window.Add(next[i]);
        }
    }
}
