#include "config_store.h"
#include "config_defaults.h"
#include "stm32wlxx_hal.h"


#include <string.h>
#include <stdint.h>


typedef struct
{
    uint32_t magic;
    uint32_t checksum;
    BridgeConfig_t cfg;
} FlashConfig_t;

static uint32_t Config_CalcChecksum(const uint8_t *data, uint32_t len)
{
    uint32_t sum = 0;
    uint32_t i;

    for (i = 0; i < len; i++)
    {
        sum += data[i];
    }

    return sum;
}

bool Config_Load(BridgeConfig_t *cfg)
{
    const FlashConfig_t *stored;
    uint32_t calc;

    if (cfg == 0)
    {
        return false;
    }

    stored = (const FlashConfig_t *)CONFIG_FLASH_ADDR;

    if (stored->magic != CONFIG_MAGIC)
    {
        return false;
    }

    calc = Config_CalcChecksum((const uint8_t *)&stored->cfg, sizeof(BridgeConfig_t));

    if (calc != stored->checksum)
    {
        return false;
    }

    memcpy(cfg, &stored->cfg, sizeof(BridgeConfig_t));
    return true;
}

bool Config_Save(const BridgeConfig_t *cfg)
{
    FlashConfig_t temp;
    FLASH_EraseInitTypeDef erase;
    uint32_t page_error = 0;
    uint32_t addr;
    uint32_t i;
    const uint64_t *src64;
    uint32_t words64_count;

    if (cfg == 0)
    {
        return false;
    }

    memset(&temp, 0xFF, sizeof(temp));

    temp.magic = CONFIG_MAGIC;
    memcpy(&temp.cfg, cfg, sizeof(BridgeConfig_t));
    temp.checksum = Config_CalcChecksum((const uint8_t *)&temp.cfg, sizeof(BridgeConfig_t));

    HAL_FLASH_Unlock();

    memset(&erase, 0, sizeof(erase));
    erase.TypeErase = FLASH_TYPEERASE_PAGES;
    erase.Page = (CONFIG_FLASH_ADDR - FLASH_BASE_ADDR) / FLASH_PAGE_SIZE;
    erase.NbPages = 1;

    if (HAL_FLASHEx_Erase(&erase, &page_error) != HAL_OK)
    {
        HAL_FLASH_Lock();
        return false;
    }

    addr = CONFIG_FLASH_ADDR;
    src64 = (const uint64_t *)&temp;
    words64_count = (sizeof(FlashConfig_t) + 7U) / 8U;

    for (i = 0; i < words64_count; i++)
    {
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr, src64[i]) != HAL_OK)
        {
            HAL_FLASH_Lock();
            return false;
        }

        addr += 8U;
    }

    HAL_FLASH_Lock();

    return true;
}
