#include "boot_can.h"

CAN_HandleTypeDef hcan_boot;

/**
  * @brief  Инициализация CAN для бутлоадера (по defines)
  */
void MX_BOOT_CAN_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    CAN_FilterTypeDef sFilterConfig;

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

    /* Настройка пинов RX/TX */
    GPIO_InitStruct.Pin = CAN_PIN_RX;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(CAN_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = CAN_PIN_TX;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(CAN_PORT, &GPIO_InitStruct);

    /* Настройка CAN */
    hcan_boot.Instance = CAN_BOOT;
    hcan_boot.Init.Prescaler = CAN_SPEED_PRESCALER;
    hcan_boot.Init.Mode = CAN_MODE;
    hcan_boot.Init.SyncJumpWidth = CAN_SJW_1TQ;
    hcan_boot.Init.TimeSeg1 = CAN_SPEED_BS1;
    hcan_boot.Init.TimeSeg2 = CAN_SPEED_BS2;
    hcan_boot.Init.TimeTriggeredMode = DISABLE;
    hcan_boot.Init.AutoBusOff = DISABLE;
    hcan_boot.Init.AutoWakeUp = DISABLE;
    hcan_boot.Init.AutoRetransmission = ENABLE;
    hcan_boot.Init.ReceiveFifoLocked = DISABLE;
    hcan_boot.Init.TransmitFifoPriority = DISABLE;

    if (HAL_CAN_Init(&hcan_boot) != HAL_OK)
    {
        Error_Handler();
    }

    /* Настройка фильтра: пропускать все сообщения */
    sFilterConfig.FilterBank = 0;
    sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
    sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
    sFilterConfig.FilterIdHigh = 0x0000;
    sFilterConfig.FilterIdLow = 0x0000;
    sFilterConfig.FilterMaskIdHigh = 0x0000;
    sFilterConfig.FilterMaskIdLow = 0x0000;
    sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
    sFilterConfig.FilterActivation = ENABLE;

    if (HAL_CAN_ConfigFilter(&hcan_boot, &sFilterConfig) != HAL_OK)
    {
        Error_Handler();
    }

    /* Запускаем CAN */
    if (HAL_CAN_Start(&hcan_boot) != HAL_OK)
    {
        Error_Handler();
    }
}

// -----------------------------
// CAN: приём страницы + CRC с таймаутом
// -----------------------------
void Bootloader_CAN_Receive_Page(Bootloader_t *bl)
{
    uint16_t bytes_received = 0;
    CAN_RxHeaderTypeDef canHeader;
    uint8_t canData[8];
    uint32_t start_tick = HAL_GetTick();

    // Приём страницы прошивки
    while(bytes_received < PAGE_SIZE)
    {
        if(HAL_CAN_GetRxFifoFillLevel(bl->hcan, CAN_RX_FIFO0) > 0)
        {
            if(HAL_CAN_GetRxMessage(bl->hcan, CAN_RX_FIFO0, &canHeader, canData) == HAL_OK)
            {
                uint8_t len = canHeader.DLC;
                if(bytes_received + len > PAGE_SIZE)
                    len = PAGE_SIZE - bytes_received;

                memcpy(&bl->fw_buffer[bytes_received], canData, len);
                bytes_received += len;
                start_tick = HAL_GetTick(); // сброс таймера
            }
        }

        // проверка таймаута
        if(HAL_GetTick() - start_tick >= FW_RECEIVE_TIMEOUT_MS)
        {
            bl->error.bit.timeout_receive = 1;
            bl->state = BL_STATE_ERROR;
            return;
        }
    }

    // Приём CRC (4 байта)
    while(1)
    {
        if(HAL_CAN_GetRxFifoFillLevel(bl->hcan, CAN_RX_FIFO0) > 0)
        {
            if(HAL_CAN_GetRxMessage(bl->hcan, CAN_RX_FIFO0, &canHeader, canData) == HAL_OK)
            {
                // CRC в первых 4 байтах пакета
                bl->fw_crc = (canData[0] << 24) |
                             (canData[1] << 16) |
                             (canData[2] << 8) |
                             canData[3];
                break;
            }
        }

        // Таймаут
        if(HAL_GetTick() - start_tick >= FW_RECEIVE_TIMEOUT_MS)
        {
            bl->error.bit.timeout_receive = 1;
            bl->state = BL_STATE_ERROR;
            return;
        }
    }

    bl->fw_len = PAGE_SIZE;
    bl->state = BL_STATE_IDLE;
}

