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

namespace skyscraper5.Docsis
{
    internal class MacExtendedHeader
    {
        public MacExtendedHeader(byte[] ehdr)
        {
            MemoryStream ms = new MemoryStream(ehdr, false);
            while (ms.GetAvailableBytes() > 0)
            {
                byte readUInt8 = ms.ReadUInt8();
                int EH_TYPE = readUInt8 & 0xf0 >> 4;
                int EH_LEN = readUInt8 & 0x0f;
                if (ms.GetAvailableBytes() < EH_LEN)
                    break;
                byte[] tmpBuffer = ms.ReadBytes(EH_LEN);
                switch (EH_TYPE)
                {
                    case 0: //Null configuration settings.
                        break;
                    case 1:
                        if (tmpBuffer.Length == 1)
                        {
                            MinislotsRequested = tmpBuffer[0];
                        }
                        else if (tmpBuffer.Length == 2)
                        {
                            (tmpBuffer[0], tmpBuffer[1]) = (tmpBuffer[1], tmpBuffer[0]);
                            SID = BitConverter.ToUInt16(tmpBuffer, 0);
                        }
                        break;
                    case 2:     //Deprecated in DOCSIS 3.1
                                //Acknowledgement Requested
                        (tmpBuffer[0], tmpBuffer[1]) = (tmpBuffer[1], tmpBuffer[0]);
                        SID = BitConverter.ToUInt16(tmpBuffer, 0);
                        break;
                    case 3:
                        UpstreamPrivacyEhElement = new UpstreamPrivacyElement(tmpBuffer);
                        break;
                    case 4: //Downstream Privacy EH Element
                        DownstreamPrivacyEhElement = new DownstreamPrivacyElement(tmpBuffer);
                        break;
                    case 5: //Payload Header Suppression Header
                        PayloadHeaderSupression = tmpBuffer[0];
                        break;
                    case 6:     //Service Flow EH Element
                        if (EH_LEN == 2)
                        {
                            UnsolicitedGrantSynchronizationHeader = tmpBuffer[1];
                        }
                        break;
                    case 7:
                        UpstreamPrivacyEhElement = new UpstreamPrivacyElement(tmpBuffer);
                        break;
                    case 8:
                        if (EH_LEN == 1)
                        {
                            //CM-SP-MULPIv4.0-I01-190815.pdf, page 98
                            throw new NotImplementedException();
                        }
                        else if (EH_LEN == 3)
                        {
                            //CM-SP-MULPIv4.0-I01-190815.pdf, page 98
                            throw new NotImplementedException();
                        }
                        else if (EH_LEN == 5)
                        {
                            //CM-SP-MULPIv4.0-I01-190815.pdf, page 99
                            throw new NotImplementedException();
                        }

                        break;
                    case 9:     //DOCSIS Path Verify EH Element
                        DpvExtendedHeader = new DpvElement(tmpBuffer);
                        break;
                    case 10:    //reserved
                        break;
                    case 11:    //reserved
                        break;
                    case 12:    //reserved
                        break;
                    case 13:    //reserved
                        break;
                    case 14:    //reserved
                        break;
                    case 15://Extended EH Element
                            //seems undocumented?
                            //see CM-SP-MULPIv4.0-I01-190815-1.pdf, page 96
                            break;
                    default:
                        //see CM-SP-MULPIv4.0-I01-190815.pdf, page 96
                        throw new NotImplementedException(String.Format("{0} {1}", nameof(EH_TYPE), EH_TYPE));
                }
            }
        }

        public byte UnsolicitedGrantSynchronizationHeader { get; set; }

        public DpvElement DpvExtendedHeader { get; private set; }

        internal class DpvElement
        {
            public DpvElement(byte[] tmpBuffer)
            {
                MemoryStream ms = new MemoryStream(tmpBuffer, false);
                StartReferencePoint = ms.ReadUInt8();
                TimestampStart = ms.ReadUInt32BE();
            }

            public uint TimestampStart { get; set; }

            public byte StartReferencePoint { get; private set; }
        }

        public ushort? SID { get; set; }

        public byte? MinislotsRequested { get; set; }

        public class UpstreamPrivacyElement : Validatable
        {
            public UpstreamPrivacyElement(byte[] buffer)
            {
                if (buffer.Length == 3)
                {
                    KEY_SEQ = (buffer[0] & 0xf0) >> 4;
                    Version = (buffer[0] & 0x0f);
                    Encrypted = (buffer[1] & 0x80) != 0;
                    OddKey = (buffer[1] & 0x40) != 0;
                    Valid = true;
                }
                else if (buffer.Length == 4)
                {
                    //see CM-SP-SECv4.0-I01-190815.pdf, page page 29
                    throw new NotImplementedException(String.Format("BP_UP {0} {1}", nameof(buffer.Length), buffer.Length));
                }
                else
                {
                    Valid = false;
                }
            }

            public bool OddKey { get; set; }
            public bool Encrypted { get; private set; }
            public int KEY_SEQ { get; set; }

            public int Version { get; private set; }
        }

        public UpstreamPrivacyElement UpstreamPrivacyEhElement { get; private set; }

        public byte PayloadHeaderSupression { get; set; }

        public class DownstreamPrivacyElement
        {
            public DownstreamPrivacyElement(byte[] buffer)
            {
                MemoryStream ms = new MemoryStream(buffer, false);
                byte readUInt8 = ms.ReadUInt8();
                KEY_SEQ = (readUInt8 & 0xf0) >> 4;
                Version = (readUInt8 & 0x0f);
                ushort readUInt16Be = ms.ReadUInt16BE();
                Encrypted = (readUInt16Be & 0x8000) != 0;
                OddKey = (readUInt16Be & 0x4000) != 0;
                SecurityAssociationId = (readUInt16Be & 0x3fff);
                ms.ReadUInt8();     //reserved

            }
            public int KEY_SEQ { get; set; }
            public int Version { get; set; }
            public bool Encrypted { get; set; }
            public bool OddKey { get; set; }
            public int SecurityAssociationId { get; set; }
        }

        public DownstreamPrivacyElement DownstreamPrivacyEhElement { get; private set; }

        public bool Encrypted
        {
            get
            {
                if (DownstreamPrivacyEhElement == null)
                    return false;
                return DownstreamPrivacyEhElement.Encrypted;
            }
        }
    }
}
