/*
 * kl_phy.c
 * Physical layer: UART driver, state-based echo suppression, RX FIFO.
 */

#include "kl_phy.h"
#include "main.h"
#include <string.h>

extern UART_HandleTypeDef huart1;

static KL_Phy phy;

/* ================================================================
 * RX FIFO (single-producer ISR, single-consumer main loop)
 * ================================================================ */
static void rx_fifo_push(uint8_t b)
{
    uint16_t next = (uint16_t)((phy.rx_head + 1U) % KL_RX_FIFO_SIZE);
    if (next == phy.rx_tail) {
        /* Overflow: drop oldest */
        phy.rx_tail = (uint16_t)((phy.rx_tail + 1U) % KL_RX_FIFO_SIZE);
    }
    phy.rx_fifo[phy.rx_head] = b;
    phy.rx_head = next;
}

int KL_Phy_RxPop(uint8_t *out)
{
    if (phy.rx_head == phy.rx_tail) return 0;
    if (out) *out = phy.rx_fifo[phy.rx_tail];
    phy.rx_tail = (uint16_t)((phy.rx_tail + 1U) % KL_RX_FIFO_SIZE);
    return 1;
}

int KL_Phy_RxAvailable(void)
{
    return (phy.rx_head != phy.rx_tail) ? 1 : 0;
}

void KL_Phy_RxFlush(void)
{
    phy.rx_tail = phy.rx_head;
}

/* ================================================================
 * GPIO pin configuration helpers
 * ================================================================ */
static void phy_set_pins_uart_af(void)
{
    GPIO_InitTypeDef gi = {0};
    __HAL_RCC_GPIOA_CLK_ENABLE();

    gi.Pin       = KL_TX_PIN | KL_RX_PIN;
    gi.Mode      = GPIO_MODE_AF_PP;
    gi.Pull      = GPIO_PULLUP;
    gi.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;
    gi.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(KL_GPIO_PORT, &gi);
}

/* ================================================================
 * Init / Mode switching
 * ================================================================ */
void KL_Phy_Init(void)
{
    memset(&phy, 0, sizeof(phy));
    phy.tx_state = KL_PHY_TX_IDLE;
    HAL_UART_DeInit(&huart1);
}

void KL_Phy_EnableUart9600(void)
{
    HAL_UART_DeInit(&huart1);
    phy_set_pins_uart_af();

    huart1.Instance          = USART1;
    huart1.Init.BaudRate     = 9600;
    huart1.Init.WordLength   = UART_WORDLENGTH_8B;
    huart1.Init.StopBits     = UART_STOPBITS_1;
    huart1.Init.Parity       = UART_PARITY_NONE;
    huart1.Init.Mode         = UART_MODE_TX_RX;
    huart1.Init.HwFlowCtl    = UART_HWCONTROL_NONE;
    huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    if (HAL_UART_Init(&huart1) != HAL_OK) {
        Error_Handler();
    }

    KL_Phy_RxFlush();
    phy.tx_state     = KL_PHY_TX_IDLE;
    phy.echo_pending = 0;

    HAL_UART_Receive_IT(&huart1, phy.rx_it_buf, 1);
}

void KL_Phy_DisableUart(void)
{
    HAL_UART_DeInit(&huart1);
    phy.tx_state     = KL_PHY_TX_IDLE;
    phy.echo_pending = 0;
}

/* ================================================================
 * Non-blocking single-byte TX
 * ================================================================ */
int KL_Phy_TxByte(uint8_t byte)
{
    if (phy.tx_state != KL_PHY_TX_IDLE) return 0;

    phy.tx_byte     = byte;
    phy.last_tx_byte = byte;
    phy.echo_pending = 1;
    phy.tx_state     = KL_PHY_TX_SENDING;

    if (HAL_UART_Transmit_IT(&huart1, &phy.tx_byte, 1) != HAL_OK) {
        phy.tx_state     = KL_PHY_TX_IDLE;
        phy.echo_pending = 0;
        return 0;
    }
    return 1;
}

int KL_Phy_TxBusy(void)
{
    return (phy.tx_state != KL_PHY_TX_IDLE) ? 1 : 0;
}

/* ================================================================
 * Service: echo-timeout recovery
 * ================================================================ */
void KL_Phy_Service(void)
{
    if (phy.tx_state == KL_PHY_TX_ECHO_WAIT) {
        if (kl_tick_diff(HAL_GetTick(), phy.tx_done_tick) >= (int32_t)KL_ECHO_TMO_MS) {
            phy.tx_state     = KL_PHY_TX_IDLE;
            phy.echo_pending = 0;
        }
    }
}

/* ================================================================
 * ISR callbacks
 * ================================================================ */
void KL_Phy_TxCpltCB(void)
{
    phy.tx_done_tick = HAL_GetTick();
    phy.tx_state     = KL_PHY_TX_ECHO_WAIT;
}

void KL_Phy_RxCpltCB(void)
{
    uint8_t byte = phy.rx_it_buf[0];

    if (phy.echo_pending && byte == phy.last_tx_byte) {
        phy.echo_pending = 0;
        phy.tx_state     = KL_PHY_TX_IDLE;
    } else {
        rx_fifo_push(byte);
    }

    HAL_UART_Receive_IT(&huart1, phy.rx_it_buf, 1);
}
