/*******************************************************************************
 * @file    device_config.c
 * @brief   Device Configuration Management
 * @author  Charles KWON <charleskwon@medithings.co.kr>
 * @date    2025-01-30
 * @copyright (c) 2025 Medithings Inc. All rights reserved.
 *
 * @details EEPROM configuration loading and default values.
 ******************************************************************************/

#include "device_config.h"
#include "nrf_delay.h"
#include "debug_print.h"
#include "cat_interface.h"
#include <cmd_parse.h>
#include <string.h>
#include "storage/dr_mem.h"
#include "fstorage.h"

/*==============================================================================
 * GLOBAL VARIABLES
 *============================================================================*/

char m_static_passkey[7] = "123456";
char SERIAL_NO[16] = "2025VIVAM00001";
char HW_NO[12] = "HW00000001";
bool bond_data_delete = true;
uint8_t m_pd_adc_cnt = 8;
uint16_t m_pd_delay_us = 8000;
uint32_t m_life_cycle = 0;
uint16_t led_pd_dac_v[48];

/*==============================================================================
 * VALIDATION FUNCTIONS
 *============================================================================*/

bool is_valid_serial_no(const char *serial)
{
    if (serial == NULL) return false;

    /* Check for reasonable serial number format */
    for (int i = 0; i < 12; i++) {
        char c = serial[i];
        if (c == 0) break;
        if (c == 0xFF) return false;  /* Uninitialized EEPROM */
        if (c < 0x20 || c > 0x7E) return false;  /* Non-printable */
    }
    return true;
}

bool is_valid_passkey(const char *passkey)
{
    if (passkey == NULL) return false;

    /* Check for 6 digit passkey */
    for (int i = 0; i < 6; i++) {
        char c = passkey[i];
        if (c == 0xFF) return false;  /* Uninitialized EEPROM */
        if (c < '0' || c > '9') return false;  /* Not a digit */
    }
    return true;
}

/*==============================================================================
 * CONFIGURATION LOADING
 *============================================================================*/

void load_default_values(void)
{
    /* Default Serial Number */
    memset(SERIAL_NO, 0, sizeof(SERIAL_NO));
    memcpy(SERIAL_NO, "2025AAAAT001", 12);

    /* Default Passkey */
    memset(m_static_passkey, 0, sizeof(m_static_passkey));
    memcpy(m_static_passkey, "123456", 6);

    /* Default measurement parameters */
    m_pd_delay_us = 8000;
    m_pd_adc_cnt  = 8;

    /* Default state */
    bond_data_delete = 1;

    /* Default AGC Gain values */
    for (int i = 0; i < 48; i++) {
        led_pd_dac_v[i] = 2048;
    }

    DBG_PRINTF("[CFG] Using default values\r\n");
}

static bool is_valid_hw_no(const char *hw)
{
    if (hw == NULL) return false;
    /* All 0x00 or 0xFF = uninitialized */
    bool all_zero = true, all_ff = true;
    for (int i = 0; i < 12; i++) {
        if (hw[i] != 0x00) all_zero = false;
        if ((uint8_t)hw[i] != 0xFF) all_ff = false;
    }
    if (all_zero || all_ff) return false;
    /* Check printable ASCII */
    for (int i = 0; i < 12; i++) {
        char c = hw[i];
        if (c == 0) break;
        if (c < 0x20 || c > 0x7E) return false;
    }
    return true;
}

