/*
 * Copyright 2009, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Bruno Albuquerque, bga@bug-br.org.br
 */

#include "DynamicBuffer.h"

#include <stdio.h>
#include <string.h>

#include <algorithm>

#include <Errors.h>
#include <SupportDefs.h>

#include <new>

DynamicBuffer::DynamicBuffer(size_t initialSize) :
	fBuffer(NULL),
	fBufferSize(0),
	fDataStart(0),
	fDataEnd(0),
	fInit(B_NO_INIT)
{
	fBuffer = new (std::nothrow) unsigned char[initialSize];
	if (fBuffer != NULL) {
		fBufferSize = initialSize;
		fInit = B_OK;
	}
}


DynamicBuffer::~DynamicBuffer()
{
	delete[] fBuffer;
	fBufferSize = 0;
	fDataStart = 0;
	fDataEnd = 0;
}


DynamicBuffer::DynamicBuffer(const DynamicBuffer& buffer) :
	fBuffer(NULL),
	fBufferSize(0),
	fDataStart(0),
	fDataEnd(0),
	fInit(B_NO_INIT)
{
	fInit = buffer.fInit;
	if (fInit == B_OK) {
		status_t result = _GrowToFit(buffer.fBufferSize, true);
		if (result == B_OK) {
			memcpy(fBuffer, buffer.fBuffer, fBufferSize);
			fDataStart = buffer.fDataStart;
			fDataEnd = buffer.fDataEnd;
		} else
			fInit = result;
	}
}


status_t
DynamicBuffer::InitCheck() const
{
	return fInit;
}


ssize_t
DynamicBuffer::Write(const void* data, size_t size)
{
	if (fInit != B_OK)
		return fInit;

	status_t result = _GrowToFit(size);
	if (result != B_OK)
		return result;

	memcpy(fBuffer + fDataEnd, data, size);
	fDataEnd += size;

	return (ssize_t)size;
}


ssize_t
DynamicBuffer::Read(void* data, size_t size)
{
	if (fInit != B_OK)
		return fInit;

	size = std::min(size, Size());
	if (size == 0)
		return 0;

	memcpy(data, fBuffer + fDataStart, size);
	fDataStart += size;

	if (fDataStart == fDataEnd)
		fDataStart = fDataEnd = 0;

	return size;
}


unsigned char*
DynamicBuffer::Data() const
{
	return fBuffer + fDataStart;
}


size_t
DynamicBuffer::Size() const
{
	return fDataEnd - fDataStart;
}


size_t
DynamicBuffer::BytesRemaining() const
{
	return fBufferSize - fDataEnd;
}


void
DynamicBuffer::PrintToStream()
{
	printf("Current buffer size : %ld\n", fBufferSize);
	printf("Data start position : %ld\n", fDataStart);
	printf("Data end position   : %ld\n", fDataEnd);
	printf("Bytes wasted        : %ld\n", fDataStart);
	printf("Bytes available     : %ld\n", fBufferSize - fDataEnd);
}


status_t
DynamicBuffer::_GrowToFit(size_t size, bool exact)
{
	if (size <= fBufferSize - fDataEnd)
		return B_OK;

	size_t newSize;
	if (!exact)
		newSize = (fBufferSize + size) * 2;
	else
		newSize = size;

	unsigned char* newBuffer = new (std::nothrow) unsigned char[newSize];
	if (newBuffer == NULL)
		return B_NO_MEMORY;

	if (fDataStart != fDataEnd)
		memcpy(newBuffer, fBuffer + fDataStart, fDataEnd - fDataStart);

	delete[] fBuffer;
	fBuffer = newBuffer;
	fDataEnd -= fDataStart;
	fDataStart = 0;
	fBufferSize = newSize;

	return B_OK;
}
