#include "board_config.h"
#include "config/version.h"
#include "driver/uart.h"
#include "hal/spi_types.h"
#include "utils/crc.h"

#include "esp_partition.h"
#include "spi_flash_mmap.h"

#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_ERR
#define SYS_LOG_DOMAIN "CFG"
#define CONS_ABORT()
#include "sys_log.h"

#include "board_config/_default_config.h"

typedef struct
{
    uint8_t license_code[CONFIG_LICENSE_CODE_LENGTH]; // 在烧录器设置的保存滚码的字段
    uint32_t crc_32;                                  // 从 size 成员开始到数据结束的 crc32 校验值
    uint16_t size;                                    // 整个数据结构的长度
    uint16_t version;                                 // 仅指本结构的版本代号
    cfg_board_t cfg;                                  // 板载配置数据
} __cfg_board_partition_t;

static const esp_partition_t *s_partition_hdl;
const __cfg_board_partition_t *p_cfg_board_partition;
const cfg_board_t *g_cfg_board = &s_cfg_board_default;
const uint8_t *g_license_code;

static void _read_partition_data(__cfg_board_partition_t *out, const cfg_board_t *cfg);
static const uint8_t *_read_legacy_license_code(void);
static uint32_t _get_data_crc(const __cfg_board_partition_t *cfg);

/**
 * @brief 初始化，使 g_cfg_board 指向正确的
 * @retval 0 指向了自定义分区中的数据
 * @retval 1 在自定义分区中的数据校验失败，指向了默认的配置
 */
int board_cfg_init(void)
{
    s_partition_hdl = esp_partition_find_first((esp_partition_type_t)CONFIG_CFG_BOARD_PARTITION_TYPE,
                                               (esp_partition_subtype_t)CONFIG_CFG_BOARD_PARTITION_SUBTYPE,
                                               CONFIG_CFG_BOARD_PARTITION_NAME);

    *(int *)g_sys_log_on = 1;

    if (s_partition_hdl == NULL)
    {
        g_license_code = _read_legacy_license_code();
        SYS_LOG_ERR("esp_partition_find_first() fail");
        return 1;
    }

    const void *cfg_board_partition;
    esp_partition_mmap_handle_t mmp_data_handle;
    esp_err_t err = esp_partition_mmap(s_partition_hdl, 0, 0x1000, ESP_PARTITION_MMAP_DATA, &cfg_board_partition, &mmp_data_handle);
    if (err != ESP_OK)
    {
        g_license_code = _read_legacy_license_code();
        SYS_LOG_ERR("esp_partition_mmap() fail");
        return 1;
    }

    p_cfg_board_partition = cfg_board_partition;
    g_license_code = p_cfg_board_partition->license_code;

    if (p_cfg_board_partition->version != BOARD_CONFIG_DATA_VER ||
        p_cfg_board_partition->size != sizeof(__cfg_board_partition_t) ||
        p_cfg_board_partition->crc_32 != _get_data_crc(p_cfg_board_partition))
    {
        SYS_LOG_WRN("no config data, using default config");
        return 1;
    }
    else
    {
        g_cfg_board = &p_cfg_board_partition->cfg;
        SYS_LOG_INF("config data checked valid");

        /* 如果代码区中的析载数据长度大于已有的数据区配置长度，则把多出来的部分添加到数据区中 */
        if (sizeof(__cfg_board_partition_t) > p_cfg_board_partition->size &&
            p_cfg_board_partition->size > sizeof(__cfg_board_partition_t) - sizeof(cfg_board_t))
        {
            cfg_board_t new_cfg;
            memcpy(&new_cfg, &s_cfg_board_default, sizeof(new_cfg));                                                              // 复制新的默认配置
            memcpy(&new_cfg, g_cfg_board, sizeof(cfg_board_t) - (sizeof(__cfg_board_partition_t) - p_cfg_board_partition->size)); // 用旧的固化数据覆已有部分
            board_cfg_fresh(&new_cfg);                                                                                            // 固化新配置
        }
        return 0;
    }
}

/**
 * @brief 写入激活码
 *
 * @param license_code
 * @return int
 */
int board_license_fresh(const uint8_t license_code[CONFIG_LICENSE_CODE_LENGTH])
{
    if (p_cfg_board_partition == NULL)
    {
        SYS_LOG_WRN("never exec board_cfg_init()");
        return -1;
    }

    __cfg_board_partition_t cfg_buf_partition;
    memcpy(&cfg_buf_partition, p_cfg_board_partition, sizeof(cfg_buf_partition));
    memcpy(&cfg_buf_partition.license_code[0], license_code, sizeof(cfg_buf_partition.license_code));
    cfg_buf_partition.version = BOARD_CONFIG_DATA_VER;
    cfg_buf_partition.size = sizeof(cfg_buf_partition);

    esp_err_t err;
    err = esp_partition_erase_range(s_partition_hdl, 0, 0x1000);
    if (err != ESP_OK)
    {
        SYS_LOG_WRN("esp_partition_erase_range() fail. err: %d", err);
        return 1;
    }
    err = esp_partition_write_raw(s_partition_hdl, 0, &cfg_buf_partition, sizeof(cfg_buf_partition));
    if (err != ESP_OK)
    {
        SYS_LOG_WRN("esp_partition_read_raw() fail. err: %d", err);
        return 1;
    }

    SYS_LOG_INF("done.");
    return 0;
}

