/**
 *******************************************************************************
 * @file  console_ringbuf.c
 * @brief This file provides firmware functions to manage the ring buffer.
 @verbatim
   Change Logs:
   Date             Author          Notes
   2022-06-30       CDT             First version
 @endverbatim
 *******************************************************************************
 * Copyright (C) 2022-2023, Xiaohua Semiconductor Co., Ltd. All rights reserved.
 *
 * This software component is licensed by XHSC under BSD 3-Clause license
 * (the "License"); You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                    opensource.org/licenses/BSD-3-Clause
 *
 *******************************************************************************
 */

/*******************************************************************************
 * Include files
 ******************************************************************************/
#include "console_conf.h"
#include "console_ringbuf.h"

/**
 * @addtogroup Serial_Port_Console
 * @{
 */

/**
 * @defgroup Ring_Buffer Ring Buffer
 * @{
 */

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/

/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/

/*******************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/
/**
 * @defgroup Ring_Buffer_Global_Functions Ring Buffer Global Functions
 * @{
 */

/**
 * @brief  Create ring buffer.
 * @param  [in] u32Size             Data buffer size
 * @retval Pointer to ring buffer @ref stc_ring_buf_t structure.
 */
stc_ring_buf_t *RINGBUF_Create(uint32_t u32Size)
{
    stc_ring_buf_t *pstcBuf = NULL;

    if (u32Size != 0UL) {
        pstcBuf = (stc_ring_buf_t *)malloc(sizeof(stc_ring_buf_t) + u32Size);
        if (pstcBuf != NULL) {
            pstcBuf->pu8Data = (uint8_t *)pstcBuf + sizeof(stc_ring_buf_t);
            pstcBuf->u32In = 0;
            pstcBuf->u32Out = 0;
            pstcBuf->u32Size = u32Size;
            pstcBuf->u32FreeSize = u32Size;
        }
    }

    return pstcBuf;
}

/**
 * @brief  Initialize ring buffer.
 * @param  [in] pstcBuf             Pointer to a @ref stc_ring_buf_t structure
 * @param  [in] pu8Data             Data buffer
 * @param  [in] u32Size             Data buffer size
 * @retval int32_t:
 *           - OPCODE_OK:           Initialize successfully.
 *           - OPCODE_ERR:          If one of following cases matches:
 *                                  - The pointer pstcBuf value is NULL.
 *                                  - The pointer pu8Data value is NULL.
 *                                  - The u32Size value is 0.
 */
int32_t RINGBUF_Init(stc_ring_buf_t *pstcBuf, uint8_t *pu8Data, uint32_t u32Size)
{
    int32_t i32Ret = OPCODE_ERR;

    if ((pstcBuf != NULL) && (pu8Data != NULL) && (u32Size != 0UL)) {
        pstcBuf->pu8Data = pu8Data;
        pstcBuf->u32In = 0;
        pstcBuf->u32Out = 0;
        pstcBuf->u32Size = u32Size;
        pstcBuf->u32FreeSize = u32Size;
        i32Ret = OPCODE_OK;
    }

    return i32Ret;
}

/**
 * @brief  Ring buffer free size.
 * @param  [in] pstcBuf             Pointer to a @ref stc_ring_buf_t structure
 * @retval Ring buffer free size
 */
uint32_t RINGBUF_FreeSize(const stc_ring_buf_t *pstcBuf)
{
    return pstcBuf->u32FreeSize;
}

/**
 * @brief  Ring buffer used size.
 * @param  [in] pstcBuf             Pointer to a @ref stc_ring_buf_t structure
 * @retval Ring buffer used size
 */
uint32_t RINGBUF_UsedSize(const stc_ring_buf_t *pstcBuf)
{
    return (pstcBuf->u32Size - pstcBuf->u32FreeSize);
}

/**
 * @brief  Ring buffer full.
 * @param  [in] pstcBuf             Pointer to a @ref stc_ring_buf_t structure
 * @retval Ring buffer status
 */
bool RINGBUF_IsFull(const stc_ring_buf_t *pstcBuf)
{
    return (0UL == pstcBuf->u32FreeSize);
}

