/*
 * serial.cpp
 *
 *  Created on: Jun 23, 2023
 *      Author: Carst
 */

#include <cstdint>
#include <cstdio>
#include <errno.h>
#include <sys/stat.h>
#include <CLI.h>
#include <stm32g0xx_ll_gpio.h>
#include "stm32g0xx_ll_lpuart.h"

static char new_char = '\0';

extern "C" int _getpid(void)
{
  return 1;
}

extern "C" int _kill(int pid, int sig)
{
  (void)pid;
  (void)sig;
  errno = EINVAL;
  return -1;
}

extern "C" int _write(int file, char *ptr, int len)
{
  (void)file;
  int DataIdx;

  for (DataIdx = 0; DataIdx < len; DataIdx++)
  {
    while(!LL_LPUART_IsActiveFlag_TXE_TXFNF(LPUART1))
    {
    }
    LL_LPUART_TransmitData8(LPUART1, static_cast<std::uint8_t>(*ptr++));
  }
  return len;
}

extern "C" int _close(int file)
{
  (void)file;
  return -1;
}

extern "C" int _lseek(int file, int ptr, int dir)
{
  (void)file;
  (void)ptr;
  (void)dir;
  return 0;
}

extern "C" int _read(int file, char *ptr, int len)
{
#if 0
  (void)file;
  int DataIdx;

  for (DataIdx = 0; DataIdx < len; DataIdx++)
  {
    *ptr++ = __io_getchar();
  }

  return len;
#else
  return 0;
#endif
}

extern "C" int _fstat(int file, struct stat *st)
{
  (void)file;
  st->st_mode = S_IFCHR;
  return 0;
}

extern "C" int _isatty(int file)
{
  (void)file;
  return 1;
}

extern "C" void USART3_4_LPUART1_IRQHandler(void)
{
  if(LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1))
  {
    new_char = LL_LPUART_ReceiveData8(LPUART1);
  }
}

extern "C" void serial_init(void)
{
  /**LPUART1 GPIO Configuration
  PA2   ------> LPUART1_TX
  PA3   ------> LPUART1_RX
  */
  LL_GPIO_InitTypeDef GPIO_InitStruct =
  {
    .Pin = LL_GPIO_PIN_2,
    .Mode = LL_GPIO_MODE_ALTERNATE,
    .Speed = LL_GPIO_SPEED_FREQ_LOW,
    .OutputType = LL_GPIO_OUTPUT_PUSHPULL,
    .Pull = LL_GPIO_PULL_NO,
    .Alternate = LL_GPIO_AF_6,
  };
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = LL_GPIO_PIN_3;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);


  LL_LPUART_InitTypeDef LPUART_InitStruct =
  {
    .PrescalerValue = LL_LPUART_PRESCALER_DIV1,
    .BaudRate = 115200,
    .DataWidth = LL_LPUART_DATAWIDTH_8B,
    .StopBits = LL_LPUART_STOPBITS_1,
    .Parity = LL_LPUART_PARITY_NONE,
    .TransferDirection = LL_LPUART_DIRECTION_TX_RX,
    .HardwareFlowControl = LL_LPUART_HWCONTROL_NONE,
  };
  LL_LPUART_Init(LPUART1, &LPUART_InitStruct);
  LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8);
  LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8);
  LL_LPUART_EnableFIFO(LPUART1);

  LL_LPUART_Enable(LPUART1);
  LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);

  /* LPUART1 interrupt Init */
  NVIC_SetPriority(USART3_4_LPUART1_IRQn, 2);
  NVIC_EnableIRQ(USART3_4_LPUART1_IRQn);

  /* Polling LPUART1 initialisation */
  while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1))))
  {
  }

  setbuf(stdout, NULL);
}

extern "C" void serial_cyclic(void)
{
  char c = new_char;
  new_char = '\0';
  CLI_CylicFunction(c);
}