#ifndef FSFW_CONTAINER_SIMPLERINGBUFFER_H_
#define FSFW_CONTAINER_SIMPLERINGBUFFER_H_

#include "RingBufferBase.h"
#include <cstddef>

/**
 * @brief 	Circular buffer implementation, useful for buffering
 *          into data streams.
 * @details
 * Note that the deleteData() has to be called to increment the read pointer.
 * This class allocated dynamically, so
 * @ingroup containers
 */
class SimpleRingBuffer: public RingBufferBase<> {
public:
    /**
     * This constructor allocates a new internal buffer with the supplied size.
     *
     * @param size
     * @param overwriteOld If the ring buffer is overflowing at a write
     * operation, the oldest data will be overwritten.
     * @param maxExcessBytes These additional bytes will be allocated in addtion
     * to the specified size to accomodate contiguous write operations
     * with getFreeElement.
     *
     */
	SimpleRingBuffer(const size_t size, bool overwriteOld,
	        size_t maxExcessBytes = 0);
	/**
	 * This constructor takes an external buffer with the specified size.
	 * @param buffer
	 * @param size
	 * @param overwriteOld
	 * If the ring buffer is overflowing at a write operartion, the oldest data
     * will be overwritten.
	 * @param maxExcessBytes
	 * If the buffer can accomodate additional bytes for contigous write
	 * operations with getFreeElement, this is the maximum allowed additional
	 * size
	 */
	SimpleRingBuffer(uint8_t* buffer, const size_t size, bool overwriteOld,
	        size_t maxExcessBytes = 0);

	virtual ~SimpleRingBuffer();

	/**
	 * Write to circular buffer and increment write pointer by amount.
	 * @param data
	 * @param amount
	 * @return -@c RETURN_OK if write operation was successfull
	 * -@c RETURN_FAILED if
	 */
	ReturnValue_t writeData(const uint8_t* data, size_t amount);

	/**
	 * Returns a pointer to a free element. If the remaining buffer is
	 * not large enough, the data will be written past the actual size
	 * and the amount of excess bytes will be cached. This function
	 * does not increment the write pointer!
	 * @param writePointer Pointer to a pointer which can be used to write
	 * contiguous blocks into the ring buffer
	 * @param amount
	 * @return
	 */
	ReturnValue_t getFreeElement(uint8_t** writePointer, size_t amount);

	/**
	 * This increments the write pointer and also copies the excess bytes
	 * to the beginning. It should be called if the write operation
	 * conducted after calling getFreeElement() was performed.
	 * @return
	 */
	void confirmBytesWritten(size_t amount);

	virtual size_t getExcessBytes() const;
	/**
	 * Helper functions which moves any excess bytes to the start
	 * of the ring buffer.
	 * @return
	 */
	virtual void moveExcessBytesToStart();

	/**
	 * Read from circular buffer at read pointer.
	 * @param data
	 * @param amount
	 * @param incrementReadPtr
	 * If this is set to true, the read pointer will be incremented.
	 * If readRemaining is set to true, the read pointer will be incremented
	 * accordingly.
	 * @param readRemaining
	 * If this is set to true, the data will be read even if the amount
	 * specified exceeds the read data available.
	 * @param trueAmount [out]
	 * If readRemaining was set to true, the true amount read will be assigned
	 * to the passed value.
	 * @return
	 * - @c RETURN_OK if data was read successfully
	 * - @c RETURN_FAILED if not enough data was available and readRemaining
	 *      was set to false.
	 */
	ReturnValue_t readData(uint8_t* data, size_t amount,
			bool incrementReadPtr = false, bool readRemaining = false,
			size_t* trueAmount = nullptr);

	/**
	 * Delete data by incrementing read pointer.
	 * @param amount
	 * @param deleteRemaining
	 * If the amount specified is larger than the remaing size to read and this
	 * is set to true, the remaining amount will be deleted as well
	 * @param trueAmount [out]
	 * If deleteRemaining was set to true, the amount deleted will be assigned
	 * to the passed value.
	 * @return
	 */
	ReturnValue_t deleteData(size_t amount, bool deleteRemaining = false,
	        size_t* trueAmount = nullptr);

private:
	static const uint8_t READ_PTR = 0;
	uint8_t* buffer = nullptr;
	size_t maxExcessBytes;
	size_t excessBytes = 0;
};

#endif /* FSFW_CONTAINER_SIMPLERINGBUFFER_H_ */