/**
 * @file uart_ring_buffer.c
 * @brief 字节环形接收缓冲区的实现。
 * @details 适用于中断接收（写）与主循环解析（读）的典型串口场景；
 *          采用“预留一格”区分空/满，最大可用容量为 UART_RX_BUFFER_SIZE-1。
 * @ingroup RingBuffer
 */
#include "uart_ring_buffer.h"
#include "gd32e23x.h"

static volatile uint8_t uart_rx_buffer[UART_RX_BUFFER_SIZE];
static volatile uint8_t write_index = 0;
static volatile uint8_t read_index = 0;
static volatile uint32_t dropped_bytes = 0;

static inline uint32_t irq_save(void) {
    uint32_t primask = __get_PRIMASK();
    __disable_irq();
    return primask;
}

static inline void irq_restore(uint32_t primask) {
    if (primask == 0U) {
        __enable_irq();
    }
}

/**
 * @brief 重置环形缓冲区状态。
 * @details 将读指针、写指针与丢弃计数清零，不清空数据区内容。
 * @note 内部工具函数；对外请优先使用 uart_ring_buffer_init()/uart_ring_buffer_clear()。
 * @ingroup RingBuffer
 */
static void uart_ring_buffer_reset_state(void) {
    write_index = 0;
    read_index = 0;
    dropped_bytes = 0;
}

/**
 * @brief 初始化环形缓冲区。
 * @details 调用内部重置逻辑，复位读写索引与丢弃计数，准备接收数据。
 * @note 若在中断环境使用，初始化前建议关闭相关接收中断以避免并发竞争。
 * @ingroup RingBuffer
 */
void uart_ring_buffer_init(void) {
    uint32_t irq_key = irq_save();
    uart_ring_buffer_reset_state();
    irq_restore(irq_key);
}

/**
 * @brief 获取当前可读的字节数。
 * @details 通过读/写指针的快照计算可读长度，范围为 [0, UART_RX_BUFFER_SIZE-1]。
 * @return uint8_t 可读字节数。
 * @note 预留一个空槽区分“空/满”，因此满时返回 UART_RX_BUFFER_SIZE-1。
 * @ingroup RingBuffer
 */
uint8_t uart_ring_buffer_available(void) {
    /* 使用快照减少并发不一致窗口 */
    uint8_t w = write_index;
    uint8_t r = read_index;
    return (uint8_t)((w + UART_RX_BUFFER_SIZE - r) % UART_RX_BUFFER_SIZE);
}

/**
 * @brief 从环形缓冲区读取一个字节。
 * @details 若缓冲区非空，返回队头字节并推进读指针；若为空，返回 -1。
 * @return int 读取到的字节（0..255），或 -1 表示缓冲区为空。
 * @ingroup RingBuffer
 */
int uart_ring_buffer_get(void) {
    if (read_index == write_index) return -1; // 空
    uint8_t data = uart_rx_buffer[read_index];
    read_index = (read_index + 1) % UART_RX_BUFFER_SIZE;
    return data;
}

/**
 * @brief 向环形缓冲区写入一个字节。
 * @details 尝试写入一个新字节；若缓冲区已满则丢弃并计数。
 * @param data 待写入的字节。
 * @return bool 是否写入成功。
 * @retval true  写入成功。
 * @retval false 写入失败（缓冲区已满，数据被丢弃并计数）。
 * @note 如需“覆盖写入”策略，可在满时先推进读指针再写入。
 * @ingroup RingBuffer
 */
bool uart_ring_buffer_put(uint8_t data) {
    uint8_t next = (write_index + 1) % UART_RX_BUFFER_SIZE;
    if (next != read_index) { // 缓冲区未满
        uart_rx_buffer[write_index] = data;
        write_index = next;
        return true;
    } else {
        /* 缓冲区已满，丢弃新字节并计数 */
        dropped_bytes++;
        return false;
    }
}

/**
 * @brief 清空环形缓冲区。
 * @details 复位读写索引与丢弃计数，相当于逻辑上丢弃所有已接收数据，不擦除数据区内容。
 * @ingroup RingBuffer
 */
void uart_ring_buffer_clear(void) {
    uint32_t irq_key = irq_save();
    uart_ring_buffer_reset_state();
    irq_restore(irq_key);
}

/**
 * @brief 获取因满而被丢弃的字节累计数量。
 * @details 写入时缓冲区满会丢弃新字节并累加计数；该计数在 init/clear 时清零。
 * @return uint32_t 丢弃的累计字节数。
 * @ingroup RingBuffer
 */
uint32_t uart_ring_buffer_drop_count(void) {
    return dropped_bytes;
}
