#ifndef __BOOTLOADER_H
#define __BOOTLOADER_H

#include "boot_project_setup.h"
#include "string.h"

/* --- Настройка: подставьте значения для вашей MCU --- */
/* Адрес начала приложения (используется в вашем коде) */
#ifndef MAIN_APP_START_ADR
#error "MAIN_APP_START_ADR must be defined"
#endif
/* Flash boundaries: подставьте реальные границы флеш-памяти вашего MCU */
#ifndef FLASH_START_ADR
#define FLASH_START_ADR   MAIN_APP_START_ADR
#endif
#ifndef FLASH_END_ADR
#define FLASH_END_ADR     FLASH_BASE + (*((uint16_t*)FLASHSIZE_BASE) * 1024U)
#endif

/* SRAM boundaries: подставьте реальные адреса SRAM вашей MCU */
#ifndef SRAM_START_ADR
#define SRAM_START_ADR    0x20000000UL
#endif
#ifndef SRAM_END_ADR
#define SRAM_END_ADR      0x2003FFFFUL
#endif



/** 
 * @brief Значение ключа, указывающее что основное приложение записано
 */
#define BL_KEY_APP_WRITTEN 0xAAAA5555


      

/** @brief Получить сохранённый код ошибки из BKP */
#define GetErrorCode() BKP->DR1
/** @brief Получить счетчик ошибок из BKP */
#define GetErrorCnt()  BKP->DR2


/**
 * @brief Сохранение кода ошибки и инкремент счетчика
 * @param code Код ошибки
 * @details 
 * Включаем тактирование PWR и BKP (APB1) и разрешаем доступ к BKP domain
 * Записываем напрямую в регистры RCC/APB1ENR и PWR->CR
 * Записываем код ошибки и счётчик ошибок
 */
#define SaveErrorCode(code) do{ \
  RCC->APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN); \
  PWR->CR |= PWR_CR_DBP; \
  GetErrorCode() = code; \
  GetErrorCnt() = GetErrorCnt() + 1; \
}while(0u);

/**
 * @brief Очистка кода ошибки и счетчика ошибок
 */
#define ClearErrorCode(code) do{ \
  RCC->APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN); \
  PWR->CR |= PWR_CR_DBP; \
  GetErrorCode() = 0; \
  GetErrorCnt() = 0; \
}while(0u);


/**
 * @brief Команды для управления бутлоадером
 */
typedef enum {
  CMD_ERASE = 0x01,       ///< Команда на стирание прошивки
  CMD_START_RECEIVE,      ///< Команда на старт приема прошивки
  CMD_WRITE,              ///< Команда на запись блока прошивки
  CMD_GOTOAPP,            ///< Команда на переход в приложение
  CMD_RESET,              ///< Команда на переход в приложение
  CMD_GOTOBOOT,           ///< Команда на переход в приложение
}BootloaderCommand_t;

/**
 * @brief Состояния конечного автомата бутлоадера
 */
typedef enum {
  BL_STATE_INIT = 0,      ///< Состояние: инициализация
  BL_STATE_JUMP_TO_APP,   ///< Состояние: запуск приложения
  BL_STATE_IDLE,          ///< Состояние: ожидание команд
  BL_STATE_ERASE,         ///< Состояние: стирание флеша
  BL_STATE_RECEIVE_UART,  ///< Состояние: прием прошивки по UART
  BL_STATE_RECEIVE_CAN,   ///< Состояние: прием прошивки по CAN
  BL_STATE_WRITE,         ///< Состояние: запись данных
  BL_STATE_ERROR,         ///< Состояние: ошибка
  BL_STATE_RESET,         ///< Состояние: сброс контролллера
  BL_STATE_JUMP_TO_BOOT,  ///< Состояние: запуск приложения
} BootloaderState_t;

/**
 * @brief Ошибки бутлоадера
 */
typedef union {
  uint16_t all;           ///< Все ошибки одним числом
  struct {
    unsigned hardfault_cycle:1; ///< Прерывание HardFault
    unsigned memmanage_cycle:1; ///< Прерывание MemManage
    unsigned watchdog_reset:1;  ///< Watchdog сброс
    unsigned unknown_cmd:1;     ///< Неизвестная команда
    unsigned erase_err:1;       ///< Ошибка стирания
    unsigned write_err:1;       ///< Ошибка записи
    unsigned verify_err:1;      ///< Ошибка проверки прошивки
    unsigned timeout_receive:1; ///< Таймаут приёма
    unsigned crc_err:1;         ///< Ошибка CRC
  } bit;
} BootloaderError_t;


/**
* @brief Дескриптор бутлоадера
 */
typedef struct {
  BootloaderState_t   state;                ///< текущее состояние бутлоадера
  BootloaderError_t   error;
  
  uint32_t            addr;                 ///< текущий адрес прошивки

  uint8_t             fw_size;              ///< размер прошивки
  uint8_t             fw_buffer[PAGE_SIZE]; ///< буфер для приема прошивки (UART/CAN)
  uint32_t            fw_len;               ///< длина принятого пакета
  uint32_t            fw_crc;               ///< контрольная сумма прошивки

  UART_HandleTypeDef  *huart;               ///< хендлер UART
  CAN_HandleTypeDef   *hcan;                ///< хендер CAN
  CAN_TxHeaderTypeDef TxHeader;             ///< Заголовок CAN сообщения для отправки
  BootloaderState_t   prev_state;           ///< предыдущее состояние бутлоадера
} Bootloader_t;

/* Основная задача бутлоадера */
void Bootloader_Task(Bootloader_t *bl);

/* Настройка тактирования */
void Boot_SystemClock_Config(void);
/* Хендлер ошибки */
void Error_Handler(void);

#endif //__BOOTLOADER_H