using System;
using System.IO;
using System.Runtime.Intrinsics.Arm;
using skyscraper5.Skyscraper;
using skyscraper5.Skyscraper.IO;
using skyscraper8.Docsis.AnnexC;
using skyscraper8.Docsis.L2VPN;

namespace skyscraper5.Docsis.AnnexC
{
    public class ModemCapabilitiesEncoding : Validatable
    {
		private readonly bool? ExpandedUnicastSidSpace;
		private readonly byte? DpvCapability;
		private readonly byte? EnergyManagementPreference;
		private byte? UpstreamScQamSymbolRateSupport;
		private byte? _512MspsUpstreamTransmitScQamChannelSupport;
		private byte? _256MspsUpstreamTransmitScQamChannelSupport;

		public ModemCapabilitiesEncoding(byte[] buffer)
        {
            MemoryStream ms = new MemoryStream(buffer, true);
            while (ms.GetAvailableBytes() > 2)
            {
                byte type = ms.ReadUInt8();
                byte len = ms.ReadUInt8();
                byte[] v = ms.ReadBytes(len);
                switch (type)
                {
                    case 1:
                        ConcatenationSupport = v[0] != 0 ? true : false;
                        break;
                    case 2:
                        DocsisVersion = DecodeVersion(v[0]);
                        break;
                    case 3:
                        FragmentationSupport = ((v[0] & 0x01) != 0);
                        break;
                    case 4:
                        PayloadHeaderSuppressionSupport = ((v[0] & 0x01) != 0);
                        break;
                    case 6:
                        PrivacySupport = (PrivacySupportValue)v[0];
                        break;
                    case 7:
                        DownstreamSaidSupport = (byte?)v[0];
                        break;
                    case 8:
                        UpstreamServiceFlowSupport = (byte?)v[0];
                        break;
                    case 10:
                        TransmitPreEqualizerTapsPerModulationInterval = (byte?)v[0];
                        break;
                    case 11:
                        NumberOfTransmitEqualizerTaps = (byte?)v[0];
                        break;
                    case 12:
                        DccSupport = (bool?)((v[0] & 0x01) == 1);
                        break;
                    case 15:
						ExpandedUnicastSidSpace = (bool?)((v[0] & 0x01) == 1);
                        break;
                    case 16:
                        this.RangingHoldOffSupport = new RangingHoldOffSupport(v[0], v[1], v[2], v[3]);
                        break;
                    case 17:
						//This is guesswork, I don't have "CM-SP-L2VPN-I15-150528"
						L2VPN = (bool?)((v[0] & 0x01) == 1);
                        break;
                    case 18:
                        this.ESafeHostCapability = new ESafeHostCapability(v);
                        break;
                    case 19:
                        this.DownstreamUnencryptedTrafficFiltering = (bool?)((v[0] & 0x01) == 1);
                        break;
                    case 20:
                        UpstreamFrequencyRangeSupport = (UpstreamFrequencyRangeSupportEnum)v[0];
                        break;
                    case 21:
                        UpstreamScQamSymbolRateSupport = v[0];
                        break;
                    case 22:
                        SelectableActiveCodeMode2Support = v[0] == 1;
                        break;
                    case 23:
                        CodeHoppingMode2Support = (bool?)(v[0] == 1);
                        break;
                    case 24:
                        ScQamMultipleTransmitChannelSupport = (byte?)v[0];
                        break;
                    case 25:
                        _512MspsUpstreamTransmitScQamChannelSupport = (byte?)v[0];
                        break;
                    case 26:
                        _256MspsUpstreamTransmitScQamChannelSupport = (byte?)v[0];
                        break;
                    case 27:
                        TotalSidClusterSupport = (byte?)v[0];
                        break;
                    case 28:
                        SidClustersPerServiceFlowSupport = (byte?)v[0];
                        break;
                    case 29:
                        ScQamMultipleReceiveChannelSupport = (byte?)v[0];
                        break;
                    case 30:
                        TotalDsidSupport = (byte?)v[0];
                        break;
                    case 31:
						ResequencingDsidSupport = (byte?)v[0];
                        break;
                    case 32:
                        MulticastDsidSupport = (byte?)v[0];
                        break;
                    case 33:
                        MulticastDsidForwarding = (MulticastDsidForwardingEnum?)v[0];
                        break;
                    case 34:
                        FrameControlTypeForwardingCapability = (bool?)(v[0] == 1);
                        break;
                    case 35:
                        DpvCapability = (byte?)v[0];
                        break;
                    case 36:
                        UnsolicitedGrantServiceFlowSupport = (byte?)v[0];
                        break;
                    case 37:
                        MapAndUcdReceiptSupport = (bool?)((v[0] & 0x01) != 1);
                        break;
                    case 38:
                        (v[0], v[1]) = (v[1], v[0]);
                        UpstreamDropClassifierSupport = BitConverter.ToUInt16(v, 0);
                        break;
                    case 39:
                        Ipv6Support = (bool?)(v[0] == 1);
                        break;
                    case 40:
                        ExtendedUpstreamTransmitPowerCapability = (byte?)v[0];
                        break;
                    case 44:
                        //Additions and Modifications for Chinese Specification
                        //I don't have this document.
                        break;
                    case 46:
                        CmStatusAck = (bool?)(v[0] == 1);
                        break;
                    case 47:
                        EnergyManagementPreference = v[3];
                        break;
                    case 48:
						(v[0], v[1]) = (v[1], v[0]);
                        ExtendedPacketLengthSupportCapability = BitConverter.ToUInt16(v, 0);
                        break;
                    case 49:
                        OfdmMultipleReceiveChannelSupport = (byte?)v[0];
                        break;
                    case 50:
                        OfdmaMultipleTransmitChannelSupport = (byte?)v[0];
                        break;
                    case 51:
                        DownstreamOfdmProfileSupport = (byte?)v[0];
                        break;
                    case 52:
                        this.DownstreamOfdmChannelSubcarrierQamModulationSupport = new ChannelSubcarrierQamModulationSupport(v[0], v[1]);
                        break;
                    case 53:
                        this.UpstreamOfdmaChannelSubcarrierQamModulationSupport = new ChannelSubcarrierQamModulationSupport(v[0], v[1]);
                        break;
                    case 54:
                        if (v[0] == 0x01)
                            DownstreamFrequencyRangeStarting = 108;
                        else if (v[0] == 0x02)
							DownstreamFrequencyRangeStarting = 258;
						break;
                    case 55:
                        if (v[0] == 0x01)
                            DownstreamFrequencyRangeUpTo = 1218;
                        else if (v[0] == 0x02)
                            DownstreamFrequencyRangeUpTo = 1794;
                        else if (v[0] == 0x04)
                            DownstreamFrequencyRangeUpTo = 1002;
                        break;
                    case 56:
                        if (v[0] == 0)
                            UpstreamFrequencyRangeUpTo = 42;
                        else if (v[0] == 1)
                            UpstreamFrequencyRangeUpTo = 65;
						else if (v[0] == 2)
							UpstreamFrequencyRangeUpTo = 85;
						else if (v[0] == 3)
							UpstreamFrequencyRangeUpTo = 117;
						else if (v[0] == 4)
							UpstreamFrequencyRangeUpTo = 204;
                        break;
                    case 57:
                        DocsisTimeProtocolMode = (DtpMode?)v[0];
                        break;
                    case 58:
                        DocsisTimeProtocolPerformanceSupport = (DtpPerformanceSupport?)v[0];
                        break;
                    case 59:
						(v[0], v[1]) = (v[1], v[0]);
                        Pmax = BitConverter.ToUInt16(v, 0);
                        break;
                    case 60:
                        if (v[0] == 0x01)
                            DownstreamFrequencyRangeStarting = 108;
                        else if (v[0] == 0x02)
                            DownstreamFrequencyRangeStarting = 258;
                        else if (v[0] == 0x03)
                            DownstreamFullDuplex = true;
                        break;
                    case 61:
						if (v[0] == 0x01)
							DownstreamFrequencyRangeUpTo = 1218;
						else if (v[0] == 0x02)
							DownstreamFrequencyRangeUpTo = 1794;
						else if (v[0] == 0x04)
							DownstreamFrequencyRangeUpTo = 1002;
						break;
                    case 62:
						if (v[0] == 0)
							UpstreamFrequencyRangeUpTo = 42;
						else if (v[0] == 1)
							UpstreamFrequencyRangeUpTo = 65;
						else if (v[0] == 2)
							UpstreamFrequencyRangeUpTo = 85;
						else if (v[0] == 3)
							UpstreamFrequencyRangeUpTo = 117;
						else if (v[0] == 4)
							UpstreamFrequencyRangeUpTo = 204;
						break;
                    case 76:
                        LowLatencySupport = (byte?)v[0];
                        break;
					default:
                        //CM-SP-MULPIv4.0-I01-190815.pdf, page 688
                        throw new NotImplementedException(String.Format("{0} {1}", nameof(ModemCapabilitiesEncoding), type));
                }
            }
            Valid = true;
        }