/**
 * @brief 把板载数据固化到自定义分区中。
 * 当数据被固化后，除非擦除芯片，否则在代码区的配置数据不生效
 *
 * @param cfg
 * @return int
 */
int board_cfg_fresh(const cfg_board_t *cfg)
{
    if (p_cfg_board_partition == NULL)
    {
        SYS_LOG_WRN("never exec board_cfg_init()");
        return -1;
    }

    __cfg_board_partition_t cfg_buf_partition;
    _read_partition_data(&cfg_buf_partition, cfg);

    esp_err_t err;
    err = esp_partition_erase_range(s_partition_hdl, 0, 0x1000);
    if (err != ESP_OK)
    {
        SYS_LOG_WRN("esp_partition_erase_range() fail. err: %d", err);
        return 1;
    }
    err = esp_partition_write_raw(s_partition_hdl, 0, &cfg_buf_partition, sizeof(cfg_buf_partition));
    if (err != ESP_OK)
    {
        SYS_LOG_WRN("esp_partition_read_raw() fail. err: %d", err);
        return 1;
    }

    g_cfg_board = &p_cfg_board_partition->cfg;
    SYS_LOG_INF("done.");
    return 0;
}

/**
 * @brief 通过终端打印 cfg 的二进制数，用于帮助制作对应 __cfg_board_partition_t 的 bin 文件。
 *
 * @param cfg 如果为 NULL 时则表示打印保存在自定义分区位置的数据
 * @retval 0 操作成功
 * @retval -1 数据校验无效
 */
int board_cfg_dump(const cfg_board_t *cfg)
{
    if (p_cfg_board_partition == NULL)
    {
        SYS_LOG_WRN("never exec board_cfg_init()");
        return -1;
    }

    if (cfg == NULL)
    {
        if (p_cfg_board_partition->size > 0x1000 ||
            p_cfg_board_partition->size < CONFIG_LICENSE_CODE_LENGTH ||
            p_cfg_board_partition->crc_32 != _get_data_crc(p_cfg_board_partition))
        {
            SYS_LOG_WRN("No internal records, currently displayed as default configuration");
        }

        cfg = &s_cfg_board_default;
    }

    __cfg_board_partition_t cfg_buf_partition;
    _read_partition_data(&cfg_buf_partition, cfg);

    SYS_LOG_WRN("The data in the custom partition is as follows %u bytes:\r\n", sizeof(cfg_buf_partition));
    int i = 0;
    int len;
    const uint8_t *p = (uint8_t *)&cfg_buf_partition;

    SYS_PRINT("/* license code */\r\n");
    len = sizeof(cfg_buf_partition.license_code);
    for (i = 0; i < len; i++)
        SYS_PRINT("0x%02X,%s", *p++, i + 1 < len ? " " : "\r\n");

    SYS_PRINT("/* crc, size, version */\r\n");
    len = cfg_buf_partition.size - sizeof(cfg_buf_partition.license_code) - sizeof(cfg_buf_partition.cfg);
    for (i = 0; i < len; i++)
        SYS_PRINT("0x%02X,%s", *p++, i + 1 < len ? " " : "\r\n");

    SYS_PRINT("/* board configuration (from %s area) */\r\n", cfg == &s_cfg_board_default ? "code" : "data");
    len = sizeof(cfg_buf_partition.cfg);
    for (i = 0; i < len; i++)
        SYS_PRINT("0x%02X,%s", *p++, i + 1 < len ? " " : "\r\n");

    SYS_PRINT("\r\n");

    return 0;
}

static void _read_partition_data(__cfg_board_partition_t *out, const cfg_board_t *cfg)
{
    memcpy(out, p_cfg_board_partition, sizeof(*out));
    memcpy(&out->cfg, cfg, sizeof(out->cfg));
    out->version = BOARD_CONFIG_DATA_VER;
    out->size = sizeof(*out);
    out->crc_32 = _get_data_crc(out);
}

/**
 * @brief 旧的版本的激活码所在(历史遗留)
 *
 * @return const uint8_t* license_code
 */
static const uint8_t *_read_legacy_license_code(void)
{
    spi_flash_mmap_handle_t handle;
    const uint32_t LEGACY_LICENSE_CODE_START_ADDR = 0x3E0000;
    uint32_t start = LEGACY_LICENSE_CODE_START_ADDR;
    uint32_t end = LEGACY_LICENSE_CODE_START_ADDR + CONFIG_LICENSE_CODE_LENGTH;
    const void *license_code;
    spi_flash_mmap(LEGACY_LICENSE_CODE_START_ADDR, end - start, SPI_FLASH_MMAP_DATA, &license_code, &handle);
    return license_code;
}

static uint32_t _get_data_crc(const __cfg_board_partition_t *cfg)
{
    int cfg_size = cfg->size - sizeof(cfg->license_code) - sizeof(cfg->crc_32);
    return crc32_calc(0, &cfg->size, cfg_size);
}
