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

namespace skyscraper5.Abertis
{
    internal class AbertisDecoder : ITsPacketProcessor, IPayloadUnitDecoder
    {
        public int Pid { get; }
        public AbertisDecoderEventHandler EventHandler { get; }

        public AbertisDecoder(int pid, AbertisDecoderEventHandler eventHandler)
        {
            this.Pid = pid;
            PacketLoss();
            this.EventHandler = eventHandler;
        }

        private bool inSync;
        private short bytesNeeded;
        private byte[] packetBuffer;
        
        
        
        public void PushPacket(TsPacket packet)
        {
            Span<byte> payload = packet.GetPayload();

            MemoryStream ms = new MemoryStream(payload.ToArray(), false);
            
            int syncLossCheck = 0;
            while (ms.GetAvailableBytes() > 0)
            {
                if (!inSync)
                {
                    byte syncByte = ms.ReadUInt8();
                    if (syncByte == 'G')
                    {
                        ms.Position--;
                        inSync = true;
                        if (syncLossCheck != 0 && ms.Position != syncLossCheck)
                        {
                            EventHandler.OnAbertisSyncLoss(Pid, syncLossCheck, ms.Position);
                        }
                    }
                }
                else
                {
                    long readChunkSize = Math.Min(bytesNeeded, ms.GetAvailableBytes());
                    int readResult = ms.Read(packetBuffer, 188 - bytesNeeded, (int)readChunkSize);
                    bytesNeeded -= (short)readResult;
                    if (bytesNeeded == 0)
                    {
                        DeliverPacket(packetBuffer, 0, 188);
                        Array.Clear(packetBuffer, 0, 188);
                        bytesNeeded = 188;
                        inSync = false;
                        syncLossCheck = (int)ms.Position;
                    }
                }
            }
        }

        public void PacketLoss()
        {
            packetBuffer = new byte[188];
            bytesNeeded = 188;
            inSync = false;
        }

        private Queue<byte[]> packetQueue;
        private void DeliverPacket(byte[] buffer, int offset, int length)
        {
            if (length != 188)
                throw new ArgumentOutOfRangeException(nameof(length), length, String.Format("{0} != {1}", nameof(length), 188));

            byte[] deliverPacketBuffer = new byte[188];
            Array.Copy(buffer, offset, deliverPacketBuffer, 0, 188);

            if (deliverPacketBuffer[0] == 'G')
            {
                //We'll use a queue to ensure that the stream type autodetection has enough time to decide whether this is the right stream type.
                if (packetQueue == null)
                    packetQueue = new Queue<byte[]>();
                packetQueue.Enqueue(deliverPacketBuffer);
                if (packetQueue.Count >= 100)
                    EventHandler.OnAbertisPacket(Pid, packetQueue.Dequeue());
            }
            else
            {
                EventHandler.OnAbertisSyncLoss(Pid);
                PacketLoss();
            }
        }
    }
}