        public Version DocsisVersion { get; private set; }

        private Version DecodeVersion(byte value)
        {
            //see CM-SP-MULPIv4.0-I01-190815.pdf, page 689
            switch (value)
            {
                case 0: return new Version(1, 0);
                case 1: return new Version(1, 1);
                case 2: return new Version(2, 0);
                case 3: return new Version(3, 0);
                case 4: return new Version(3, 1);
                case 5: return new Version(4, 0);
                default:
                    return null;
            }
        }

        public bool? ConcatenationSupport { get; private set; }
		public bool? FragmentationSupport { get; }
		public bool? PayloadHeaderSuppressionSupport { get; }
		public PrivacySupportValue? PrivacySupport { get; }
		public byte? DownstreamSaidSupport { get; }
		public byte? UpstreamServiceFlowSupport { get; }
		public byte? TransmitPreEqualizerTapsPerModulationInterval { get; }
		public byte? NumberOfTransmitEqualizerTaps { get; }
		public bool? DccSupport { get; }
		public RangingHoldOffSupport RangingHoldOffSupport { get; }
		public bool? L2VPN { get; }
		public ESafeHostCapability ESafeHostCapability { get; }
		public bool? DownstreamUnencryptedTrafficFiltering { get; }
		public UpstreamFrequencyRangeSupportEnum? UpstreamFrequencyRangeSupport { get; }

