/*******************************************************************************
 * @file    dr_mem.c
 * @brief   Name-based Memory API Implementation
 * @author  Charles KWON <charleskwon@medithings.co.kr>
 * @date    2026-02-19
 * @copyright (c) 2026 Medithings Inc. All rights reserved.
 *
 * @details Routes name-based read/write to FDS or W25Q32 backend.
 *
 *   dr_memWrite("serial_no", data, 12)
 *          |
 *          v
 *   [Lookup Table] --> FDS (config_data_t memcpy + config_save)
 *                  --> W25Q32 (w25q32_write at physical address)
 ******************************************************************************/

#include "dr_mem.h"
#include "fstorage.h"
#include "drivers/w25q32/w25q32.h"
#include "debug_print.h"
#include <string.h>
#include <stddef.h>
#include <stdio.h>

/*==============================================================================
 * DEFAULT VALUES
 *============================================================================*/

static const char     dflt_hw_no[12]    = "123456789012";
static const char     dflt_serial[12]   = "2025AAAAT001";
static const uint8_t  dflt_passkey[6]   = {'1','2','3','4','5','6'};
static const uint8_t  dflt_bond_del     = 1;
static const int8_t   dflt_reset_st     = 99;
static const uint8_t  dflt_adc_cnt      = 8;
static const uint16_t dflt_pd_delay     = 8000;
static const uint16_t dflt_agc[48]      = {
    2048,2048,2048,2048,2048,2048,2048,2048,
    2048,2048,2048,2048,2048,2048,2048,2048,
    2048,2048,2048,2048,2048,2048,2048,2048,
    2048,2048,2048,2048,2048,2048,2048,2048,
    2048,2048,2048,2048,2048,2048,2048,2048,
    2048,2048,2048,2048,2048,2048,2048,2048
};
static const uint32_t dflt_life_cycle   = 0;

/*==============================================================================
 * LOOKUP TABLE
 *
 * FDS entries: plaintext in config_data_t (internal flash, RBP protected)
 * W25Q32 entries: direct physical address access
 *============================================================================*/

static const dr_mem_entry_t mem_table[] = {
    /* FDS entries (device config - internal flash, plaintext) */
    { "hw_no",         MEM_BACKEND_FDS,    offsetof(config_data_t, hw_no),           12, false, dflt_hw_no     },
    { "serial_no",     MEM_BACKEND_FDS,    offsetof(config_data_t, serial_no),       12, false, dflt_serial    },
    { "passkey",       MEM_BACKEND_FDS,    offsetof(config_data_t, static_passkey),   6, false, dflt_passkey   },
    { "bond_delete",   MEM_BACKEND_FDS,    offsetof(config_data_t, bond_data_delete), 1, false, &dflt_bond_del },
    { "reset_status",  MEM_BACKEND_FDS,    offsetof(config_data_t, reset_status),     1, false, &dflt_reset_st },
    { "pd_adc_cnt",    MEM_BACKEND_FDS,    offsetof(config_data_t, pd_adc_cnt),       1, false, &dflt_adc_cnt  },
    { "pd_delay",      MEM_BACKEND_FDS,    offsetof(config_data_t, pd_delay_us),      2, false, &dflt_pd_delay },

    /* W25Q32 entries (calibration + data - external flash) */
    { "agc_gain",      MEM_BACKEND_W25Q32, 0x000000, 96, false, dflt_agc        },
    { "life_cycle",    MEM_BACKEND_W25Q32, 0x000060,  4, false, &dflt_life_cycle },
};

#define MEM_TABLE_COUNT  (sizeof(mem_table) / sizeof(mem_table[0]))

/*==============================================================================
 * PRIVATE FUNCTIONS
 *============================================================================*/

/**
 * @brief Find entry by key name
 */
static const dr_mem_entry_t *find_entry(const char *key)
{
    for (uint32_t i = 0; i < MEM_TABLE_COUNT; i++) {
        if (strcmp(mem_table[i].key, key) == 0) {
            return &mem_table[i];
        }
    }
    return NULL;
}

/**
 * @brief Write to FDS backend (config_data_t field + config_save)
 *        FDS stores plaintext - internal flash is RBP protected
 */
static ret_code_t fds_backend_write(const dr_mem_entry_t *entry,
                                    const void *data, uint16_t len)
{
    uint16_t copy_len = (len < entry->size) ? len : entry->size;
    uint8_t *dst = (uint8_t *)&m_config + entry->offset;

    DBG_PRINTF("[FDS_W] '%s' %uB offset=%u\r\n", entry->key, copy_len, entry->offset);
    memcpy(dst, data, copy_len);
    DBG_PRINTF("[FDS_W] memcpy OK, calling config_save\r\n");
    config_save();
    DBG_PRINTF("[FDS_W] config_save returned\r\n");
    return NRF_SUCCESS;
}

