#include "cbuffer.h"

#include <string.h>

cBufferData *cBufferData::Construct(size_t Size) {
	cBufferData *data;

	data = (cBufferData*)new char[sizeof(cBufferData) + Size];
	data->m_Size = Size;
	data->m_Offset = 0;
	data->m_Filling = true;

	return data;
}

cBuffer::cBuffer(size_t BlockSize, uint MaxCount, bool Input) {
	m_BlockSize = BlockSize;
	m_MaxCount  = MaxCount;
	m_MayRead   = Input;
	m_MayWrite  = true;

	m_MaxUsed   = 0;
}

cBuffer::~cBuffer(void) {
	while (!isEmpty())
		delete[] (char*)dequeue();
}

bool cBuffer::Put(const char *Data, size_t Length) {
	ssize_t bytes;

	cBufferData *buffer = NULL;

	if (count())
		buffer = first();

	if ((!buffer) || ((!buffer->m_Filling) && (count() < m_MaxCount))) {
		buffer = cBufferData::Construct(m_BlockSize);
		enqueue(buffer);
	}

	if (!buffer->m_Filling)
		return false;

	if (Length <= buffer->Available()) {
		memcpy(*buffer, Data, Length);
		*buffer += Length;
	
		if (buffer->Available() == 0) {
			size_t realsize = buffer->m_Offset;

			buffer->m_Filling = false;
			buffer->m_Size    = realsize;
			buffer->m_Offset  = 0;
		}
	} else {
		bytes = buffer->Available();

		memcpy(*buffer, Data, bytes);

		buffer->m_Filling = false;
		buffer->m_Offset  = 0;

		Put(Data + bytes, Length - bytes);
	}

	if (count() > m_MaxUsed)
		m_MaxUsed = count();

	m_MayWrite = true;

	return true;
}

void cBuffer::Commit(void) {
	if (isEmpty())
		return;

	cBufferData *buffer = first();

	if (buffer->m_Filling) {
		size_t realsize = buffer->m_Offset;

		buffer->m_Filling = false;
		buffer->m_Size    = realsize;
		buffer->m_Offset  = 0;
	}
}

cTBFileNo &operator<<(cTBFileNo &dest, cBuffer &src) {
	ssize_t bytes;

	if (!src.m_MayWrite)
		return dest;

	cBufferData *buffer = src.last();

	if (!buffer->m_Filling) {
		if ((bytes = dest.SysWrite(*buffer, buffer->Available())) > 0) {
			*buffer += bytes;
			dest.HasWritten(bytes);
		}
	}

	if (buffer->m_Offset == buffer->m_Size)
		delete[] (char*)src.dequeue();

	if (!src.m_MayRead && (src.count() == 0))
		src.m_MayWrite = false;
	
	return dest;
}

cTBFileNo &operator>>(cTBFileNo &dest, cBuffer &src) {
	ssize_t bytes;

	cBufferData *buffer = NULL;

	if (!src.m_MayRead)
		return dest;

	if (src.count())
		buffer = src.first();

	if ((!buffer) || ((!buffer->m_Filling) && (src.count() < src.m_MaxCount))) {
		buffer = cBufferData::Construct(src.m_BlockSize);
		src.enqueue(buffer);
	}

	if (!buffer->m_Filling)
		return dest;

	if ((bytes = dest.SysRead(*buffer, buffer->Available())) > 0) {
		*buffer += bytes;
		dest.HasRead(bytes);
	}
	
	if ((bytes == 0) || (buffer->Available() == 0)) {
		size_t realsize = buffer->m_Offset;

		buffer->m_Filling = false;
		buffer->m_Size    = realsize;
		buffer->m_Offset  = 0;

		if (bytes == 0)
			src.m_MayRead = false;
	}

	if (src.count() > src.m_MaxUsed)
		src.m_MaxUsed = src.count();

	return dest;
}
