#include "IFIFO.h"

IFIFO::IFIFO(UInt32 length)
{
	fifoBuf = new UInt8[length];
	fifoBufSize = length;
	fifoBase = 0;
	fifoDataLength = 0;
}

IFIFO::~IFIFO()
{
	delete fifoBuf;
}

bool IFIFO::Push(UInt8 * buf, UInt32 length)
{
	// would that overflow the buffer?
	if(length > GetBufferRemain())
		return false;

	UInt32	writeOffset = GetWriteOffset();

	// will this cross the end of the buffer?
	if(writeOffset + length > fifoBufSize)
	{
		UInt32	segmentLength = fifoBufSize - writeOffset;

		std::memcpy(&fifoBuf[writeOffset], buf, segmentLength);
		std::memcpy(fifoBuf, &buf[segmentLength], length - segmentLength);
	}
	else
	{
		std::memcpy(&fifoBuf[writeOffset], buf, length);
	}

	// update pointers
	fifoDataLength += length;

	return true;
}

bool IFIFO::Pop(UInt8 * buf, UInt32 length)
{
	bool	result = Peek(buf, length);

	// update pointers if we were successful
	if(result)
	{
		fifoDataLength -= length;
		fifoBase = ToRawOffset(fifoBase + length);
	}

	return result;
}

bool IFIFO::Peek(UInt8 * buf, UInt32 length)
{
	// would that underflow the buffer?
	if(length > fifoDataLength)
		return false;

	// will this cross the end of the buffer?
	if(fifoBase + length > fifoBufSize)
	{
		UInt32	segmentLength = fifoBufSize - fifoBase;

		std::memcpy(buf, &fifoBuf[fifoBase], segmentLength);
		std::memcpy(&buf[segmentLength], fifoBuf, length - segmentLength);
	}
	else
	{
		std::memcpy(buf, &fifoBuf[fifoBase], length);
	}

	return true;
}

void IFIFO::Clear(void)
{
	fifoDataLength = 0;

	// this isn't needed, but staying away from the buffer end is always good
	fifoBase = 0;
}