#include "eeprom.h"
#include <string.h>
#include "modbus.h"
HAL_StatusTypeDef EEPROM_ErasePage(void);
flash_device_parameter_t flash_device_parameter_default;


/**
 * @brief Liest die Struktur aus dem Flash in den RAM
 * @param data_ptr Zeiger auf die Ziel-Struktur im RAM
 * @param size     Größe der Struktur (sizeof)
 */
void EEPROM_Read(void* data_ptr, size_t size) {
    // Flash ist Memory-Mapped, wir können direkt mit memcpy lesen
    memcpy(data_ptr, (void*)MY_EEPROM_FLASH_ADDR, size);
}

/**
 * @brief Hilfsfunktion zum Löschen der Flash-Page
 */
 HAL_StatusTypeDef EEPROM_ErasePage(void) {
    FLASH_EraseInitTypeDef eraseInit;
    uint32_t pageError = 0;

    eraseInit.TypeErase = FLASH_TYPEERASE_PAGES;    
    eraseInit.Page      = MY_EEPROM_PAGE;
    eraseInit.NbPages   = 1;
    return HAL_FLASHEx_Erase(&eraseInit, &pageError);
}

/**
 * @brief Speichert die Struktur im Flash (Löschen -> Schreiben)
 * @return HAL_OK oder Fehlercode
 */
HAL_StatusTypeDef EEPROM_Write(void* data_ptr, size_t size) {
    // 1. Check: Haben sich die Daten überhaupt geändert? (Flash-Schonung)
    if (memcmp(data_ptr, (void*)MY_EEPROM_FLASH_ADDR, size) == 0) {
        return HAL_OK; // Keine Änderung, kein Schreibvorgang nötig
    }

    HAL_StatusTypeDef status;
    uint8_t *byte_ptr = (uint8_t*)data_ptr;
    uint32_t current_addr = MY_EEPROM_FLASH_ADDR;

    // 2. Flash entsperren
    HAL_FLASH_Unlock();

    // 3. Page löschen (Flash muss vor dem Schreiben 0xFF sein)
    status = EEPROM_ErasePage();
    if (status != HAL_OK) {
        HAL_FLASH_Lock();
        return status;
    }

    // 4. In 8-Byte Blöcken schreiben (Double Word)
    for (size_t i = 0; i < size; i += 8) {
        uint64_t double_word = 0xFFFFFFFFFFFFFFFF;
        size_t remaining = size - i;

        if (remaining >= 8) {
            memcpy(&double_word, &byte_ptr[i], 8);
        } else {
            memcpy(&double_word, &byte_ptr[i], remaining);
        }

        status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, current_addr, double_word);
        if (status != HAL_OK) break;

        current_addr += 8;
    }

    // 5. Flash wieder sperren
    HAL_FLASH_Lock();
    return status;
}

void EEPROM_WriteDefaults(void)
{
  
  flash_device_parameter_default.baudrate = 19200;
  flash_device_parameter_default.parityMode = MODBUS_UART_PARITY_EVEN;
  flash_device_parameter_default.stopBit = 1;
  flash_device_parameter_default.slave_address = 1;
  flash_device_parameter_default.first_start_id = FIRST_START_ID;
  
  EEPROM_Write( (void*) &flash_device_parameter_default, sizeof(flash_device_parameter_default));

}



uint16_t EEPROM_ReadFirstStart(void)
{
  
  flash_device_parameter_t dp;
  
  EEPROM_Read( (void*) &dp, sizeof(dp));

  return dp.first_start_id;


}

void EEPROM_ReadDeviceParameter(void)
{
  
  flash_device_parameter_t dp;
  
  EEPROM_Read( (void*) &dp, sizeof(dp));
  sys_data.s.parameter.sn = dp.sn;
  sys_data.s.parameter.baudrate = dp.baudrate;
  sys_data.s.parameter.parityMode = dp.parityMode;
  sys_data.s.parameter.stopBit = dp.stopBit;
  sys_data.s.parameter.slave_address = dp.slave_address;

}

void EEPROM_StoreConfig(bool writeSN)
{
  flash_device_parameter_t dp;
  
  EEPROM_Read( (void*) &dp, sizeof(dp));
  

  
  dp.baudrate = sys_data.s.parameter.baudrate;
  dp.parityMode = sys_data.s.parameter.parityMode;
  dp.stopBit = sys_data.s.parameter.stopBit;
  dp.slave_address = sys_data.s.parameter.slave_address;
  
  if (writeSN == true)
  {
	dp.sn = sys_data.s.parameter.sn;
  }

  
  EEPROM_Write( (void*) &dp, sizeof(dp));
}