#ifndef FRAMEWORK_CONTAINER_RINGBUFFERBASE_H_
#define FRAMEWORK_CONTAINER_RINGBUFFERBASE_H_

#include "../returnvalues/HasReturnvaluesIF.h"
#include <cstddef>

template<uint8_t N_READ_PTRS = 1>
class RingBufferBase {
public:
	RingBufferBase(size_t startAddress, const size_t size, bool overwriteOld) :
			start(startAddress), write(startAddress), size(size),
			overwriteOld(overwriteOld) {
		for (uint8_t count = 0; count < N_READ_PTRS; count++) {
			read[count] = startAddress;
		}
	}

	virtual ~RingBufferBase() {}

	bool isFull(uint8_t n = 0) {
		return (availableWriteSpace(n) == 0);
	}
	bool isEmpty(uint8_t n = 0) {
		return (getAvailableReadData(n) == 0);
	}

	size_t getAvailableReadData(uint8_t n = 0) const {
		return ((write + size) - read[n]) % size;
	}
	size_t availableWriteSpace(uint8_t n = 0) const  {
		//One less to avoid ambiguous full/empty problem.
		return (((read[n] + size) - write - 1) % size);
	}

	bool overwritesOld() const {
		return overwriteOld;
	}

	size_t getMaxSize() const {
		return size - 1;
	}

	void clear() {
		write = start;
		for (uint8_t count = 0; count < N_READ_PTRS; count++) {
			read[count] = start;
		}
	}

	size_t writeTillWrap() {
		return (start + size) - write;
	}

	size_t readTillWrap(uint8_t n = 0) {
		return (start + size) - read[n];
	}

	size_t getStart() const {
		return start;
	}

protected:
	const size_t start;
	size_t write;
	size_t read[N_READ_PTRS];
	const size_t size;
	const bool overwriteOld;
	void incrementWrite(uint32_t amount) {
		write = ((write + amount - start) % size) + start;
	}
	void incrementRead(uint32_t amount, uint8_t n = 0) {
		read[n] = ((read[n] + amount - start) % size) + start;
	}

	ReturnValue_t readData(uint32_t amount, uint8_t n = 0) {
		if (getAvailableReadData(n) >= amount) {
			incrementRead(amount, n);
			return HasReturnvaluesIF::RETURN_OK;
		} else {
			return HasReturnvaluesIF::RETURN_FAILED;
		}
	}

	ReturnValue_t writeData(uint32_t amount) {
		if (availableWriteSpace() >= amount or overwriteOld) {
			incrementWrite(amount);
			return HasReturnvaluesIF::RETURN_OK;
		} else {
			return HasReturnvaluesIF::RETURN_FAILED;
		}
	}


	size_t getRead(uint8_t n = 0) const {
		return read[n];
	}

	void setRead(uint32_t read, uint8_t n = 0) {
		if (read >= start && read < (start+size)) {
			this->read[n] = read;
		}
	}

	uint32_t getWrite() const {
		return write;
	}

	void setWrite(uint32_t write) {
		this->write = write;
	}
};

#endif /* FRAMEWORK_CONTAINER_RINGBUFFERBASE_H_ */