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

namespace skyscraper5.Docsis.AnnexC
{
    public class ServiceFlowSidClusterAssignmentObject
    {
        public ServiceFlowSidClusterAssignmentObject(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:
                        (v[0], v[1], v[2], v[3]) = (v[3], v[2], v[1], v[0]);
                        SFID = BitConverter.ToUInt32(v, 0);
                        break;
                    case 2:
                        SidClusterEncoding = new SidClusterEncodingObject(v);
                        break;
                    case 3:
                        SidClusterSwitchoverCriteria = new SidClusterSwitchoverCriteriaObject(v);
                        break;
                    default:
                        //See CM-SP-MULPIv4.0-I01-190815.pdf page 720->721
                        throw new NotImplementedException(String.Format("{0} TLV Type {1}", nameof(ServiceFlowSidClusterAssignmentObject), type));
                }
            }
        }

        public SidClusterSwitchoverCriteriaObject SidClusterSwitchoverCriteria { get; private set; }
        public class SidClusterSwitchoverCriteriaObject
        {
            public SidClusterSwitchoverCriteriaObject(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:
                            MaximumRequestPerSidCluster = v[0];
                            break;
                        case 2:
                            (v[0], v[1], v[2], v[3]) = (v[3], v[2], v[1], v[0]);
                            MaximumOutstandingBytesPerSidCluster = BitConverter.ToUInt32(v, 0);
                            break;
                        case 3:
                            (v[0], v[1], v[2], v[3]) = (v[3], v[2], v[1], v[0]);
                            MaximumTotalBytesRequestedPerSidCluster = BitConverter.ToUInt32(v, 0);
                            break;
                        case 4:
                            (v[0], v[1]) = (v[1], v[0]);
                            MaximumTimeInTheSidCluster = BitConverter.ToUInt16(v, 0);
                            break;
                        default:
                            //See CM-SP-MULPIv4.0-I01-190815.pdf page 722
                            throw new NotImplementedException(String.Format("{0} TLV Type {1}", nameof(SidClusterSwitchoverCriteriaObject), type));
                    }
                }
            }

            public ushort? MaximumTimeInTheSidCluster { get; set; }

            public uint? MaximumTotalBytesRequestedPerSidCluster { get; set; }

            public uint? MaximumOutstandingBytesPerSidCluster { get; set; }

            public byte? MaximumRequestPerSidCluster { get; set; }
        }
        public SidClusterEncodingObject SidClusterEncoding { get; private set; }
        public class SidClusterEncodingObject
        {
            public SidClusterEncodingObject(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:
                            SidClusterId = v[0];
                            break;
                        case 2:
                            SidToChannelMapping = new SidToChannelMappingObject(v);
                            break;
                        // CM-SP-MULPIv4.0-I01-190815.pdf page 721
                        default:
                            throw new NotImplementedException(String.Format("{0} TLV Type {1}", nameof(SidClusterEncodingObject), type));
                    }
                }
            }

            public SidToChannelMappingObject SidToChannelMapping { get; private set; }
            public class SidToChannelMappingObject
            {
                public SidToChannelMappingObject(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:
                                UpstreamChannelId = v[0];
                                break;
                            case 2:
                                (v[0], v[1]) = (v[1], v[0]);
                                SID = BitConverter.ToUInt16(v, 0);
                                break;
                            case 3:
                                Action = (ActionEnum)v[0];
                                break;
                            default:
                                // CM-SP-MULPIv4.0-I01-190815.pdf page 721
                                throw new NotImplementedException(String.Format("{0} TLV Type {1}", nameof(SidToChannelMappingObject), type));
                        }
                    }
                }

                public ActionEnum? Action { get; private set; }
                public enum ActionEnum : byte
                {
                    Add = 1,
                    Delete = 2
                }

                public ushort? SID { get; set; }

                public byte? UpstreamChannelId { get; set; }
            }
            public byte? SidClusterId { get; set; }
        }
        public uint? SFID { get; set; }
    }
}
