using iq2dev.com.IQCommLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace iq2dev.com.iqCommLib
{
    struct MST_FastProcessingStateT
    {
        public bool IsIsduResponseReady;
        public bool IsCachedIsduAvailable;

        public MST_FastProcessingStateT(byte data)
        {
            IsIsduResponseReady = (data & 0x01) == 0x01 ? true : false;
            IsCachedIsduAvailable = (data & 0x02) == 0x02 ? true : false;
        }
    };

    class FastProcessingResponse
    {
        //////////// ISDU FAST PROCESSING Response /////////////
        // byte 0 : ISDU FAST PROCESSING state
        // byte 1 : reserved
        // byte 2 : reserved
        // byte 3 : reserved
        // byte 4 : reserved
        // byte 5 : reserved
        // byte 6 - 7 : Index (MSB) - if response available
        // byte 8 : SubIndex - if response available
        // byte 9 : Od data length -  if response available
        // byte 10-(10+Od data length - 1) : Od data - if response available

        #region Properties

        public readonly MST_FastProcessingStateT State;
        public readonly UInt16? Index;
        public readonly Byte? Subindex;
        public readonly Byte? Length;
        public readonly List<byte>? Value;

        #endregion Properties

        public FastProcessingResponse(List<byte> data)
        {
            data.RemoveAt(0);
            State = new MST_FastProcessingStateT(data[0]);
            if (State.IsIsduResponseReady)
            {
                if (data.Count < 10)
                {
                    throw new Exception($"[{data.Count}] {BitConverter.ToString(data.ToArray())}. Invalid data");
                }
                Index = BitConverter.ToUInt16(data.Skip(6).Take(2).Reverse().ToArray());
                Subindex = data[8];
                Length = data[9];
                Value = data.Skip(10).ToList();
                if (Value.Count == 0)
                {
                    throw new Exception($"Data is empty");
                }
            }
        }
    }

    public static class FastProcessingExtention
    {
        private enum MST_ISDU_FAST_PROCESSING_NextIsduTypeE
        {
            MST_ISDU_FAST_PROCESSING_NO_ISDU,
            MST_ISDU_FAST_PROCESSING_READ_ISDU,
            MST_ISDU_FAST_PROCESSING_WRITE_ISDU,
        };

        static public List<byte> ReadODSync(this ProtocolCommunicator protocolCommunicator, UInt16 index, byte subIndex)
        {
            //////////// ISDU FAST PROCESSING REQUEST /////////////
            // byte 0 : ISDU request type
            // byte 1 : reserved
            // byte 2 : reserved
            // byte 3 : reserved
            // byte 4 : reserved
            // byte 5 : reserved
            // byte 6 - 7 : Index (MSB)
            // byte 8 : SubIndex
            // byte 9 : Od data length
            // byte 10-(10+Od data length - 1) : Od data

            List<byte> send_data = new List<byte>();
            // ISDU request type
            send_data.Add((byte)(MST_ISDU_FAST_PROCESSING_NextIsduTypeE.MST_ISDU_FAST_PROCESSING_READ_ISDU));

            //reserved 5 bytes
            send_data.Add(0);
            send_data.Add(0);
            send_data.Add(0);
            send_data.Add(0);
            send_data.Add(0);

            send_data.Add((byte)(index & 0x00FF));        // add index low            
            send_data.Add((byte)((index >> 8) & 0x00FF)); // add index high
            send_data.Add(subIndex);                      // add subIndex
            FastProcessingResponse response = new (protocolCommunicator.SendRecvCommand(IQCommChannel.IOLink, IQCommand.FastProcessingIsdu, send_data));

            //clear structure to update 
            send_data = new List<byte>() { (byte)(MST_ISDU_FAST_PROCESSING_NextIsduTypeE.MST_ISDU_FAST_PROCESSING_NO_ISDU) };
            var counter = 0;
#if true //.NET 6 support required
            if (SpinWait.SpinUntil(() =>
            {
                counter++;
                // ISDU request type
                response = new(protocolCommunicator.SendRecvCommand(IQCommChannel.IOLink, IQCommand.FastProcessingIsdu, send_data));
                return response.State.IsIsduResponseReady == true;
            }, TimeSpan.FromSeconds(5)) == false)
            {
                throw new TimeoutException($"Reading {index} {subIndex} failed because of ISDU timeout (5 seconds). ");
            }
#else
			Stopwatch timer = new Stopwatch();
			timer.Start();
            while(response.State.IsIsduResponseReady == false)
            {
                response = new(protocolCommunicator.SendRecvCommand(IQCommChannel.IOLink, IQCommand.FastProcessingIsdu, send_data));
                if (timer.Elapsed.TotalSeconds > 5)
                {
                    throw new TimeoutException($"Reading {index} {subIndex} failed because of ISDU timeout (5 seconds). ");
                }
            }
#endif
            if (response.State.IsCachedIsduAvailable == true)
            {
                throw new InvalidOperationException("Not expected. ISDU from cache should be moved to transmit");
            }
            return response.Value!;
        }

        static public List<byte> WriteODSync(this ProtocolCommunicator protocolCommunicator, UInt16 index, byte subIndex, List<byte> isdu_data)
        {
            //////////// ISDU FAST PROCESSING REQUEST /////////////
            // byte 0 : ISDU request type
            // byte 1 : reserved
            // byte 2 : reserved
            // byte 3 : reserved
            // byte 4 : reserved
            // byte 5 : reserved
            // byte 6 - 7 : Index (MSB)
            // byte 8 : SubIndex
            // byte 9 : Od data length
            // byte 10-(10+Od data length - 1) : Od data

            List<byte> send_data = new List<byte>();
            // ISDU request type
            send_data.Add((byte)(MST_ISDU_FAST_PROCESSING_NextIsduTypeE.MST_ISDU_FAST_PROCESSING_WRITE_ISDU));

            //reserved 5 bytes
            send_data.Add(0);
            send_data.Add(0);
            send_data.Add(0);
            send_data.Add(0);
            send_data.Add(0);

            send_data.Add((byte)(index & 0x00FF));        // add index low            
            send_data.Add((byte)((index >> 8) & 0x00FF)); // add index high
            send_data.Add(subIndex);                      // add subIndex
            send_data.Add((byte)isdu_data.Count);               // add length
            send_data.AddRange(isdu_data);
            FastProcessingResponse response = new(protocolCommunicator.SendRecvCommand(
                                                    IQCommChannel.IOLink, 
                                                    IQCommand.FastProcessingIsdu, 
                                                    send_data));

            //create header for a request state only
            send_data = new List<byte>() 
            {
                (byte)(MST_ISDU_FAST_PROCESSING_NextIsduTypeE.MST_ISDU_FAST_PROCESSING_NO_ISDU) 
            };

            //wait until cach is free for next isdu request
            if (response.State.IsCachedIsduAvailable == true)
            {
                //wait for response
                var counter = 0;
#if true //.NET 6 support required
                if (SpinWait.SpinUntil(() =>
                {
                    counter++;
                    // ISDU request type
                    response = new(protocolCommunicator.SendRecvCommand(IQCommChannel.IOLink, IQCommand.FastProcessingIsdu, send_data));
                    return (response.State.IsCachedIsduAvailable == false);
                }, TimeSpan.FromSeconds(5)) == false)
                {
                    throw new TimeoutException($"Reading {index} {subIndex} failed because of ISDU timeout (5 seconds). ");
                }
#else
			    Stopwatch timer = new Stopwatch();
			    timer.Start();
                while (response.State.IsCachedIsduAvailable == true)
                {
                    response = new(protocolCommunicator.SendRecvCommand(IQCommChannel.IOLink, IQCommand.FastProcessingIsdu, send_data));
                    if (timer.Elapsed.TotalSeconds > 5)
                    {
                        throw new TimeoutException($"Reading {index} {subIndex} failed because of ISDU timeout (5 seconds). ");
                    }
                }
#endif
            }

            return response.Value!;
        }

        static public void SwitchCachedMode(this ProtocolCommunicator protocolCommunicator, MST_IsduCacheModeE mode)
        {
            if (mode == MST_IsduCacheModeE.MST_ISDU_CACHE_DISABLED)
            {
                //wait, until all ISDU is transmitted
                protocolCommunicator.WaitODRsp();
            }
            protocolCommunicator.writeIQCommPar(IQPar.IQParSetIsduCashedMode, new List<byte>() { (byte)mode, 0 });
        }
    }
}
