#ifndef __BOOTLOADER_H
#define __BOOTLOADER_H

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


#define BL_KEY_APP_WRITTEN 0xAAAA5555
/* --- Настройка: подставьте значения для вашей MCU --- */
/* Адрес начала приложения (используется в вашем коде) */
#ifndef MAIN_APP_START_ADR
#error "MAIN_APP_START_ADR must be defined"
#endif

/* Если нужен другой полином/поведение CRC, используйте вашу функцию CRC32_Compute.
   В вашем коде уже есть CRC32_Compute, поэтому будем её использовать. */
   
   

#define GetErrorCode() BKP->DR1
#define GetErrorCnt()  BKP->DR2


#define SaveErrorCode(code) do{ \
  RCC->APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN); \
  PWR->CR |= PWR_CR_DBP; \
  GetErrorCode() = code; \
  GetErrorCnt() = GetErrorCnt() + 1; \
}while(0u);

#define ClearErrorCode(code) do{ \
  RCC->APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN); \
  PWR->CR |= PWR_CR_DBP; \
  GetErrorCode() = 0; \
  GetErrorCnt() = 0; \
}while(0u);


// ERROR DEFINES
/**
 * @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;
    unsigned memmanage_cycle:1;
    unsigned watchdog_reset:1;
    unsigned unknown_cmd:1;
    unsigned erase_err:1;
    unsigned write_err:1;
    unsigned verify_err:1;
    unsigned timeout_receive:1;
    unsigned crc_err:1;
  }bit;
} BootloaderError_t;


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;

extern uint32_t TxMailBoxBoot;
extern uint8_t TXDataBoot[8];

void ResetKey(void);
void JumpToBootloader(void);

void App_Init(void);
void Bootloader_Task(Bootloader_t *bl);

void Error_Handler(void);

#endif //__BOOTLOADER_H