using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Numerics;
using System.Runtime.Intrinsics.X86;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Docsis.AnnexC;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.IO;
using skyscraper5.Skyscraper.Plugins;

namespace skyscraper5.Docsis.MacManagement
{
	[SkyscraperPlugin]
	[MacManagementMessageType(1,5)]
    [MacManagementMessageType(5,5)]
    public class RangingResponse : MacManagementMessage
    {
        public RangingResponse(PhysicalAddress source, PhysicalAddress destination, byte[] buffer) : base(source, destination, buffer)
        {
            MemoryStream ms = new MemoryStream(buffer, false);
            SID = ms.ReadUInt16BE();
            UpstreamChannelId = ms.ReadUInt8();
            long positionFor1516;

            while (ms.GetAvailableBytes() > 2)
            {
                positionFor1516 = ms.Position;
                byte type = ms.ReadUInt8();
                byte length = ms.ReadUInt8();
                if (length > ms.GetAvailableBytes())
                {
                    Valid = false;
                    return;
                }
                if (length == 0 && type != 15 && type != 16)
                {
                    Valid = false;
                    return;
                }
                byte[] v = ms.ReadBytes(length);
                switch (type)
                {
                    case 0:
                        //reserved
                        break;
                    case 1:
                        if (length < 4)
                        {
                            Valid = false;
                            return;
                        }
                        (v[0], v[1], v[2], v[3]) = (v[3], v[2], v[1], v[0]);
                        TimingAdjust = BitConverter.ToUInt32(v, 0);
                        break;
                    case 2:
                        PowerLevelAdjust = v[0];
                        break;
                    case 3:
						if (length < 2)
						{
							Valid = false;
							return;
						}
						(v[0], v[1]) = (v[1], v[0]);
                        OffsetFrequencyAdjust = BitConverter.ToUInt16(v, 0);
                        break;
                    case 4:
                        TxEqualizationData = v;
                        break;
                    case 5:
                        RangingStatus = (RangingStatusEnum)v[0];
                        break;
                    case 6:
						if (length < 4)
						{
							Valid = false;
							return;
						}
						(v[0], v[1], v[2], v[3]) = (v[3], v[2], v[1], v[0]);
						DownstreamFrequencyOverride = BitConverter.ToUInt32(v, 0);
						break;
                    case 7:
						UpstreamChannelIdOverride = v[0];
                        break;
                    case 8:
                        TimingAdjustFractionalPart = v[0];
                        break;
					case 9:
                        TransmitEqualizationSet = v;
                        break;
                    case 10:
						SCdmaMaximumScheduledCodes = v[0];
                        break;
                    case 11:
                        SCdmaPowerHeadroom = v[0];
                        break;
                    case 12:
                        UpstreamChannelAdjustments = new UpstreamChannelAdjustmentsObject(v);
                        break;
                    case 13:
                        T4TimeoutMultiplier = v[0];
                        break;
                    case 14:
                        DynamicRangeWindowUpperEdge = ((double)v[0]) * 0.25;
                        break;
                    case 15:
					case 16:
						ms.Position = positionFor1516;
                        byte type2 = ms.ReadUInt8();
                        ushort newLen = ms.ReadUInt16BE();
                        if (ms.GetAvailableBytes() < newLen)
                        {
                            newLen = (ushort)ms.GetAvailableBytes();
                        }
						v = ms.ReadBytes(newLen);
                        if (type == 15)
                            TransmitEqualizationAdjustForOfdmaChannels = Parse1516(v);
                        else
                        {
                            Debug.Assert(type == 16);
                            TransmitEqualizationSetForOfdmaChannels = Parse1516(v);
                        }
                        break;
					case 17:
                        CommandPower = new CommandPowerObject(v);
                        if (!CommandPower.Valid)
                        {
                            CommandPower = null;
                            Valid = false;
                            return;
                        }
                        break;
                    case 18:
                        FdxCommandedPower = new CommandPowerObject(v);
						if (!FdxCommandedPower.Valid)
						{
							FdxCommandedPower = null;
							Valid = false;
							return;
						}
						break;
					default:
                        //see CM-SP-MULPIv4.0-I01-190815.pdf page 141
                        throw new NotImplementedException(String.Format("{0} Type {1}", nameof(RangingResponse), type));
                }
            }
            Valid = true;
        }

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

