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.Mpeg2;
using skyscraper5.Skyscraper.IO;

namespace skyscraper5.Dvb.Psi
{
    public class TimetableParser : IPsiProcessor
    {
        public ITdtEventHandler TdtEventHandler { get; }
        public ITotEventHandler TotEventHandler { get; }

        public TimetableParser(ITdtEventHandler tdtEventHandler, ITotEventHandler totEventHandler)
        {
            TdtEventHandler = tdtEventHandler;
            TotEventHandler = totEventHandler;
        }

        public void GatherPsi(PsiSection section, int sourcePid)
        {
            switch (section.TableId)
            {
                case 0x73:  //time_offset_section
                    ParseTot(section.GetDataCopy());
                    break;
                case 0x70:  //time_and_date_table
                    ParseTdt(section.GetDataCopy());
                    break;
                default:
                    return;
            }
        }

        private void ParseTdt(byte[] buffer)
        {
            MemoryStream ms = new MemoryStream(buffer, false);
            byte table_id = ms.ReadUInt8();
            if (table_id != 0x70)
                throw new NotImplementedException("not a tdt");

            ushort readUInt16Be = ms.ReadUInt16BE();
            bool sectionSyntaxIndicator = (readUInt16Be & 0x8000) != 0;
            int sectionLength = readUInt16Be & 0x0fff;

            if (ms.GetAvailableBytes() < 5)
                return;

            DateTime? utcTime = ms.ReadEtsiEn300468AnnexCDateTime();
            if (utcTime != null)
                TdtEventHandler.OnTdtTime(utcTime.Value);
        }

        private void ParseTot(byte[] buffer)
        {
            MemoryStream ms = new MemoryStream(buffer, false);
            byte table_id = ms.ReadUInt8();
            if (table_id != 0x73)
                throw new NotImplementedException("not a tot");

            ushort readUInt16Be = ms.ReadUInt16BE();
            bool sectionSyntaxIndicator = (readUInt16Be & 0x8000) != 0;
            int sectionLength = readUInt16Be & 0x0fff;

            DateTime? utcTime = ms.ReadEtsiEn300468AnnexCDateTime();
            if (utcTime == null)
                return;
            

            int descriptorsLoopLength = ms.ReadUInt16BE() & 0x0fff;

            LocalTimeOffsetDescriptor ltod = null;

            byte[] descriptorsData = ms.ReadBytes(descriptorsLoopLength);
            IEnumerable<TsDescriptor> unpackDescriptors = TsDescriptorUnpacker.GetInstance().UnpackDescriptors(descriptorsData, "TOT");
            foreach (TsDescriptor dvbDescriptor in unpackDescriptors)
            {
                string name = dvbDescriptor.GetType().Name;
                switch (name)
                {
                    case nameof(LocalTimeOffsetDescriptor):
                        ltod = (LocalTimeOffsetDescriptor)dvbDescriptor;
                        break;
                    default:
                        throw new NotImplementedException(name);
                }
            }

            if (ltod != null)
            {
                if (ltod.Valid)
                {
                    TotEventHandler.OnTotTime(utcTime.Value, ltod);
                }
            }
        }
    }
}
