#include "boot_uart.h"
#include "boot_gpio.h"

UART_HandleTypeDef huart_boot;

/**
  * @brief  Инициализация UART для бутлоадера
  */
void MX_BOOT_UART_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* Включаем тактирование */
    __RCC_UART_BOOT_CLK_ENABLE();
    __RCC_UART_PORT_CLK_ENABLE();

    /* Настройка GPIO TX/RX */
    GPIO_InitStruct.Pin = UART_PIN_TX;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(UART_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = UART_PIN_RX;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(UART_PORT, &GPIO_InitStruct);

    /* Настройка UART */
    huart_boot.Instance = UART_BOOT;
    huart_boot.Init.BaudRate = UART_SPEED;
    huart_boot.Init.WordLength = UART_WORDLENGTH_8B;
    huart_boot.Init.StopBits = UART_STOPBITS_1;
    huart_boot.Init.Parity = UART_PARITY_NONE;
    huart_boot.Init.Mode = UART_MODE_TX_RX;
    huart_boot.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart_boot.Init.OverSampling = UART_OVERSAMPLING_16;

    if (HAL_UART_Init(&huart_boot) != HAL_OK)
    {
        Error_Handler(); // твоя функция обработки ошибок
    }
}

/**
  * @brief  Приём команды по UART по протоколу:
  *         [SOH][CMD][LEN_H][LEN_L][DATA ...][CRC32_H..CRC32_L]
  *         Idle — не блокируется до SOH
  *         После SOH — блокирующий приём всего пакета
  * @param  bl: указатель на структуру бутлоадера
  * @retval BootloaderCommand_t — принятая команда или NO_CMD
  */
BootloaderCommand_t Bootloader_UART_Receive(Bootloader_t *bl)
{
  BootloaderCommand_t cmd;
  uint8_t byte = 0;
  HAL_StatusTypeDef res;

  // -----------------------------
  // 1. Ждём SOH в неблокирующем режиме
  res = HAL_UART_Receive(bl->huart, &byte, 1, 1); // 1 ms таймаут
  if(res != HAL_OK)
    return NO_CMD; // пакета нет

  if(byte != 0xAA)
    return NO_CMD; // игнорируем мусор

  // -----------------------------
  // 2. Блокирующий приём CMD + LEN_H + LEN_L
  uint8_t header[3];
  res = HAL_UART_Receive(bl->huart, header, 3, FW_RECEIVE_TIMEOUT_MS);
  if(res != HAL_OK)
  {
    bl->error.bit.timeout_receive = 1;
    bl->state = BL_STATE_ERROR;
    return NO_CMD;
  }

  cmd = (BootloaderCommand_t)header[0];
  bl->fw_len = ((uint16_t)header[1] << 8) | header[2];

  if(bl->fw_len > PAGE_SIZE)
  {
    bl->error.bit.overflow = 1;
    bl->state = BL_STATE_ERROR;
    return NO_CMD;
  }

  // -----------------------------
  // 3. Блокирующий приём DATA
  for(uint16_t i = 0; i < bl->fw_len; i++)
  {
    res = HAL_UART_Receive(bl->huart, &bl->fw_buffer[i], 1, FW_RECEIVE_TIMEOUT_MS);
    if(res != HAL_OK)
    {
      bl->error.bit.timeout_receive = 1;
      bl->state = BL_STATE_ERROR;
      return NO_CMD;
    }
  }

  // -----------------------------
  // 4. Приём CRC32
  uint8_t crc_buf[4];
  res = HAL_UART_Receive(bl->huart, crc_buf, 4, FW_RECEIVE_TIMEOUT_MS);
  if(res != HAL_OK)
  {
    bl->error.bit.timeout_receive = 1;
    bl->state = BL_STATE_ERROR;
    return NO_CMD;
  }
  bl->fw_crc = (crc_buf[0]<<24) | (crc_buf[1]<<16) | (crc_buf[2]<<8) | crc_buf[3];

  // -----------------------------
  // 5. Проверка CRC по всему пакету (CMD + LEN + DATA)
  uint32_t crc_calc = CRC32_Compute(header, 3);              // CMD + LEN
  crc_calc = CRC32_Compute(bl->fw_buffer, bl->fw_len) ^ crc_calc; // DATA

  if(crc_calc != bl->fw_crc)
  {
    bl->error.bit.crc_err = 1;
    bl->state = BL_STATE_ERROR;
    return NO_CMD;
  }

  // -----------------------------
  // 6. Всё верно — возвращаем команду
  return cmd;
}


/**
  * @brief  Приём UART: страница + CRC
  * @param  bl: указатель на структуру бутлоадера
  */
void Bootloader_UART_Receive_Page(Bootloader_t *bl)
{
  uint16_t bytes_received = 0;
  uint8_t crc_buf[4];
  HAL_StatusTypeDef res;
  uint32_t start_tick = HAL_GetTick(); // старт таймера

  // Приём данных страницы
  while(bytes_received < PAGE_SIZE)
  {
    uint8_t byte = 0;
    res = HAL_UART_Receive(bl->huart, &byte, 1, FW_RECEIVE_TIMEOUT_MS); // блокирующий приём 100ms

    if(res == HAL_OK)
    {
      bl->fw_buffer[bytes_received++] = byte;
      start_tick = HAL_GetTick(); // сброс таймера при успешном приёме
      LED_BOOT_TOOGLE();
    }
    else
    {
      bl->error.bit.timeout_receive = 1;
      bl->state = BL_STATE_ERROR;  // превышен таймаут
      return;
    }
  }

  // Приём CRC (4 байта)
  for(uint8_t i = 0; i < 4; i++)
  {
    res = HAL_UART_Receive(bl->huart, &crc_buf[i], 1, FW_RECEIVE_TIMEOUT_MS);
    if(res == HAL_OK)
    {
      start_tick = HAL_GetTick(); // сброс таймера
    }
    else
    {
      bl->error.bit.timeout_receive = 1;
      bl->state = BL_STATE_ERROR;
      return;
    }
  }

  // Сохраняем CRC в структуру
  bl->fw_crc = (crc_buf[0] << 24) | (crc_buf[1] << 16) | (crc_buf[2] << 8) | crc_buf[3];
  bl->fw_len = bytes_received;
  bl->state = BL_STATE_IDLE;
}
