/*
 * Copyright (c) 2022, Shenzhen CVA Innovation CO.,LTD
 * All rights reserved.
 *
 * Shenzhen CVA Innovation CO.,LTD (CVA chip) is supplying this file for use
 * exclusively with CVA's microcontroller products. This file can be freely
 * distributed within development tools that are supporting such microcontroller
 * products.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
 * CVA SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
 * OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 */

#ifndef _RINGBUFFER_H_
#define _RINGBUFFER_H_

/*! \brief  Contains public interface to various functions related
 *          to the ring buffer template
 */

/*******************************************************************************
 * the includes
 ******************************************************************************/

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/*******************************************************************************
 * the defines
 ******************************************************************************/

/*******************************************************************************
 * the typedefs
 ******************************************************************************/

/*! \brief RingBuffer prototype definition
 */
typedef struct _RingBufferType_
{
    void   *pBuffer;    /*!< The actual buffer holder */
    int32_t readIndex;  /*!< The current index for the read */
    int32_t writeIndex; /*!< The current index for the write */
    int32_t usedNum;    /*!< The number of used elements */
    int32_t totalNum;   /*!< The number of total elements */
} RingBufferType;

/*******************************************************************************
 * the globals
 ******************************************************************************/

/*******************************************************************************
 * the function prototypes
 ******************************************************************************/

/*! \brief Initializes the ringbuffer instance
 *
 * This function initializes a ringbuffer instance
 *
 * \param[in] obj : pointer to RingBuffer instance
 * \param[in] pBuffer : the actual buffer holder
 * \param[in] size : the size of actual buffer holder
 */
static inline void RingBuffer_Init(RingBufferType *obj, void *pBuffer, uint32_t size)
{
    obj->pBuffer    = pBuffer;
    obj->readIndex  = 0;
    obj->writeIndex = 0;
    obj->usedNum    = 0;
    obj->totalNum   = size;
}

/*! \brief Puts an element to the ringbuffer.
 *
 * This function puts an element to the ringbuffer.
 *
 * \param[in] obj : pointer to RingBuffer instance
 * \param[in] pElem : pointer to the element to push into the buffer
 * \param[in] size : the size of the element in bytes
 * \return the result of the put operation
 *         true : success
 *         false : failed because the buffer is full
 */
static inline bool RingBuffer_Put(RingBufferType *obj, const void *pElem, uint32_t size)
{
    uint32_t       i = 0;
    unsigned char *src;
    unsigned char *dest;

    /* Check if the buffer is full */
    if(obj->usedNum >= obj->totalNum)
    {
        return false;
    }

    /* Copy the element */
    src  = (unsigned char *)pElem;
    dest = (unsigned char *)obj->pBuffer + (obj->writeIndex * size);
    for(i = 0; i < size; i++)
    {
        *dest++ = *src++;
    }

    /* Move the index */
    obj->writeIndex++;
    if(obj->writeIndex >= obj->totalNum)
    {
        obj->writeIndex = 0;
    }
    obj->usedNum++;

    return true;
}

/*! \brief Gets an element from the ringbuffer.
 *
 * This function gets an element from the ringbuffer.
 *
 * \param[in] obj : pointer to RingBuffer instance
 * \param[in] pElem : pointer to the element to get from the buffer
 * \param[in] size : the size of the element in bytes
 * \return the result of the put operation
 *         true : success
 *         false : failed because the buffer is empty
 */
static inline bool RingBuffer_Get(RingBufferType *obj, void *pElem, uint32_t size)
{
    uint32_t       i = 0;
    unsigned char *src;
    unsigned char *dest;

    /* Check if the buffer is empty */
    if(obj->usedNum <= 0)
    {
        return false;
    }

    /* Copy the element */
    dest = (unsigned char *)pElem;
    src  = (unsigned char *)obj->pBuffer + (obj->readIndex * size);
    for(i = 0; i < size; i++)
    {
        *dest++ = *src++;
    }

    /* Move the index */
    obj->readIndex++;
    if(obj->readIndex >= obj->totalNum)
    {
        obj->readIndex = 0;
    }
    obj->usedNum--;

    return true;
}

/*! \brief See if the ringbuffer is full.
 *
 * This function gets if the ringbuffer is full
 *
 * \param[in] obj : pointer to RingBuffer instance
 * \return whether the buffer is full
 *         true : the buffer is full
 *         false : the buffer is not full
 */
static inline bool RingBuffer_IsFull(const RingBufferType *obj)
{
    return (obj->usedNum >= obj->totalNum);
}

/*! \brief See if the ringbuffer is empty.
 *
 * This function gets if the ringbuffer is empty
 *
 * \param[in] obj : pointer to RingBuffer instance
 * \return whether the buffer is empty
 *         true : the buffer is empty
 *         false : the buffer is not empty
 */
static inline bool RingBuffer_IsEmpty(const RingBufferType *obj)
{
    return (obj->usedNum <= 0);
}

#ifdef __cplusplus
}
#endif /* extern "C" */

#endif /* _RINGBUFFER_H_ */
