using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using skyscraper5.Skyscraper.IO;

namespace skyscraper5.Docsis.AnnexC
{
    public class GeneralPacketClassifierEncoding
    {
        public GeneralPacketClassifierEncoding(byte[] buffer)
        {
            MemoryStream ms = new MemoryStream(buffer, false);

            while (ms.GetAvailableBytes() > 2)
            {
                byte type = ms.ReadUInt8();
                byte length = ms.ReadUInt8();
                byte[] v = ms.ReadBytes(length);
                switch (type)
                {
                    case 1:
                        ClassifierReference = v[0];
                        break;
                    case 2:
                        (v[0], v[1]) = (v[1], v[0]);
                        ClassifierIdentifier = BitConverter.ToUInt16(v, 0);
                        break;
                    case 3:
                        (v[0], v[1]) = (v[1], v[0]);
                        ServiceFlowReference = BitConverter.ToUInt16(v, 0);
                        break;
                    case 4:
                        (v[0], v[1], v[2], v[3]) = (v[3], v[2], v[1], v[0]);
                        ServiceFlowIdentifier = BitConverter.ToUInt32(v, 0);
                        break;
                    case 5:
                        RulePriority = v[0];
                        break;
                    case 6:
                        ClassifierActivationState = v[0] != 0;
                        break;
                    case 7:
                        DynamicServiceChangeAction = (DynamicServiceChangeActionEnum)v[0];
                        break;
                    case 9:
                        Ipv4PacketClassification = new Ipv4PacketClassificationEncodings(v);
                        break;
                    case 10:
                        EthernetLlcPacketClassificationEncodings = new EthernetLlcPacketClassificationEncoding(v);
                        break;
                    case 12:
                        Ipv6PacketClassification = new Ipv6PacketClassificationEncoding(v);
                        break;
					default:
                        //see CM-SP-MULPIv4.0-I01-190815.pdf page 742->743
                        throw new NotImplementedException(string.Format("{0} TLV Type {1}", nameof(GeneralPacketClassifierEncoding), type));
                }
            }
        }

        public DynamicServiceChangeActionEnum? DynamicServiceChangeAction { get; private set; }
        public enum DynamicServiceChangeActionEnum : byte
        {
            Add,
            Replace,
            Delete
        }
        public bool? ClassifierActivationState { get; set; }

        public byte? RulePriority { get; set; }

        public Ipv4PacketClassificationEncodings Ipv4PacketClassification { get; private set; }
        public class Ipv4PacketClassificationEncodings
        {
            public Ipv4PacketClassificationEncodings(byte[] buffer)
            {
                MemoryStream ms = new MemoryStream(buffer, false);
                while (ms.GetAvailableBytes() > 2)
                {
                    byte type = ms.ReadUInt8();
                    byte length = ms.ReadUInt8();
                    byte[] v = ms.ReadBytes(length);
                    switch (type)
                    {
                        case 2:
                            (v[0], v[1]) = (v[1], v[0]);
                            IpProtocol = BitConverter.ToUInt16(v, 0);
                            break;
                        case 3:
                            Ipv4SourceAddress = new IPAddress(v);
                            break;
                        case 4:
                            Ipv4SourceMask = new IPAddress(v);
                            break;
                        case 5:
                            Ipv4DestinationAddress = new IPAddress(v);
                            break;
                        case 6:
							Ipv4DestinationMask = new IPAddress(v);
                            break;
						case 7:
                            (v[0], v[1]) = (v[1], v[0]);
                            SourcePortStart = BitConverter.ToUInt16(v, 0);
                            break;
                        case 8:
                            (v[0], v[1]) = (v[1], v[0]);
                            SourcePortEnd = BitConverter.ToUInt16(v, 0);
                            break;
                        case 9:
                            (v[0], v[1]) = (v[1], v[0]);
                            DestinationPortStart = BitConverter.ToUInt16(v, 0);
                            break;
                        case 10:
                            (v[0], v[1]) = (v[1], v[0]);
                            DestinationPortEnd = BitConverter.ToUInt16(v, 0);
                            break;
                        default:
                            //see CM-SP-MULPIv4.0-I01-190815.pdf page 746->747
                            throw new NotImplementedException(string.Format("{0} TLV Type {1}", nameof(Ipv4PacketClassificationEncodings), type));
                    }
                }
            }

            public ushort? DestinationPortStart { get; set; }

            public IPAddress Ipv4SourceAddress { get; set; }

            public IPAddress Ipv4DestinationAddress { get; set; }

            public ushort? DestinationPortEnd { get; set; }

            public ushort? IpProtocol { get; set; }

            public ushort? SourcePortStart { get; set; }

            public ushort? SourcePortEnd { get; set; }
			public IPAddress Ipv4SourceMask { get; private set; }
			public IPAddress Ipv4DestinationMask { get; private set; }
		}
        public ushort? ServiceFlowReference { get; set; }

        public byte? ClassifierReference { get; set; }

        public ushort? ClassifierIdentifier { get; set; }

        public uint? ServiceFlowIdentifier { get; set; }
		public EthernetLlcPacketClassificationEncoding EthernetLlcPacketClassificationEncodings { get; }
		public Ipv6PacketClassificationEncoding Ipv6PacketClassification { get; }
	}
}
