/*
 * psg_prop.c
 * PSG K-Line data dictionary implementation.
 *
 * Provides lookup functions for all ROM and EEPROM read responses.
 * Static constant tables are derived from protocol captures.
 * Dynamic values (DFI, calibration) use runtime storage.
 *
 *  Created on: 2025
 *      Author: herli
 */

#include "psg_prop.h"
#include "ee_manager.h"
#include <string.h>

// ================================================================
// ROM lookup tables
// ================================================================

/* Binary ROM entries: fixed-length raw data blobs */
typedef struct {
    uint16_t addr;
    uint8_t  len;
    uint8_t  data[10];
} RomEntry_t;

static const RomEntry_t s_rom_bin_table[] = {
    /* addr    len   data bytes (from protocol capture)                           description        */
    { 0x9FCC, 10, { 0xA2,0x93,0x61,0x9F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF } }, /* fixed block         */
    { 0x9FD6, 10, { 0x4B,0x9F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x10,0x9C } }, /* fixed block         */
    { 0x9FE0, 10, { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF } }, /* fixed block         */
    { 0x9FEA, 10, { 0xFF,0xFF,0xFF,0xFF,0xDD,0x95,0xFF,0xFF,0xFF,0xFF } }, /* fixed block         */
    { 0x9FF4, 10, { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF } }, /* fixed block         */
    { 0x9FB6,  2, { 0x44,0x00 } },                                         /* ptr -> DFI EEPROM addr */
    { 0x9FBE,  2, { 0xC6,0x00 } },                                         /* unknown ptr         */
    { 0x9FBC,  2, { 0xA4,0x00 } },                                         /* unknown ptr         */
    { 0x9FBA,  2, { 0xED,0x00 } },                                         /* unknown ptr         */
    { 0x9FA6,  2, { 0x06,0x04 } },                                         /* unknown             */
    { 0x9FA8,  2, { 0x42,0x00 } },                                         /* dT-Hybrid ref       */
    { 0x9F74,  2, { 0x70,0x00 } },                                         /* DeltaBlockierwinkel */
    { 0x9F72,  2, { 0x74,0x00 } },                                         /* DeltaPhi-Offset     */
    { 0x9F70,  2, { 0x48,0x00 } },                                         /* DynFDKorrekturt     */
    { 0x93AD,  3, { 0x30,0x30,0x30 } },                                    /* Änderungsindex PV "000" */
    { 0x9F9E,  3, { 0xFF,0xFF,0xFF } },                                    /* PSG Fertigungsdatum (unset) */
    { 0x9FA1,  3, { 0xFF,0xFF,0xFF } },                                    /* PSG Chargen-Nr. (unset) */
    { 0x9C10, 10, PSG_KUNDENNUMMER1_STR },/* Kundennummer 1 "1234567891" */
    { 0x9C1A,  2, PSG_KUNDENNUMMER2_STR },                                         /* Kundennummer 2 "23" */
    { PSG_CUST_CHANGE_ADDR, 3, PSG_FERTIGUNGSWERK_STR     },   /* Fertigungswerk                 */
    { PSG_CUST_CHANGE_ADDR + 3, 6,  PSG_MOD_INDEX_STR     },   /* Mod Index                 */
};
#define ROM_BIN_COUNT  (sizeof(s_rom_bin_table) / sizeof(s_rom_bin_table[0]))

/* String ROM entries: 10-byte ASCII identifiers */
typedef struct {
    uint16_t    addr;
    const char *str;   /* exactly 10 ASCII bytes, no NUL */
} RomStrEntry_t;

static const RomStrEntry_t s_rom_str_table[] = {
    { 0x93BA, PSG_IDENT_STR        },   /* ident (at PSG_IDENT_ADDR - 10)   */
    { 0x9F80, PSG_SOFTWARE_VER_STR },   /* software version                  */
    { 0x9F94, PSG_STEUERGERAET_STR },   /* Steuergeraet                      */
    { 0x9F76, PSG_FAHRSOFTWARE_STR },   /* Fahrsoftware                      */
    { 0x9C1C, PSG_DATENSATZ_STR    },   /* Datensatz                         */
    { 0x93B0, PSG_REFERENZ_STR     },   /* Referenz-Kennfeld                 */

};
#define ROM_STR_COUNT  (sizeof(s_rom_str_table) / sizeof(s_rom_str_table[0]))

// ================================================================
// EEPROM static data blocks
// ================================================================

/*
 * DTC storage region (0x0000-0x003F, 64 bytes).
 * Stored as a flat array so any-length chunk reads reconstruct correctly.
 * Byte values derived from protocol capture.
 */