/**
 * @brief Read from FDS backend (config_data_t field)
 */
static ret_code_t fds_backend_read(const dr_mem_entry_t *entry,
                                   void *data, uint16_t len)
{
    uint16_t copy_len = (len < entry->size) ? len : entry->size;
    uint8_t *src = (uint8_t *)&m_config + entry->offset;

    memcpy(data, src, copy_len);
    return NRF_SUCCESS;
}

/**
 * @brief Write to W25Q32 backend
 */
static ret_code_t w25q_backend_write(const dr_mem_entry_t *entry,
                                     const void *data, uint16_t len)
{
    uint16_t copy_len = (len < entry->size) ? len : entry->size;
    w25q32_write(entry->offset, (const uint8_t *)data, copy_len);
    return NRF_SUCCESS;
}

/**
 * @brief Read from W25Q32 backend
 */
static ret_code_t w25q_backend_read(const dr_mem_entry_t *entry,
                                    void *data, uint16_t len)
{
    uint16_t copy_len = (len < entry->size) ? len : entry->size;
    w25q32_read(entry->offset, (uint8_t *)data, copy_len);
    return NRF_SUCCESS;
}

/*==============================================================================
 * PUBLIC API
 *============================================================================*/

ret_code_t dr_memInit(void)
{
    /* FDS is initialized in fs_storage_init() + config_load() (main.c) */
    /* W25Q32 is initialized in w25q32_power_on() + w25q32_init() (main.c) */
    printf("[DR_MEM] Init OK (FDS + W25Q32)\r\n");
    return NRF_SUCCESS;
}

ret_code_t dr_memWrite(const char *key, const void *data, uint16_t len)
{
    const dr_mem_entry_t *entry = find_entry(key);
    if (entry == NULL) {
        printf("[DR_MEM] Write ERR: key '%s' not found\r\n", key);
        return NRF_ERROR_NOT_FOUND;
    }

    if (len > entry->size) {
        printf("[DR_MEM] Write WARN: len %u > size %u for '%s'\r\n",
               len, entry->size, key);
    }

    printf("[DR_MEM] W '%s' %uB -> %s\r\n", key, len,
           entry->backend == MEM_BACKEND_FDS ? "FDS" : "W25Q32");

    switch (entry->backend) {
        case MEM_BACKEND_FDS:
            return fds_backend_write(entry, data, len);
        case MEM_BACKEND_W25Q32:
            return w25q_backend_write(entry, data, len);
        default:
            return NRF_ERROR_NOT_SUPPORTED;
    }
}

ret_code_t dr_memRead(const char *key, void *data, uint16_t len)
{
    const dr_mem_entry_t *entry = find_entry(key);
    if (entry == NULL) {
        printf("[DR_MEM] Read ERR: key '%s' not found\r\n", key);
        return NRF_ERROR_NOT_FOUND;
    }

    if (len > entry->size) {
        printf("[DR_MEM] Read WARN: len %u > size %u for '%s'\r\n",
               len, entry->size, key);
    }

    switch (entry->backend) {
        case MEM_BACKEND_FDS:
            return fds_backend_read(entry, data, len);
        case MEM_BACKEND_W25Q32:
            return w25q_backend_read(entry, data, len);
        default:
            return NRF_ERROR_NOT_SUPPORTED;
    }
}

const dr_mem_entry_t *dr_memFind(const char *key)
{
    return find_entry(key);
}

ret_code_t dr_memSetDefaults(void)
{
    printf("[DR_MEM] Setting defaults (RAM only)\r\n");

    /* FDS entries: write to m_config RAM only (no config_save)
     * FDS flash defaults are handled by config_load() -> fds_default_value_set()
     * W25Q32 is not initialized during boot */
    for (uint32_t i = 0; i < MEM_TABLE_COUNT; i++) {
        const dr_mem_entry_t *e = &mem_table[i];
        if (e->default_val == NULL) continue;

        if (e->backend == MEM_BACKEND_FDS) {
            uint8_t *dst = (uint8_t *)&m_config + e->offset;
            memcpy(dst, e->default_val, e->size);
        }
        printf("[DR_MEM] D '%s' %uB\r\n", e->key, e->size);
    }

    printf("[DR_MEM] Defaults applied (%u entries, RAM)\r\n", MEM_TABLE_COUNT);
    return NRF_SUCCESS;
}
