using System.IO;
using skyscraper5.Dvb;
using skyscraper5.Skyscraper.IO;

namespace skyscraper5.Mpeg2
{
    class ProgramStreamPackHeader
    {
        public ProgramStreamPackHeader(byte[] readBytes)
        {
            MemoryStream ms = new MemoryStream(readBytes, false);
            if (ms.ReadUInt32BE() != 0x000001BA)
                throw new DvbException("invalid pack header");

            byte readUInt8 = ms.ReadUInt8();
            int magic01 = (readUInt8 & 0xc0) >> 6;
            if (magic01 != 1)
                throw new DvbException("invalid pack header");

            SystemClockReferenceBase = (readUInt8 & 0x38) >> 3;
            SystemClockReferenceBase <<= 2;

            if ((readUInt8 & 0x04) != 0)
                throw new DvbException("invalid pack header");

            SystemClockReferenceBase += (readUInt8 & 0x03);
            SystemClockReferenceBase <<= 8;

            readUInt8 = ms.ReadUInt8();
            SystemClockReferenceBase += readUInt8;
            SystemClockReferenceBase <<= 5;

            readUInt8 = ms.ReadUInt8();
            SystemClockReferenceBase += ((readUInt8 & 0xf8) >> 3);
            SystemClockReferenceBase <<= 2;

            if ((readUInt8 & 0x04) != 0)
                throw new DvbException("invalid pack header");

            SystemClockReferenceBase += ((readUInt8 & 0x03));
            SystemClockReferenceBase <<= 8;

            readUInt8 = ms.ReadUInt8();
            SystemClockReferenceBase += readUInt8;
            SystemClockReferenceBase <<= 5;

            readUInt8 = ms.ReadUInt8();
            SystemClockReferenceBase += ((readUInt8 & 0xf8) >> 3);

            if ((readUInt8 & 0x04) != 0)
                throw new DvbException("invalid pack header");

            SystemClockReferenceExtension = ((readUInt8 & 0x03));
            SystemClockReferenceExtension <<= 7;

            readUInt8 = ms.ReadUInt8();
            SystemClockReferenceExtension += ((readUInt8 & 0xfe) >> 1);

            if ((readUInt8 & 0x01) != 0)
                throw new DvbException("invalid pack header");

            ProgramMuxRate = ms.ReadUInt16BE();
            ProgramMuxRate <<= 6;
            
            readUInt8 = ms.ReadUInt8();
            ProgramMuxRate += (readUInt8 & 0xfc);

            if ((readUInt8 & 0x02) != 0)
                throw new DvbException("invalid pack header");

            if ((readUInt8 & 0x01) != 0)
                throw new DvbException("invalid pack header");

            readUInt8 = ms.ReadUInt8();
            PackStuffingLength = readUInt8 & 0x07;

            ms.Position += PackStuffingLength;

            long available = ms.GetAvailableBytes();
            if (available >= 4)
            {
                if (ms.ReadUInt32BE() == 0x000001BB)
                {
                    ms.Position -= 4;
                    SystemHeader = new ProgramStreamSystemHeader(ms.ReadBytes(available));
                }
            }
        }

        public ProgramStreamSystemHeader SystemHeader { get; private set; }

        public int PackStuffingLength { get; private set; }

        public long ProgramMuxRate { get; private set; }

        public long SystemClockReferenceExtension { get; private set; }

        public long SystemClockReferenceBase { get; private set; }
    }
}
