
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
//
// Changes to this file may cause incorrect behavior and will be lost when
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
#nullable enable
namespace HathoraCloud.Utils
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;

    /// <summary>
    /// This stream maintains data only until the data is read, then it is purged from the stream.
    /// </summary>
    public class MemoryQueueBufferStream : Stream
    {
        private class Chunk
        {
            public int ChunkReadStartIndex { get; set; } = 0;

            public byte[] Data { get; set; }

            public Chunk(byte[] data)
            {
                Data = data;
            }
        }

        private Queue<Chunk> bufferQueue;

        /// <summary>
        /// Indicates whether Unity marked the response this stream is associated with as complete.
        /// If complete is true, then more data may not be added to the stream.
        /// </summary>
        public bool Complete { get; set; } = false;

        public MemoryQueueBufferStream()
        {
            bufferQueue = new Queue<Chunk>();
        }

        /// <summary>
        /// Reads data from the stream. 
        /// </summary>
        public override int Read(byte[] buffer, int offset, int count)
        {
            ValidateBufferArgs(buffer, offset, count);

            int remainingBytesToRead = count;

            var totalBytesRead = 0;

            while (totalBytesRead <= count && bufferQueue.Count > 0)
            {
                Chunk chunk = bufferQueue.Peek();

                int unreadChunkLength = chunk.Data.Length - chunk.ChunkReadStartIndex;

                int bytesToRead = Math.Min(unreadChunkLength, remainingBytesToRead);

                if (bytesToRead > 0)
                {
                    Buffer.BlockCopy(
                        chunk.Data,
                        chunk.ChunkReadStartIndex,
                        buffer,
                        offset + totalBytesRead,
                        bytesToRead
                    );

                    totalBytesRead += bytesToRead;
                    remainingBytesToRead -= bytesToRead;

                    if (chunk.ChunkReadStartIndex + bytesToRead >= chunk.Data.Length)
                    {
                        bufferQueue.Dequeue();
                    }
                    else
                    {
                        chunk.ChunkReadStartIndex = chunk.ChunkReadStartIndex + bytesToRead;
                    }
                }
                else
                {
                    break;
                }
            }

            return totalBytesRead;
        }

        private void ValidateBufferArgs(byte[] buffer, int offset, int count)
        {
            if (offset < 0)
            {
                throw new ArgumentOutOfRangeException("offset", "offset must be non-negative");
            }
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count", "count must be non-negative");
            }
            if ((buffer.Length - offset) < count)
            {
                throw new ArgumentException("requested count exceeds available size");
            }
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            ValidateBufferArgs(buffer, offset, count);

            byte[] bufSave = new byte[count];
            Buffer.BlockCopy(buffer, offset, bufSave, 0, count);

            bufferQueue.Enqueue(new Chunk(bufSave));
        }

        public override bool CanSeek
        {
            get { return false; }
        }

        public override long Position
        {
            get { return 0; }
            set { throw new NotSupportedException(GetType().Name + " is not seekable"); }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException(GetType().Name + " is not seekable");
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException(GetType().Name + " length can not be changed");
        }

        public override bool CanRead
        {
            get { return true; }
        }

        public override long Length
        {
            get
            {
                if (bufferQueue == null)
                {
                    return 0;
                }

                if (bufferQueue.Count == 0)
                {
                    return 0;
                }

                return bufferQueue.Sum(b => b.Data.Length - b.ChunkReadStartIndex);
            }
        }

        public override void Flush() { }
    }
}