		public enum PrivacySupportValue : byte
        {
            BpiSupport = 0,
            BpiPlusSupport = 1
        }

        public enum UpstreamFrequencyRangeSupportEnum : byte
        {
            Standard = 0,
            Upstream = 1,
            Extended = 2
        }

        public bool _160kspsSymbolRateSupport
        {
            get
            {
                if (!UpstreamScQamSymbolRateSupport.HasValue)
                    return true;
                return (UpstreamScQamSymbolRateSupport & 0x01) != 0;
            }
        }

		public bool _320kspsSymbolRateSupport
		{
			get
			{
				if (!UpstreamScQamSymbolRateSupport.HasValue)
					return true;
				return (UpstreamScQamSymbolRateSupport & 0x02) != 0;
			}
		}

		public bool _640kspsSymbolRateSupport
		{
			get
			{
				if (!UpstreamScQamSymbolRateSupport.HasValue)
					return true;
				return (UpstreamScQamSymbolRateSupport & 0x04) != 0;
			}
		}

		public bool _1280kspsSymbolRateSupport
		{
			get
			{
				if (!UpstreamScQamSymbolRateSupport.HasValue)
					return true;
				return (UpstreamScQamSymbolRateSupport & 0x08) != 0;
			}
		}

		public bool _2560kspsSymbolRateSupport
		{
			get
			{
				if (!UpstreamScQamSymbolRateSupport.HasValue)
					return true;
				return (UpstreamScQamSymbolRateSupport & 0x10) != 0;
			}
		}