        private TransmitEqualizationEncodingsForOfdmaChannels Parse1516(byte[] buffer)
        {
            MemoryStream ms = new MemoryStream(buffer);
            byte l = ms.ReadUInt8();
			byte m = ms.ReadUInt8();
			byte r = ms.ReadUInt8();
            TransmitEqualizationEncodingsForOfdmaChannels result = new TransmitEqualizationEncodingsForOfdmaChannels();
            result.LowestSubcarrier = (l << 4);
            result.LowestSubcarrier += ((m & 0xf0) >> 4);
            result.HighestSubcarrier = (m & 0x0f);
            result.HighestSubcarrier <<= 4;
            result.HighestSubcarrier += r;

            long numCoefficents = ms.GetAvailableBytes() / 4;
            result.Coefficients = new Complex[numCoefficents];
            for (int i = 0; i < numCoefficents; i++)
            {
                ushort real = ms.ReadUInt16BE();
                ushort imaginary = ms.ReadUInt16BE();
                result.Coefficients[i] = new Complex(real, imaginary);
            }
            return result;
		}

        public TransmitEqualizationEncodingsForOfdmaChannels TransmitEqualizationAdjustForOfdmaChannels { get; private set; }
        public CommandPowerObject CommandPower { get; set; }
        public class CommandPowerObject : Validatable
        {
            public CommandPowerObject(byte[] buffer)
            {
                if (buffer.Length < 2)
                {
                    Valid = false;
                    return;
                }

                MemoryStream ms = new MemoryStream(buffer, false);
                while (ms.GetAvailableBytes() > 2)
                {
                    byte type = ms.ReadUInt8();
                    byte length = ms.ReadUInt8();
                    if (length > ms.GetAvailableBytes())
                    {
                        Valid = false;
                        return;
                    }
                    byte[] v = ms.ReadBytes(length);
                    switch (type)
                    {
                        case 1:
                            DynamicRangeWindow = v[0];
                            break;
                        case 2:
                            TransmitPowerLevels = v;
                            break;
                        default:
                            Valid = false;
                            return;
                    }
                }
                Valid = true;
            }

            public byte[] TransmitPowerLevels { get; set; }

            public byte? DynamicRangeWindow { get; set; }
        }

        public byte? T4TimeoutMultiplier { get; set; }

        public byte[] TxEqualizationData { get; set; }

        public byte? PowerLevelAdjust { get; set; }

        public RangingStatusEnum? RangingStatus { get; private set; }

        public enum RangingStatusEnum : byte
        {
            Continue = 1,
            Abort = 2,
            Success = 3
        }

        public ushort? OffsetFrequencyAdjust { get; set; }

        public uint? TimingAdjust { get; set; }

        public ushort? UpstreamChannelId { get; set; }

        public ushort? SID { get; set; }
		public TransmitEqualizationEncodingsForOfdmaChannels TransmitEqualizationSetForOfdmaChannels { get; private set; }
		public double? DynamicRangeWindowUpperEdge { get; private set; }
		public uint? DownstreamFrequencyOverride { get; }
		public byte? UpstreamChannelIdOverride { get; private set; }
		public byte? SCdmaPowerHeadroom { get; }
		public UpstreamChannelAdjustmentsObject UpstreamChannelAdjustments { get; private set; }
		public CommandPowerObject FdxCommandedPower { get; }
		public byte? TimingAdjustFractionalPart { get; }
		public byte? SCdmaMaximumScheduledCodes { get; }

		public class UpstreamChannelAdjustmentsObject : Validatable
        {
            public UpstreamChannelAdjustmentsObject(byte[] buffer)
            {
                MemoryStream ms = new MemoryStream(buffer, false);

                while (ms.GetAvailableBytes() > 2)
                {
                    byte type = ms.ReadUInt8();
                    byte length = ms.ReadUInt8();
                    if (length > ms.GetAvailableBytes())
                    {
                        Valid = false;
                        return;
                    }
                    byte[] v = ms.ReadBytes(length);
                    switch (type)
                    {
                        default:
                            if (type > 6)
                            {
                                Valid = false;
                                return;
                            }
							//See CM-SP-MULPIv4.0-I01-190815.pdf page 142
							throw new NotImplementedException(String.Format("{0} TLV Type {1}", nameof(UpstreamChannelAdjustmentsObject), type));
					}
                }
                Valid = true;
            }
        }

		public class TransmitEqualizationEncodingsForOfdmaChannels
		{
			public int LowestSubcarrier { get; internal set; }
			public int HighestSubcarrier { get; internal set; }
			public Complex[] Coefficients { get; internal set; }
		}
	}
}