/**
 * @brief  Check ring buffer empty.
 * @param  [in] pstcBuf             Pointer to a @ref stc_ring_buf_t structure
 * @retval Ring buffer status
 */
bool RINGBUF_IsEmpty(const stc_ring_buf_t *pstcBuf)
{
    return (pstcBuf->u32Size == pstcBuf->u32FreeSize);
}

/**
 * @brief  Read ring buffer.
 * @param  [in] pstcBuf             Pointer to a @ref stc_ring_buf_t structure
 * @param  [in] au8Data             Pointer to data buffer to read
 * @param  [in] u32Len              Data length
 * @retval Read length
 */
uint32_t RINGBUF_Read(stc_ring_buf_t *pstcBuf, uint8_t au8Data[], uint32_t u32Len)
{
    uint32_t u32SR;
    uint32_t u32CopyLen;
    uint32_t u32ReadLen = 0UL;

    if ((pstcBuf != NULL) && (au8Data != NULL) && (u32Len != 0UL)) {
        u32ReadLen = RINGBUF_UsedSize(pstcBuf);
        if (0UL != u32ReadLen) {
            if (u32ReadLen >= u32Len) {
                u32ReadLen = u32Len;
            }

            if (pstcBuf->u32Out + u32ReadLen <= pstcBuf->u32Size) {
                (void)memcpy(au8Data, &pstcBuf->pu8Data[pstcBuf->u32Out], u32ReadLen);
            } else {
                u32CopyLen = pstcBuf->u32Size - pstcBuf->u32Out;
                (void)memcpy(&au8Data[0], &pstcBuf->pu8Data[pstcBuf->u32Out], u32CopyLen);
                (void)memcpy(&au8Data[u32CopyLen], &pstcBuf->pu8Data[0], u32ReadLen - u32CopyLen);
            }

            u32SR = CRITICAL_ZONE_ENTER();
            pstcBuf->u32FreeSize += u32ReadLen;
            pstcBuf->u32Out += u32ReadLen;
            if (pstcBuf->u32Out >= pstcBuf->u32Size) {
                pstcBuf->u32Out %= pstcBuf->u32Size;
            }
            CRITICAL_ZONE_EXIT(u32SR);
        }
    }

    return u32ReadLen;
}

/**
 * @brief  Write ring buffer.
 * @param  [in] pstcBuf             Pointer to a @ref stc_ring_buf_t structure
 * @param  [in] au8Data             Pointer to data buffer to write
 * @param  [in] u32Len              Data length
 * @retval Write length
 */
uint32_t RINGBUF_Write(stc_ring_buf_t *pstcBuf, uint8_t au8Data[], uint32_t u32Len)
{
    uint32_t u32SR;
    uint32_t u32CopyLen;
    uint32_t u32WriteLen = 0UL;

    if ((pstcBuf != NULL) && (au8Data != NULL) && (u32Len != 0UL)) {
        u32WriteLen = RINGBUF_FreeSize(pstcBuf);

        if (u32Len <= u32WriteLen) {
            u32WriteLen = u32Len;
        }

        if (pstcBuf->u32In + u32WriteLen <= pstcBuf->u32Size) {
            (void)memcpy(&pstcBuf->pu8Data[pstcBuf->u32In], au8Data, u32WriteLen);
        } else {
            u32CopyLen = pstcBuf->u32Size - pstcBuf->u32In;
            (void)memcpy(&pstcBuf->pu8Data[pstcBuf->u32In], &au8Data[0], u32CopyLen);
            (void)memcpy(&pstcBuf->pu8Data[0], &au8Data[u32CopyLen], u32WriteLen - u32CopyLen);
        }

        u32SR = CRITICAL_ZONE_ENTER();
        pstcBuf->u32FreeSize -= u32WriteLen;
        pstcBuf->u32In += u32WriteLen;
        if (pstcBuf->u32In >= pstcBuf->u32Size) {
            pstcBuf->u32In %= pstcBuf->u32Size;
        }
        CRITICAL_ZONE_EXIT(u32SR);
    }

    return u32WriteLen;
}

/**
 * @}
 */

/**
 * @}
 */

/**
 * @}
 */

/*******************************************************************************
 * EOF (not truncated)
 ******************************************************************************/