		public bool _5120kspsSymbolRateSupport
		{
			get
			{
				if (!UpstreamScQamSymbolRateSupport.HasValue)
					return true;
				return (UpstreamScQamSymbolRateSupport & 0x20) != 0;
			}
		}

		public bool? SelectableActiveCodeMode2Support { get; }
		public bool? CodeHoppingMode2Support { get; }
		public byte? ScQamMultipleTransmitChannelSupport { get; }
		public byte? TotalSidClusterSupport { get; }
		public byte? SidClustersPerServiceFlowSupport { get; }
		public byte? ScQamMultipleReceiveChannelSupport { get; }
		public byte? TotalDsidSupport { get; }
		public byte? ResequencingDsidSupport { get; private set; }
		public byte? MulticastDsidSupport { get; }
		public MulticastDsidForwardingEnum? MulticastDsidForwarding { get; }
		public bool? FrameControlTypeForwardingCapability { get; }

		public enum MulticastDsidForwardingEnum : byte
        {
            NoSupport,
            SupportExplicit,
            SupportPromiscious
        }

        public bool? DpvPerPathSuppported
        {
            get
            {
                if (DpvCapability == null)
                    return null;
                return (DpvCapability & 0x01) != 0;
			}
        }

		public bool? DpvPerPacketSuppported
		{
			get
			{
				if (DpvCapability == null)
					return null;
				return (DpvCapability & 0x02) != 0;
			}
		}

		public byte? UnsolicitedGrantServiceFlowSupport { get; }
		public bool? MapAndUcdReceiptSupport { get; }
		public ushort UpstreamDropClassifierSupport { get; }
		public bool? Ipv6Support { get; }
		public byte? ExtendedUpstreamTransmitPowerCapability { get; }
		public bool? CmStatusAck { get; }

        public bool? EnergyManagement1x1Feature
        {
            get
            {
                if (EnergyManagementPreference == null)
                    return null;
                return (EnergyManagementPreference & 0x01) != 0;
            }
        }

        public bool? DocsisLightSleepMode
        {
            get
            {
				if (EnergyManagementPreference == null)
					return null;
				return (EnergyManagementPreference & 0x02) != 0;
			}
        }

		public ushort ExtendedPacketLengthSupportCapability { get; }
		public byte? OfdmMultipleReceiveChannelSupport { get; }
		public byte? OfdmaMultipleTransmitChannelSupport { get; }
		public byte? DownstreamOfdmProfileSupport { get; }
		public ChannelSubcarrierQamModulationSupport DownstreamOfdmChannelSubcarrierQamModulationSupport { get; }
		public ChannelSubcarrierQamModulationSupport UpstreamOfdmaChannelSubcarrierQamModulationSupport { get; }
		public int DownstreamFrequencyRangeStarting { get; }
		public int DownstreamFrequencyRangeUpTo { get; }
		public int UpstreamFrequencyRangeUpTo { get; }
		public DtpMode? DocsisTimeProtocolMode { get; }
		public DtpPerformanceSupport? DocsisTimeProtocolPerformanceSupport { get; }
		public ushort Pmax { get; }
		public bool DownstreamFullDuplex { get; }
		public byte? LowLatencySupport { get; }

		public enum DtpMode : byte
        {
            NotSupported = 0,
            SlaveOnly = 1,
            MasterOnly = 2,
            MasterOrSlave = 3
        }

        public enum DtpPerformanceSupport : byte
        {
            NotSupported = 0,
            Level1 = 1,
            Level2 = 2,
            Level3 = 3,
            Level4 = 4,
            Level5 = 5,
            NoSpecifiedPerformance = 6,
        }
    }
}
