#include "PA2.hpp"
#include "CircularBuffer.hpp"

CircularBuffer::CircularBuffer()
{
	memset(this, 0, sizeof(*this));
}

CircularBuffer::CircularBuffer(uint count)
{
	memset(this, 0, sizeof(*this));

	Resize(count);
}

void CircularBuffer::Resize(uint count)
{
	delete [] buffer;

	memset(this, 0, sizeof(*this));

	if(count > 0)
	{
		buffer = new byte[count];
		buffer_size = count;
	}
}

uint CircularBuffer::MaxReadBytes()
{
	return bytes_used;
}

uint CircularBuffer::MaxWriteBytes()
{
	return buffer_size - bytes_used;
}

uint CircularBuffer::MaxContiguousReadBytes()
{
	uint right = buffer_size - read_pos;

	return min(right, bytes_used);
}

byte *CircularBuffer::GetContiguousWritePointer(uint count)
{
	uint right = buffer_size - write_pos;

	assert(count <= right);
	assert(count + bytes_used <= buffer_size);

	byte *write = buffer + write_pos;
	write_pos += count;
	bytes_used += count;
	if(write_pos >= buffer_size)
	{
		write_pos -= buffer_size;
	}

	return write;
}

byte *CircularBuffer::PeekContiguousReadPointer(uint count)
{
	uint right = buffer_size - read_pos;

	assert(count <= right);
	assert(bytes_used - count <= bytes_used);

	return buffer + read_pos;
}

byte *CircularBuffer::GetContiguousReadPointer(uint count)
{
	byte *read = PeekContiguousReadPointer(count);

	read_pos += count;
	bytes_used -= count;
	if(read_pos >= buffer_size)
	{
		read_pos -= buffer_size;
	}

	return read;
}

void CircularBuffer::Peek(byte *destination, uint count)
{
	assert(count < MaxReadBytes());

	uint right = buffer_size - read_pos;
	if(count > right)
	{
		memcpy(destination, buffer + read_pos, right);
		memcpy(destination + right, buffer, count - right);
	}
	else
	{
		memcpy(destination, buffer + read_pos, count);
	}
}

void CircularBuffer::Read(byte *destination, uint count)
{
	Peek(destination, count);
	read_pos += count; 
	if(read_pos > buffer_size)
	{
		read_pos -= buffer_size;
	}
	bytes_used -= count;
}

void CircularBuffer::Write(byte *data, uint count)
{
	assert(count < MaxWriteBytes());

	uint right = buffer_size - write_pos;

	if(count > right)
	{
		memcpy(buffer + write_pos, data, right);
		memcpy(buffer, data + right, count - right);
		write_pos = count - right;
	}
	else
	{
		memcpy(buffer + write_pos, data, count);
		write_pos += count;
	}

	bytes_used += count;
}

void CircularBuffer::Clear()
{
	bytes_used = 0;
	read_pos = write_pos = 0;
}