static const uint8_t s_dtc_eeprom_block[64] = {
    /* 0x0000-0x000C (13 bytes, DTC MSG 1) */
    0x50, 0x20, 0xAB, 0x0A, 0x0E, 0x09, 0x01, 0xED, 0x5A, 0x20, 0xDF, 0x2D, 0x05,
    /* 0x000D-0x0019 (13 bytes, DTC MSG 2) */
    0x00, 0x02, 0x33, 0x5B, 0x20, 0xDF, 0x2D, 0x05, 0x00, 0x03, 0x34, 0x52, 0x20,
    /* 0x001A-0x0026 (13 bytes, DTC MSG 3) */
    0xDF, 0x2D, 0x00, 0x00, 0x04, 0x30, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    /* 0x0027-0x0033 (13 bytes, DTC MSG 4) */
    0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
    /* 0x0034-0x003F (12 bytes, DTC MSG 5) */
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

/*
 * DeltaPhi calibration array (0x009A-0x00BD, 36 bytes = 18 x int16 LE).
 * 0x8000 entries are "not calibrated" placeholders.
 */
static const uint8_t s_deltaphi_block[36] = {
    /* 0x009A-0x00A6 (13 bytes, AD1) */
    0xDE, 0xFF, 0xE8, 0xFF, 0x0C, 0x00, 0x08, 0x00, 0xFE, 0xFF, 0x00, 0x80, 0x00,
    /* 0x00A7-0x00B3 (13 bytes, AD2) */
    0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80,
    /* 0x00B4-0x00BD (10 bytes, AD3) */
    0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80
};

/* ================================================================
 * Runtime-writable EEPROM values.
 * Initialized to sniffed defaults; updated by WriteEeprom handlers.
 * ================================================================ */
static uint8_t s_dt_hybrid[2]            = { 0xDE, 0x22 };  /* 0x0042, dT-Hybrid, default 1958.5 K */
static uint8_t s_dynfd_korr[2]           = { 0x00, 0x00 };  /* 0x0048, DynFDKorrekturt             */
static uint8_t s_delta_blockierwinkel[4] = { 0x00, 0x00, 0x00, 0x00 }; /* 0x0070 */
static uint8_t s_deltaphi_offset[4]      = { 0x00, 0x00, 0x00, 0x00 }; /* 0x0074 */

// ================================================================
// PSG_ROM_GetData
// ================================================================
uint8_t PSG_ROM_GetData(uint16_t addr, uint8_t len, uint8_t *out)
{
    /* customerChangeAddress (0x9FFE) — 2 bytes little-endian */
    if (addr == 0x9FFE && len == 2) {
        out[0] = (uint8_t)(PSG_CUST_CHANGE_ADDR & 0xFF);
        out[1] = (uint8_t)((PSG_CUST_CHANGE_ADDR >> 8) & 0xFF);
        return 1;
    }

    /* Binary data blocks */
    for (int i = 0; i < (int)ROM_BIN_COUNT; i++) {
        if (s_rom_bin_table[i].addr == addr && s_rom_bin_table[i].len == len) {
            memcpy(out, s_rom_bin_table[i].data, len);
            return 1;
        }
    }

    /* 10-byte ASCII string entries */
    for (int i = 0; i < (int)ROM_STR_COUNT; i++) {
        if (s_rom_str_table[i].addr == addr && len == 10) {
            memcpy(out, s_rom_str_table[i].str, 10);
            return 1;
        }
    }

    return 0;
}

// ================================================================
// PSG_EEPROM_GetData
// ================================================================
uint8_t PSG_EEPROM_GetData(uint16_t addr, uint8_t len, uint8_t *out)
{
    /* DFI angle correction (0x0044, 2 bytes) — dynamic, caller must check lock */
    if (addr == 0x0044 && len == 2) {
        int8_t code = GetDfiValue();
        out[0] = (uint8_t)code;
        out[1] = (uint8_t)(-(int8_t)code);   /* two's-complement checksum byte */
        return 1;
    }

    /* Serial number (0x0080, 6 bytes) */
    if (addr == 0x0080 && len == 6) {
        memcpy(out, PSG_SERIAL_STR, 6);
        return 1;
    }

    /* Fertigungsdatum / manufacturing date (0x0086, 3 bytes) */
    if (addr == 0x0086 && len == 3) {
        memcpy(out, FERTIGUNGSDATUM_STR, 3);
       //memcpy(out + 3, FERTIGUNGSDATUM_STR, 3); si length = 6

        return 1;
    }

    /* Unknown/reserved (0x0094, 2 bytes) */
    if (addr == 0x0094 && len == 2) {
        out[0] = 0x00; out[1] = 0x00;
        return 1;
    }

    /* dT-Hybrid (0x0042, 2 bytes) */
    if (addr == 0x0042 && len == 2) {
        memcpy(out, s_dt_hybrid, 2);
        return 1;
    }

    /* DynFDKorrekturt (0x0048, 2 bytes) */
    if (addr == 0x0048 && len == 2) {
        memcpy(out, s_dynfd_korr, 2);
        return 1;
    }

    /* DeltaBlockierwinkel (0x0070, 4 bytes) */
    if (addr == 0x0070 && len == 4) {
        memcpy(out, s_delta_blockierwinkel, 4);
        return 1;
    }

    /* DeltaPhi-Offset (0x0074, 4 bytes) */
    if (addr == 0x0074 && len == 4) {
        memcpy(out, s_deltaphi_offset, 4);
        return 1;
    }

    /* DTC EEPROM block (0x0000-0x003F, 64 bytes total) */
    if (addr < 0x0040U) {
        if ((uint16_t)(addr + len) <= 0x0040U) {
            memcpy(out, &s_dtc_eeprom_block[addr], len);
            return 1;
        }
    }

    /* DeltaPhi calibration array (0x009A-0x00BD, 36 bytes) */
    if (addr >= 0x009AU && addr < (0x009AU + 36U)) {
        uint16_t off   = addr - 0x009AU;
        uint16_t avail = (uint16_t)(36U - off);
        uint8_t  n     = (len <= (uint8_t)avail) ? len : (uint8_t)avail;
        memcpy(out, &s_deltaphi_block[off], n);
        if (n < len) memset(out + n, 0xFF, len - n);
        return 1;
    }

    return 0;
}
