#include "usart.hpp"

#include <stdint.h>

#include <string_view>

#include "pindef.hpp"
#include "stm32f4xx_hal_uart.h"

namespace driver::usart {

Usart::Usart(USART_TypeDef* usart, uint32_t baudRate, uint32_t wordLength, uint32_t stopBits,
             uint32_t parity, uint32_t mode, uint32_t hwFlowCtl, uint32_t overSampling)
    : mHandle{.Instance = usart,
              .Init{.BaudRate     = baudRate,
                    .WordLength   = wordLength,
                    .StopBits     = stopBits,
                    .Parity       = parity,
                    .Mode         = mode,
                    .HwFlowCtl    = hwFlowCtl,
                    .OverSampling = overSampling}} {}

bool Usart::init() {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if (mHandle.Instance == USART2) {
        // USART2 clock enable
        __HAL_RCC_USART2_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();
        // USART2 GPIO Configuration
        //   PA2 -> USART2_TX
        //   PA3 -> USART2_RX

        GPIO_InitStruct.Pin       = USART_TX_Pin | USART_RX_Pin;
        GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull      = GPIO_NOPULL;
        GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }

    return HAL_UART_Init(&mHandle) != HAL_OK;
}

void Usart::deinit() {
    if (mHandle.Instance == USART2) {
        /* Peripheral clock disable */
        __HAL_RCC_USART2_CLK_DISABLE();

        HAL_GPIO_DeInit(GPIOA, USART_TX_Pin | USART_RX_Pin);
    }
}

void Usart::print(const std::string_view str) {
    tx(str);
};

void Usart::println(const std::string_view str) {
    print(str);
    tx("\r\n");
}

void Usart::tx(const std::string_view range) {
    for (uint32_t pt{0}; pt <= range.size(); pt += TX_BUFSIZE) {
        HAL_UART_Transmit(&mHandle, reinterpret_cast<const uint8_t*>(range.begin() + pt),
                          TX_BUFSIZE, TX_TIMEOUT_MS);
    }
};

}  // namespace driver::usart