using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.IO;
using skyscraper5.Skyscraper.Plugins;
using skyscraper5.T2MI.Packets.AdressingFunctions;
using skyscraper8.T2MI.Packets.AdressingFunctions;

namespace skyscraper5.T2MI.Packets
{
	[SkyscraperPlugin]
	[T2MiPacketType(0x21)]
    public class _0x21_IndividualAddressing : T2MiPacket
    {
        public _0x21_IndividualAddressing(T2MIHeader header, byte[] buffer) : base(header, buffer)
        {
            if (buffer.Length < 2)
            {
                Valid = false;
                return;
            }
            bool validated = false;

            MemoryStream ms = new MemoryStream(buffer, true);
            RFU = ms.ReadUInt8();
           byte individualAddressingLength = ms.ReadUInt8();
            if (individualAddressingLength > ms.GetAvailableBytes())
            {
                Valid = false;
                return;
            }

            ms = new MemoryStream(ms.ReadBytes(individualAddressingLength), true);
            while (ms.GetAvailableBytes() > 3)
            {
                ushort txIdentifier = ms.ReadUInt16BE();
                byte functionLoopLength = ms.ReadUInt8();
                if (ms.GetAvailableBytes() < functionLoopLength)
                {
                    Valid = false;
                    return;
                }
                MemoryStream ms2 = new MemoryStream(ms.ReadBytes(functionLoopLength), true);
                while (ms2.GetAvailableBytes() > 2)
                {
                    AddressingFunction function = DecodeAddressingFunction(ms2);
                    if (function == null)
                    {
                        Valid = false;
                        return;
                    }

                    if (Commands == null)
                        Commands = new List<AddressingFunction>();

                    function.TxIdentifier = txIdentifier;
                    Commands.Add(function);
                    Valid = true;
                    validated = true;
                }
            }

            if (!validated)
                Valid = false;
        }

        public List<AddressingFunction> Commands { get; set; }

        public byte RFU { get; private set; }

        private AddressingFunction DecodeAddressingFunction(MemoryStream ms)
        {
            byte functionTag = ms.ReadUInt8();
            byte functionLength = ms.ReadUInt8();
            functionLength -= 2;
            if (functionLength > ms.GetAvailableBytes())
                return null;
            byte[] functionBody = ms.ReadBytes(functionLength);

            AddressingFunction result = null;
            switch (functionTag)
            {
                case 0x00: result = new _0x00_TransmitterTimeOffset(functionBody); break;
                case 0x01: throw new NotImplementedException("Transmitter frequency offset");
                case 0x02: result = new _0x02_TransmitterPower(functionBody); break;
                case 0x03: throw new NotImplementedException("Private data");
                case 0x04: result = new _0x04_CellId(functionBody); break;
                case 0x05: throw new NotImplementedException("Enable");
                case 0x06: throw new NotImplementedException("Bandwidth");
                case 0x10: throw new NotImplementedException("ACE-PAPR");
                case 0x11: result = new _0x11_TransmitterMisoGroup(functionBody); break;
                case 0x12: result = new _0x12_TrPapr(functionBody); break;
                case 0x13: result =  new _0x13_L1AcePapr(functionBody); break;
                case 0x15: throw new NotImplementedException("TX-SIG FEF: Sequence Numbers");
                case 0x16: throw new NotImplementedException("TX-SIG Aux stream: Transmitter ID");
                case 0x17: result = new _0x17_Frequency(functionBody); break;
            }

            if (result == null)
                return null;

            if (!result.Valid)
                result = null;

            return result;
        }
    }
}
