using skyscraper5.Skyscraper.IO;

namespace skyscraper8.GSE.GSE_HEM;

public class RayBuffer
{
    private MemoryStream currentItem;
    private Queue<Tuple<MemoryStream, int>> queue;

    public int QueuedItems
    {
        get
        {
            int result = 0;
            if (currentItem != null)
                result++;
            if (queue != null)
                result += queue.Count;
            return result;
        }
    }

    public void Enqueue(MemoryStream ms, int syncPoint)
    {
        if (currentItem == null)
        {
            currentItem = ms;
            currentItem.Position = syncPoint;
            return;
        }
        else
        {
            if (queue == null)
                queue = new Queue<Tuple<MemoryStream, int>>();
            queue.Enqueue(new Tuple<MemoryStream, int>(ms, syncPoint));
        }
    }

    public long AvailableBytes 
    { 
        get
        {
            if (currentItem == null)
                return 0;

            long result = currentItem.GetAvailableBytes();
            if (queue != null)
            {
                result += queue.Select(x => x.Item1.GetAvailableBytes()).Sum();
            }

            return result;
        }
    }

public byte ReadUInt8()
    {
        byte[] tmp = new byte[1];
        if (Read(tmp, 0, 1) != 1)
            throw new IOException("ReadUInt8 failed");
        return tmp[0];
    }

    private int Read(byte[] outBuffer, int offset, int count)
    {
        int result = 0;
        while (count > 0)
        {
            int stepSize = Math.Min(count, (int)currentItem.GetAvailableBytes());
            int stepResult = currentItem.Read(outBuffer, offset, stepSize);
            offset += stepResult;
            count -= stepResult;
            result += stepResult;
            if (currentItem.GetAvailableBytes() == 0)
            {
                currentItem = queue.Dequeue().Item1;
            }
        }

        return result;
    }

    public ushort ReadUInt16BE()
    {
        byte[] tmp = new byte[2];
        if (Read(tmp, 0, 2) != 2)
            throw new IOException("ReadUInt16BE failed");
        if (BitConverter.IsLittleEndian)
            (tmp[0], tmp[1]) = (tmp[1], tmp[0]);
        return BitConverter.ToUInt16(tmp, 0);
    }

    public byte[] ReadBytes(int p0)
    {
        byte[] tmp = new byte[p0];
        if (Read(tmp, 0, p0) != p0)
            throw new IOException(String.Format("Reading {0} bytes failed.", p0));
        return tmp;
    }

    public void Resync()
    {
        Tuple<MemoryStream, int> tuple = queue.Dequeue();
        currentItem = tuple.Item1;
        currentItem.Position = tuple.Item2;
        if (currentItem.Position > currentItem.Length)
        {
            Resync();
        }
    }
}