using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using skyscraper5.Dvb;
using skyscraper5.Dvb.Descriptors;
using skyscraper5.Mpeg2.Descriptors;
using skyscraper5.Skyscraper.IO;

namespace skyscraper5.Mpeg2.Psi
{
    class TsdtParser : IPsiProcessor
    {
        public TsdtParser(ITsdtEventHandler tsdtEventHandler)
        {
            this.EventHandler = tsdtEventHandler;
        }

        public ITsdtEventHandler EventHandler { get; private set; }

        public void GatherPsi(PsiSection section, int sourcePid)
        {
            MemoryStream ms = new MemoryStream(section.GetDataCopy(), false);
            byte tableId = ms.ReadUInt8();
            if (tableId != 0x03)
                return;

            ushort readUInt16Be = ms.ReadUInt16BE();
            bool sectionSyntaxIndicator = (readUInt16Be & 0x8000) != 0;
            int sectionLength = (readUInt16Be & 0x0fff);
            if (sectionLength > 1021)
                return;
            ms.ReadUInt16BE();  //reserved

            if (ms.GetAvailableBytes() == 0)
                return;
            byte readUInt8 = ms.ReadUInt8();
            int versionNumber = (readUInt8 & 0x3e);
            bool currentNextIndicator = (readUInt8 & 0x01) != 0;

            if (ms.GetAvailableBytes() == 0)
                return;
            byte sectionNumber = ms.ReadUInt8();
            if (ms.GetAvailableBytes() == 0)
                return;
            byte lastSectionNumber = ms.ReadUInt8();
            if (sectionNumber > lastSectionNumber)
                return;

            if (ms.GetAvailableBytes() < 4)
                return;
            byte[] descriptorBytes = ms.ReadBytes(ms.GetAvailableBytes() - 4);
            ParseDescriptors(descriptorBytes);

            uint crc32 = ms.ReadUInt32BE();
        }

        private void ParseDescriptors(byte[] buffer)
        {
            RegistrationDescriptor registrationDescriptor = null;
            IEnumerable<TsDescriptor> descriptors = TsDescriptorUnpacker.GetInstance().UnpackDescriptors(buffer, "TSDT");
            foreach (TsDescriptor tsDescriptor in descriptors)
            {
                string name = tsDescriptor.GetType().Name;
                switch (name)
                {
                    case nameof(TransportStreamDescriptor):
                        EventHandler.NotifyOfCompliance(((TransportStreamDescriptor)tsDescriptor).Compliance);
                        break;
                    case nameof(DsngDescriptor):
                        EventHandler.NotifiyStationIdentification(((DsngDescriptor)tsDescriptor).StationIdentification);
                        break;
                    case nameof(UserDefinedDescriptor):
                        if (registrationDescriptor == null)
                            break;
                        TsDescriptor userDefinedDescriptor = UserDefinedDescriptorUnpacker.GetInstance().UnpackUserDefinedDescriptor(
                            (UserDefinedDescriptor)tsDescriptor, registrationDescriptor.FormatIdentifier, "TSDT");
                        if (userDefinedDescriptor == null)
                            break;
                        throw new NotImplementedException(userDefinedDescriptor.GetType().Name);
                    case nameof(RegistrationDescriptor):
                        RegistrationDescriptor rd = (RegistrationDescriptor)tsDescriptor;
                        registrationDescriptor = rd;
                        break;
                    case nameof(StuffingDescriptor):
                        //"The IRDs may discard the stuffing bytes."
                        break;
                    default:
                        throw new NotImplementedException(name);
                }
            }
        }
    }
}