void load_device_configuration(void)
{
    extern uint8_t m_reset_status;
    ret_code_t err_code;

    /* Start with RAM defaults, then override with FDS-saved values below.
     * dr_memSetDefaults() removed: it was clobbering FDS-loaded m_config,
     * wiping user-saved data (serial_no, passkey, etc.) on every boot
     * when hw_no was still all-zeros. */
    load_default_values();

    /* Show m_config state right after config_load (before dr_memRead) */
    {
        char tmp[13] = {0};
        memcpy(tmp, m_config.serial_no, 12);
        DBG_PRINTF("[CFG] FDS serial_no='%s'\r\n", tmp);
        memcpy(tmp, m_config.hw_no, 12);
        DBG_PRINTF("[CFG] FDS hw_no='%s'\r\n", tmp);
    }

    /* Read FDS entries (already loaded by config_load() into m_config) */
    err_code = dr_memRead("serial_no", (uint8_t *)SERIAL_NO, 12);
    DBG_PRINTF("[CFG] dr_memRead S/N rc=%u valid=%d\r\n", err_code, is_valid_serial_no(SERIAL_NO));
    DBG_PRINTF("[CFG] S/N hex: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\r\n",
               (uint8_t)SERIAL_NO[0], (uint8_t)SERIAL_NO[1], (uint8_t)SERIAL_NO[2], (uint8_t)SERIAL_NO[3],
               (uint8_t)SERIAL_NO[4], (uint8_t)SERIAL_NO[5], (uint8_t)SERIAL_NO[6], (uint8_t)SERIAL_NO[7],
               (uint8_t)SERIAL_NO[8], (uint8_t)SERIAL_NO[9], (uint8_t)SERIAL_NO[10], (uint8_t)SERIAL_NO[11]);
    if (err_code != NRF_SUCCESS || !is_valid_serial_no(SERIAL_NO)) {
        DBG_PRINTF("[CFG] Invalid S/N - using default\r\n");
        memset(SERIAL_NO, 0, sizeof(SERIAL_NO));
        memcpy(SERIAL_NO, "2025AAAAT001", 12);
    }

    err_code = dr_memRead("passkey", (uint8_t *)m_static_passkey, 6);
    if (err_code != NRF_SUCCESS || !is_valid_passkey(m_static_passkey)) {
        DBG_PRINTF("[CFG] Invalid passkey - using default\r\n");
        memset(m_static_passkey, 0, sizeof(m_static_passkey));
        memcpy(m_static_passkey, "123456", 6);
    }

    {
        uint8_t raw = 0;
        dr_memRead("bond_delete", &raw, 1);
        bond_data_delete = (raw != 0);
    }

    dr_memRead("reset_status", &m_reset_status, 1);
    if (m_reset_status > 2) {
        DBG_PRINTF("[CFG] Invalid reset_status (%u) - using 1\r\n", m_reset_status);
        m_reset_status = 1;
    }

    dr_memRead("pd_adc_cnt", &m_pd_adc_cnt, 1);
    if (m_pd_adc_cnt == 0 || m_pd_adc_cnt >= 255) {
        DBG_PRINTF("[CFG] Invalid adc_cnt (%u) - using 8\r\n", m_pd_adc_cnt);
        m_pd_adc_cnt = 8;
    }

    dr_memRead("pd_delay", &m_pd_delay_us, 2);
    if (m_pd_delay_us < 5000 || m_pd_delay_us > 30000) {
        DBG_PRINTF("[CFG] Invalid pd_delay (%u) - using 8000\r\n", m_pd_delay_us);
        m_pd_delay_us = 8000;
    }

    /* W25Q32 entries - NOT read during boot
     * W25Q32 is not initialized at boot (SPIM + BSP event conflict).
     * agc_gain and life_cycle use RAM defaults from load_default_values().
     * Actual W25Q32 values are loaded later when W25Q32 is powered on
     * (via BLE command or measurement start). */

    DBG_PRINTF("[CFG] Loaded: S/N=%s, delay=%u, cnt=%u, life=%u\r\n",
               SERIAL_NO, m_pd_delay_us, m_pd_adc_cnt, m_life_cycle);

    /* 3. config_data_t dump (FDS raw) */
    {
        char tmp[13] = {0};
        #define CFG_DLY() nrf_delay_ms(10)

        DBG_PRINTF("[CFG] === config_data_t (FDS) ===\r\n"); CFG_DLY();
        DBG_PRINTF("[CFG] magic   : 0x%08X\r\n", m_config.magic_number); CFG_DLY();

        memcpy(tmp, m_config.hw_no, 12); tmp[12] = 0;
        DBG_PRINTF("[CFG] hw_no   : %s\r\n", tmp); CFG_DLY();

        memcpy(tmp, m_config.serial_no, 12); tmp[12] = 0;
        DBG_PRINTF("[CFG] serial  : %s\r\n", tmp); CFG_DLY();

        DBG_PRINTF("[CFG] passkey : %c%c%c%c%c%c\r\n",
                   m_config.static_passkey[0], m_config.static_passkey[1],
                   m_config.static_passkey[2], m_config.static_passkey[3],
                   m_config.static_passkey[4], m_config.static_passkey[5]); CFG_DLY();
        DBG_PRINTF("[CFG] bond_del: %u\r\n",  m_config.bond_data_delete); CFG_DLY();
        DBG_PRINTF("[CFG] reset_st: %d\r\n",  m_config.reset_status); CFG_DLY();
        DBG_PRINTF("[CFG] adc_cnt : %u\r\n",  m_config.pd_adc_cnt); CFG_DLY();
        DBG_PRINTF("[CFG] pd_delay: %u\r\n",  m_config.pd_delay_us); CFG_DLY();

        /* W25Q32 entries */
        DBG_PRINTF("[CFG] agc_gain[48]:\r\n"); CFG_DLY();
        for (int i = 0; i < 48; i += 8) {
            DBG_PRINTF("  [%2d] %u %u %u %u %u %u %u %u\r\n", i,
                       led_pd_dac_v[i],   led_pd_dac_v[i+1],
                       led_pd_dac_v[i+2], led_pd_dac_v[i+3],
                       led_pd_dac_v[i+4], led_pd_dac_v[i+5],
                       led_pd_dac_v[i+6], led_pd_dac_v[i+7]); CFG_DLY();
        }
        DBG_PRINTF("[CFG] life    : %u\r\n", m_life_cycle); CFG_DLY();
        DBG_PRINTF("[CFG] ========================\r\n");

        #undef CFG_DLY
    }
}
