using Lidgren.Network;
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;

namespace Barotrauma.Networking
{

    public class VoipQueue : IDisposable
    {
        public const int BUFFER_COUNT = 8;
        protected int[] bufferLengths;
        protected byte[][] buffers;
        protected int newestBufferInd;
        protected bool firstRead;

        public int EnqueuedTotalLength
        {
            get
            {
                int enqueuedTotalLength = 0;
                for (int i = 0; i < BUFFER_COUNT; i++)
                {
                    enqueuedTotalLength += bufferLengths[i];
                }
                return enqueuedTotalLength;
            }
        }

        public byte[] BufferToQueue
        {
            get;
            protected set;
        }

        public virtual byte QueueID
        {
            get;
            protected set;
        }

        public UInt16 LatestBufferID
        {
            get;
            protected set;
        }

        public bool CanSend
        {
            get;
            protected set;
        }

        public bool CanReceive
        {
            get;
            protected set;
        }

        public DateTime LastReadTime
        {
            get;
            private set;
        }

        public VoipQueue(byte id, bool canSend, bool canReceive)
        {
            BufferToQueue = new byte[VoipConfig.MAX_COMPRESSED_SIZE];
            newestBufferInd = BUFFER_COUNT - 1;
            bufferLengths = new int[BUFFER_COUNT];
            buffers = new byte[BUFFER_COUNT][];
            for (int i = 0; i < BUFFER_COUNT; i++)
            {
                buffers[i] = new byte[VoipConfig.MAX_COMPRESSED_SIZE];
            }
            QueueID = id;
            CanSend = canSend;
            CanReceive = canReceive;
            LatestBufferID = BUFFER_COUNT-1;
            firstRead = true;

            LastReadTime = DateTime.Now;
        }

        public void EnqueueBuffer(int length)
        {
            if (length > byte.MaxValue) return;

            newestBufferInd = (newestBufferInd + 1) % BUFFER_COUNT;

            int enqueuedTotalLength = EnqueuedTotalLength;

            bufferLengths[newestBufferInd] = length;
            BufferToQueue.CopyTo(buffers[newestBufferInd], 0);
            
            if ((enqueuedTotalLength+length)>0) LatestBufferID++;
        }

        public void RetrieveBuffer(int id,out int outSize,out byte[] outBuf)
        {
            lock (buffers)
            {
                if (id >= LatestBufferID - (BUFFER_COUNT - 1) && id <= LatestBufferID)
                {
                    int index = (newestBufferInd - (LatestBufferID - id)); if (index < 0) index += BUFFER_COUNT;
                    outSize = bufferLengths[index];
                    outBuf = buffers[index];
                    return;
                }
            }
            outSize = -1;
            outBuf = null;
        }

        public virtual void Write(NetBuffer msg)
        {
            if (!CanSend) throw new Exception("Called Write on a VoipQueue not set up for sending");

            msg.Write((UInt16)LatestBufferID);
            for (int i = 0; i < BUFFER_COUNT; i++)
            {
                int index = (newestBufferInd + i + 1) % BUFFER_COUNT;

                msg.Write((byte)bufferLengths[index]);
                msg.Write(buffers[index], 0, bufferLengths[index]);
            }
        }

        public virtual bool Read(NetBuffer msg)
        {
            if (!CanReceive) throw new Exception("Called Read on a VoipQueue not set up for receiving");

            UInt16 incLatestBufferID = msg.ReadUInt16();
            if (firstRead || NetIdUtils.IdMoreRecent(incLatestBufferID,LatestBufferID))
            {
                firstRead = false;
                for (int i = 0; i < BUFFER_COUNT; i++)
                {
                    bufferLengths[i] = msg.ReadByte();
                    msg.ReadBytes(buffers[i], 0, bufferLengths[i]);
                }
                newestBufferInd = BUFFER_COUNT - 1;
                LatestBufferID = incLatestBufferID;
                LastReadTime = DateTime.Now;
                return true;
            }
            else
            {
                for (int i = 0; i < BUFFER_COUNT; i++)
                {
                    byte len = msg.ReadByte();
                    msg.Position += len * 8;
                }
                return false;
            }
        }

        public virtual void Dispose() { }
    }
}
