/** 
 * \file circbuff.h
 * \brief circbuff header file
 * \details File contains structures and functions declarations for circular (ring) buffer
 *
 * \author Juraj Golej
 * \version 1.0
 */


#ifndef CIRCBUFF_H_
#define CIRCBUFF_H_

#include <stdio.h>
#include <inttypes.h>
#include <stdbool.h>
#include <utils/retcode/retcode.h>
#include <utils/assert/arm_assert.h>

/** 
 * \brief circbuff structure
 *
 * Declaration of circbuff structure. There are defined all variables and function pointers.
 *
 * \note Encapsulated static array is here as pointer on \c void. The size of one element is defined in \c iItemSize.
 */
struct circbuff {
	void* iBuff;				///< Pointer on static array of elements
	uint16_t iItemSize;			///< Size of one element (in bytes)
	uint16_t iMaxSize;			///< Maximum size of \c iBuff array (in original element size)
	uint16_t iActSize;			///< Actual size of pushed elements in \c iBuff array (in original element size)
	uint16_t iReadIndex;		///< Read index in \c iBuff array (in original element size)
	uint16_t iWriteIndex;		///< Write index in \c iBuff array (in original element size)
}; /* struct circbuff */

/**
 * \brief Initialization of circular buffer
 * 
 * \param[in] This pointer on \c circbuff structure
 * \param[in] buff pointer on static array
 * \param[in] itemSize size of one element of array (\c sizeof(element) )
 * \param[in] buffSize maximum size of array
 * 
 * \return \b enum \b retCode Return code value
 * \retval ENone everything OK
 * \retval EInvalidParameter \c itemSize or \c buffSize is zero
 *
 * \warning It's important to call this function before anything else
 * \note It's recommended to call this function after \c struct \c circbuff definition
 */
enum retCode circbuff_init(volatile struct circbuff* const This, void* const buff, const uint16_t itemSize, const uint16_t buffSize);

/**
 * \brief Deinitialization of circular buffer
 * 
 * \param[in] This pointer on \c circbuff structure
 */
void circbuff_deinit(volatile struct circbuff* const This);

/**
 * \brief Push one element into circular buffer
 * 
 * This function push one element into circular buffer on the end of the queue.
 *
 * \param[in] This pointer on \c circbuff structure
 * \param[in] data \c void pointer on the element
 * 
 * \return \b enum \b retCode Return code value
 * \retval ENone everything OK
 * \retval ENullPtr \c This or \c data are \c NULL or isn't initialized
 * \retval EOverflow circular buffer is full
 *
 * \note Input \c data must be retype to \c void*
 */
enum retCode circbuff_push(volatile struct circbuff* const This, const void* const data);

/**
 * \brief Pull one element from circular buffer
 * 
 * This function pull one element from the front of the circular buffer.
 *
 * \param[in] This pointer on \c circbuff structure
 * \param[out] data \c void pointer on the element, where the data will be saved
 * 
 * \return \b enum \b retCode Return code value
 * \retval ENone everything OK
 * \retval ENullPtr \c This or \c data are \c NULL
 * \retval EDenied circular buffer is empty or isn't initialized
 *
 * \note Output \c data must be retype to \c void*
 */
enum retCode circbuff_pull(volatile struct circbuff* const This, void* const data);

/**
 * \brief Flush (clear) circular buffer
 *
 * \param[in] This pointer on \c circbuff structure
 * 
 * \return \b enum \b retCode Return code value
 * \retval ENone everything OK
 * \retval ENullPtr \c This pointer is \c NULL
 */
static inline enum retCode circbuff_flush(volatile struct circbuff* const This)
{
	if(!This) {
		return ENullPtr;
	}

	This->iActSize = 0;
	This->iReadIndex = 0;
	This->iWriteIndex = 0;

	return ENone;
}

/**
 * \brief Check, if circular buffer is full
 *
 * \param[in] This pointer on \c circbuff structure
 * 
 * \return \b bool Return buffer status
 * \retval false buffer isn't full
 * \retval true buffer is full or \c This pointer is \c NULL
 */
static inline bool circbuff_isFull(volatile struct circbuff* const This)
{
	if(!This) {
		return true;
	}

	if(This->iActSize == This->iMaxSize) {
		return true;
	}

	return false;
}

/**
 * \brief Check, if circular buffer is empty
 *
 * \param[in] This pointer on \c circbuff structure
 * 
 * \return \b bool Return buffer status
 * \retval false buffer isn't empty or \c This pointer is \c NULL
 * \retval true buffer is empty
 */
static inline bool circbuff_isEmpty(volatile struct circbuff* const This)
{
	if(!This) {
		return false;
	}

	if(This->iActSize == 0) {
		return true;
	}

	return false;
}

/**
 * \brief Get the maximum buffer length
 *
 * \param[in] This pointer on \c circbuff structure
 * 
 * \return \b uint16_t Return maximum buffer length or 0, if \c This pointer is \c NULL
 */
static inline uint16_t circbuff_maxSize(volatile struct circbuff* const This)
{
	if(!This) {
		return 0;
	}

	return This->iMaxSize;
}

/**
 * \brief Get actual number of elements in buffer
 *
 * \param[in] This pointer on \c circbuff structure
 * 
 * \return \b int32_t Return actual number of elements or error code
 * \retval -1 \c This pointer is \c NULL
 */
static inline int32_t circbuff_size(volatile const struct circbuff* const This)
{
	if(!This) {
		return -1;
	}

	return This->iActSize;
}


#endif /* CIRCBUFF_H_ */