/* SPDX-License-Identifier: Apache-2.0 */
/******************************************************************************/
/**                                                                          */
/** RK0 - The Embedded Real-Time Kernel '0'                                  */
/** (C) 2026 Antonio Giacomelli <dev@kernel0.org>                            */
/**                                                                          */
/** VERSION: V0.60.0                                                         */
/**                                                                          */
/******************************************************************************/

#include <main.h>
#include <stm32f1xx_hal.h>
#include <ktrace.h>

extern UART_HandleTypeDef huart2;

#if (RK_CONF_TRACE == ON)
#define TRACE_UART_RX_BUF_LEN 64U
#if ((TRACE_UART_RX_BUF_LEN & (TRACE_UART_RX_BUF_LEN - 1U)) != 0U)
#error "TRACE_UART_RX_BUF_LEN must be a power of two"
#endif

static volatile uint8_t traceUartRxBuf[TRACE_UART_RX_BUF_LEN];
static volatile uint32_t traceUartRxHead;
static volatile uint32_t traceUartRxTail;
#endif

int __io_putchar(int ch)
{
    while ((huart2.Instance->SR & UART_FLAG_TXE) == 0U)
    {
    }
    huart2.Instance->DR = (uint8_t)ch;
    while ((huart2.Instance->SR & UART_FLAG_TC) == 0U)
    {
    }
    return ch;
}

void kPutc(char const c)
{
    (void)__io_putchar((unsigned char)c);
}

void kPuts(char const *str)
{
    while (*str != '\0')
    {
        kPutc(*str);
        str++;
    }
}

int _write(int file, char const *ptr, int len)
{
    (void)file;
    __disable_irq();
    for (int i = 0; i < len; i++)
    {
        (void)__io_putchar((unsigned char)ptr[i]);
    }
    __enable_irq();
    return len;
}

#if (RK_CONF_TRACE == ON)
int kTraceUartGetc(char *chPtr)
{
    if (chPtr == NULL)
    {
        return (0);
    }

    uint32_t const primask = __get_PRIMASK();
    __disable_irq();

    if (traceUartRxHead == traceUartRxTail)
    {
        __set_PRIMASK(primask);
        return (0);
    }

    *chPtr = (char)traceUartRxBuf[traceUartRxTail];
    traceUartRxTail = (traceUartRxTail + 1U) & (TRACE_UART_RX_BUF_LEN - 1U);

    __set_PRIMASK(primask);
    return (1);
}

void kTraceUartRxEnable(void)
{
    uint32_t const primask = __get_PRIMASK();
    __disable_irq();

    traceUartRxHead = 0U;
    traceUartRxTail = 0U;
    __HAL_UART_CLEAR_OREFLAG(&huart2);
    __HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);
    HAL_NVIC_SetPriority(USART2_IRQn, 6U, 0U);
    HAL_NVIC_EnableIRQ(USART2_IRQn);

    __set_PRIMASK(primask);
}

void USART2_IRQHandler(void)
{
    uint32_t const sr = huart2.Instance->SR;

    if ((sr & (USART_SR_PE | USART_SR_FE | USART_SR_NE | USART_SR_ORE |
               USART_SR_RXNE)) == 0U)
    {
        return;
    }

    uint8_t const ch = (uint8_t)(huart2.Instance->DR & 0xFFU);

    if ((sr & USART_SR_RXNE) != 0U)
    {
        uint32_t const nextHead =
            (traceUartRxHead + 1U) & (TRACE_UART_RX_BUF_LEN - 1U);
        if (nextHead != traceUartRxTail)
        {
            traceUartRxBuf[traceUartRxHead] = ch;
            traceUartRxHead = nextHead;
        }
        kTraceInputSignalFromISR();
    }
}
#endif
