#include "../container/SimpleRingBuffer.h"
#include <cstring>

SimpleRingBuffer::SimpleRingBuffer(const size_t size, bool overwriteOld,
		size_t maxExcessBytes) :
		RingBufferBase<>(0, size, overwriteOld),
		maxExcessBytes(maxExcessBytes) {
	if(maxExcessBytes > size) {
		this->maxExcessBytes = size;
	}
	else {
		this->maxExcessBytes = maxExcessBytes;
	}
	buffer = new uint8_t[size + maxExcessBytes];
}

SimpleRingBuffer::SimpleRingBuffer(uint8_t *buffer, const size_t size,
		bool overwriteOld, size_t maxExcessBytes):
        RingBufferBase<>(0, size, overwriteOld), buffer(buffer) {
	if(maxExcessBytes > size) {
		this->maxExcessBytes = size;
	}
	else {
		this->maxExcessBytes = maxExcessBytes;
	}
}


SimpleRingBuffer::~SimpleRingBuffer() {
	delete[] buffer;
}


ReturnValue_t SimpleRingBuffer::getFreeElement(uint8_t **writePointer,
        size_t amount) {
    if (availableWriteSpace() >= amount or overwriteOld) {
        size_t amountTillWrap = writeTillWrap();
        if (amountTillWrap < amount) {
            if((amount - amountTillWrap + excessBytes) > maxExcessBytes) {
                return HasReturnvaluesIF::RETURN_FAILED;
            }
            excessBytes = amount - amountTillWrap;
        }
        *writePointer = &buffer[write];
        return HasReturnvaluesIF::RETURN_OK;
    }
    else {
        return HasReturnvaluesIF::RETURN_FAILED;
    }
}

void SimpleRingBuffer::confirmBytesWritten(size_t amount) {
	if(getExcessBytes() > 0) {
		moveExcessBytesToStart();
	}
	incrementWrite(amount);

}

ReturnValue_t SimpleRingBuffer::writeData(const uint8_t* data,
		size_t amount) {
	if (availableWriteSpace() >= amount or overwriteOld) {
		size_t amountTillWrap = writeTillWrap();
		if (amountTillWrap >= amount) {
			// remaining size in buffer is sufficient to fit full amount.
			memcpy(&buffer[write], data, amount);
		}
		else {
			memcpy(&buffer[write], data, amountTillWrap);
			memcpy(buffer, data + amountTillWrap, amount - amountTillWrap);
		}
		incrementWrite(amount);
		return HasReturnvaluesIF::RETURN_OK;
	} else {
		return HasReturnvaluesIF::RETURN_FAILED;
	}
}

ReturnValue_t SimpleRingBuffer::readData(uint8_t* data, size_t amount,
		bool incrementReadPtr, bool readRemaining, size_t* trueAmount) {
	size_t availableData = getAvailableReadData(READ_PTR);
	size_t amountTillWrap = readTillWrap(READ_PTR);
	if (availableData < amount) {
		if (readRemaining) {
			// more data available than amount specified.
			amount = availableData;
		} else {
			return HasReturnvaluesIF::RETURN_FAILED;
		}
	}
	if (trueAmount != nullptr) {
		*trueAmount = amount;
	}
	if (amountTillWrap >= amount) {
		memcpy(data, &buffer[read[READ_PTR]], amount);
	} else {
		memcpy(data, &buffer[read[READ_PTR]], amountTillWrap);
		memcpy(data + amountTillWrap, buffer, amount - amountTillWrap);
	}

	if(incrementReadPtr) {
		deleteData(amount, readRemaining);
	}
	return HasReturnvaluesIF::RETURN_OK;
}

size_t SimpleRingBuffer::getExcessBytes() const {
    return excessBytes;
}

void SimpleRingBuffer::moveExcessBytesToStart() {
    if(excessBytes > 0) {
        std::memcpy(buffer, &buffer[size], excessBytes);
        excessBytes = 0;
    }
}

ReturnValue_t SimpleRingBuffer::deleteData(size_t amount,
		bool deleteRemaining, size_t* trueAmount) {
	size_t availableData = getAvailableReadData(READ_PTR);
	if (availableData < amount) {
		if (deleteRemaining) {
			amount = availableData;
		} else {
			return HasReturnvaluesIF::RETURN_FAILED;
		}
	}
	if (trueAmount != nullptr) {
		*trueAmount = amount;
	}
	incrementRead(amount, READ_PTR);
	return HasReturnvaluesIF::RETURN_OK;
}