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

namespace skyscraper5.Mpeg2
{
    class ProgramStreamSystemHeader
    {
        public ProgramStreamSystemHeader(byte[] readBytes)
        {
            MemoryStream ms = new MemoryStream(readBytes, false);
            if (ms.ReadUInt32BE() != 0x000001BB)
                throw new DvbException("invalid program stream system header");

            ushort headerLength = ms.ReadUInt16BE();

            byte readUInt8 = ms.ReadUInt8();
            if ((readUInt8 & 0x80) != 0)
                throw new DvbException("invalid program stream system header");

            RateBound = readUInt8 & 0x7f;

            ushort readUInt16Be = ms.ReadUInt16BE();
            RateBound += ((readUInt8 & 0xfffe) >> 1);

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

            readUInt8 = ms.ReadUInt8();
            AudioBound = ((readUInt8 & 0xfc) >> 2);

            FixedFlag = (readUInt8 & 0x02) != 0;
            CspsFlag = (readUInt8 & 0x01) != 0;

            readUInt8 = ms.ReadUInt8();
            SystemAudioLockFlag = ((readUInt8 & 0x80) != 0);
            SystemVideoLockFlag = ((readUInt8 & 0x40) != 0);
            if ((readUInt8 & 0x20) != 0)
                throw new DvbException("invalid program stream system header");

            VideoBound = (readUInt8 & 0x1f);

            readUInt8 = ms.ReadUInt8();
            PacketRateRestrictionFlag = ((readUInt8 & 0x80) != 0);

            while (ms.GetAvailableBytes() > 3)
            {
                byte streamId = ms.ReadUInt8();
                if ((streamId & 0x80) == 0)
                    break;

                ushort uInt16Be = ms.ReadUInt16BE();
                if ((uInt16Be & 0xc000) != 0xc000)
                    break;

                bool pStdBufferBoundScale = (uInt16Be & 0x2000) != 0;
                int pStdBuffSizeBound = (uInt16Be & 0x1fff);

                ElementaryStream es = new ElementaryStream(streamId, pStdBufferBoundScale, pStdBuffSizeBound);
                if (ElementaryStreams == null)
                    ElementaryStreams = new List<ElementaryStream>();
                ElementaryStreams.Add(es);
            }
        }
        
        public bool PacketRateRestrictionFlag { get; private set; }

        public int VideoBound { get; private set; }

        public bool SystemVideoLockFlag { get; private set; }

        public bool SystemAudioLockFlag { get; private set; }

        public bool CspsFlag { get; private set; }

        public bool FixedFlag { get; private set; }

        public int AudioBound { get; private set; }

        public long RateBound { get; private set; }

        public List<ElementaryStream> ElementaryStreams { get; private set; }

        internal class ElementaryStream
        {
            public byte StreamId { get; }
            public bool PStdBufferBoundScale { get; }
            public int PStdBuffSizeBound { get; }

            public ElementaryStream(byte streamId, bool pStdBufferBoundScale, int pStdBuffSizeBound)
            {
                StreamId = streamId;
                PStdBufferBoundScale = pStdBufferBoundScale;
                PStdBuffSizeBound = pStdBuffSizeBound;
            }
        }
    }
}
