/**
 * @file flash_param.c
 * @brief Flash parameter storage module implementation
 */

#include "flash_param.h"
#include "stm32f1xx_hal.h"

#include <string.h>

/*---------------------------------------------------------------------------
 * Private Definitions
 *---------------------------------------------------------------------------*/

/* CRC32 polynomial (IEEE 802.3) */
#define CRC32_POLYNOMIAL    0xEDB88320

/*---------------------------------------------------------------------------
 * Private Variables
 *---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------
 * Private Functions
 *---------------------------------------------------------------------------*/

/**
 * @brief Unlock Flash for writing
 */
static HAL_StatusTypeDef flash_unlock(void)
{
    return HAL_FLASH_Unlock();
}

/**
 * @brief Lock Flash after writing
 */
static void flash_lock(void)
{
    HAL_FLASH_Lock();
}

/**
 * @brief Erase Flash page
 */
static HAL_StatusTypeDef flash_erase_page(uint32_t page_addr)
{
    FLASH_EraseInitTypeDef erase_init;
    uint32_t page_error;
    HAL_StatusTypeDef status;
    
    erase_init.TypeErase = FLASH_TYPEERASE_PAGES;
    erase_init.PageAddress = page_addr;
    erase_init.NbPages = 1;
    
    status = HAL_FLASHEx_Erase(&erase_init, &page_error);
    
    return status;
}

/**
 * @brief Program Flash half-word (16-bit)
 */
static HAL_StatusTypeDef flash_program_halfword(uint32_t addr, uint16_t data)
{
    return HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, addr, data);
}

/*---------------------------------------------------------------------------
 * Public Functions
 *---------------------------------------------------------------------------*/

/**
 * @brief Initialize Flash parameter storage
 */
int flash_param_init(void)
{
    return 0;
}

/**
 * @brief Read parameters from Flash
 */
int flash_param_read(void *data, uint32_t len)
{
    if (data == NULL || len == 0)
    {
        return -1;
    }
    
    /* Check if length exceeds available space */
    if (len > FLASH_PARAM_PAGE_SIZE)
    {
        return -1;
    }
    
    /* Direct memory read from Flash */
    memcpy(data, (const void *)FLASH_PARAM_START_ADDR, len);
    
    return 0;
}

/**
 * @brief Write parameters to Flash
 */
int flash_param_write(const void *data, uint32_t len)
{
    HAL_StatusTypeDef status;
    uint32_t addr;
    const uint8_t *src;
    uint16_t halfword;
    uint32_t i;
    
    if (data == NULL || len == 0)
    {
        return -1;
    }
    
    /* Check if length exceeds available space */
    if (len > FLASH_PARAM_PAGE_SIZE)
    {
        return -1;
    }
    
    /* Unlock Flash */
    status = flash_unlock();
    if (status != HAL_OK)
    {
        return -1;
    }
    
    /* Erase the page */
    status = flash_erase_page(FLASH_PARAM_START_ADDR);
    if (status != HAL_OK)
    {
        flash_lock();
        return -1;
    }
    
    /* Program Flash (half-word at a time for STM32F1) */
    addr = FLASH_PARAM_START_ADDR;
    src = (const uint8_t *)data;
    
    for (i = 0; i < len; i += 2)
    {
        /* Build half-word (little-endian) */
        halfword = src[i];
        if (i + 1 < len)
        {
            halfword |= ((uint16_t)src[i + 1]) << 8;
        }
        else
        {
            halfword |= 0xFF00;  /* Pad with 0xFF */
        }
        
        status = flash_program_halfword(addr, halfword);
        if (status != HAL_OK)
        {
            flash_lock();
            return -1;
        }
        
        addr += 2;
    }
    
    /* Lock Flash */
    flash_lock();
    
    /* Verify write */
    if (memcmp((const void *)FLASH_PARAM_START_ADDR, data, len) != 0)
    {
        return -1;
    }
    
    return 0;
}

/**
 * @brief Erase parameter storage area
 */
int flash_param_erase(void)
{
    HAL_StatusTypeDef status;
    
    /* Unlock Flash */
    status = flash_unlock();
    if (status != HAL_OK)
    {
        return -1;
    }
    
    /* Erase the page */
    status = flash_erase_page(FLASH_PARAM_START_ADDR);
    
    /* Lock Flash */
    flash_lock();
    
    return (status == HAL_OK) ? 0 : -1;
}

/**
 * @brief Calculate CRC32
 */
uint32_t flash_param_crc32(const void *data, uint32_t len)
{
    const uint8_t *p = (const uint8_t *)data;
    uint32_t crc = 0xFFFFFFFF;
    uint32_t i;
    uint32_t j;

    for (i = 0; i < len; i++)
    {
        crc ^= p[i];
        for (j = 0; j < 8u; ++j)
        {
            if ((crc & 1u) != 0u)
            {
                crc = (crc >> 1) ^ CRC32_POLYNOMIAL;
            }
            else
            {
                crc >>= 1;
            }
        }
    }
    
    return crc ^ 0xFFFFFFFF;
}

/**
 * @brief Verify parameter storage integrity
 */
int flash_param_verify(void)
{
    uint32_t magic;
    
    /* Read magic number */
    memcpy(&magic, (const void *)FLASH_PARAM_START_ADDR, sizeof(magic));
    
    /* Check if Flash is erased (all 0xFF) */
    if (magic == 0xFFFFFFFF)
    {
        return -1;  /* Empty/erased */
    }
    
    return 0;
}
