/*
 * $Id: user_config.c 500 2021-08-08 19:43:38Z true $
 * begin 20190615 true
 */

#include "user_config.h"

#include "hw/eeprom16.h"
#include "misc/checksum.h"

#include "led/rgbled.h"

#include <string.h>
#include <stdio.h>



UserConf uconf;
uint8_t  sysflags;

uint16_t uconf_flash_offset;

static const uint8_t uconf_edge_defaults[8][8] = {
    {0x03, 0x10, 0x04, 0x00, 0x00, 0x00, 0xff, 0xa0},
    {0x03, 0x10, 0x04, 0x00, 0x00, 0x00, 0xff, 0xa0},
    {0x15, 0x42, 0x50, 0x00, 0xa0, 0x30, 0xff, 0x20},
    {0x03, 0x10, 0x04, 0x00, 0x00, 0x00, 0xff, 0xa0},
    {0x03, 0x11, 0x05, 0x00, 0x00, 0x00, 0xff, 0x80},
    {0x00, 0x01, 0x04, 0x00, 0x00, 0x66, 0x46, 0x80},
    {0x03, 0x10, 0x04, 0x00, 0x00, 0x00, 0xff, 0xa0},
    {0x03, 0x10, 0x04, 0x00, 0x00, 0x00, 0xff, 0xa0}
};



void uconf_defaults()
{
    int i;

    memset(&uconf, 0x00, sizeof(uconf));

    uconf.ver               = UCONF_VER;
    uconf.flags             = UCONF_FLAGS_LEDS_ENABLE;
    uconf.framemod          = UCONF_FRAMERATE_FULL;
    uconf.nameconf          = UCONF_NAME_DISP_DEMOWAVE1 | UCONF_NAME_MODE_AUTOROTATE | UCONF_NAME_MODE_COLOR_INVERT;
            // UCONF_NAME_DISP_DEMOWAVES1 | UCONF_NAME_MODE_AUTOROTATE | UCONF_NAME_MODE_COLOR_INVERT;

    uconf.font_idx          = 5;
    uconf.char_spacing      = 2;
    uconf.y_offset          = 0;

    // todo: add LUT
    strcpy(uconf.name, "Supercon");

    uconf.favcolor_hue      = 170;
    uconf.favcolor_sat      = 240;
    uconf.favcolor_val      = 32;

    uconf.altcolor_hue      = 0;
    uconf.altcolor_sat      = 240;
    uconf.altcolor_val      = 32;

    uconf.ledprog_rgb_idx   = 4;

    for (i = 0; i < 8; i++) {
        uconf.ledprog_rgb[i] = 0;
    }

    memcpy(uconf.ledprog_rgb_data, uconf_edge_defaults, sizeof(uconf_edge_defaults));

    uconf.lsens_dark_thresh = 0x6a0;       // todo: figure out what this should be by testing
    uconf.menu_timeout      = 5  * 60;
    uconf.sleep_timeout     = 20 * 60;

    uconf.checksum          = checksum_gen((uint8_t *)&uconf, sizeof(uconf) - 2);
}

// returns 0 if valid
static int8_t uconf_validate()
{
    // blank check
    if (uconf.checksum == 0xffff) return -1;

    // version check
    if (uconf.ver != UCONF_VER) {
        return -2;
    }

    // checksum verify
    if (!checksum_verify((uint8_t *)&uconf, sizeof(uconf) - 2, uconf.checksum)) {
        return -3;
    }

    // fix any mistakes
    if (uconf.ledprog_rgb_idx > (sizeof(rgb_pgm) / sizeof(rgb_pgm[0]))) uconf.ledprog_rgb_idx = 0;

    return 0;
}

void uconf_load()
{
    uint8_t i;
    uint32_t iter = 0;

    uconf_flash_offset = FLASH_RSVD_PAGES;

    for (i = 0; i < FLASH_RSVD_PAGES; i++) {
        // search nv data for latest user config
        eep16_read(i * FLASH_UCONF_BYTES, (uint8_t *)&uconf, FLASH_UCONF_BYTES);
        if (!uconf_validate()) {
            if (uconf.iter > iter) {
                // this page has the highest iter value, so is the latest we've found so far
                uconf_flash_offset = i;
                iter = uconf.iter;
            }
        }
    }

    if (uconf_flash_offset >= FLASH_RSVD_PAGES) {
        // flash has no valid data whatsoever
        // load some defaults into RAM and ignore flash for now
        uconf_defaults();
    } else {
        // reload the latest save data found
        eep16_read(uconf_flash_offset * FLASH_UCONF_BYTES, (uint8_t *)&uconf, FLASH_UCONF_BYTES);
    }
}

void uconf_clear()
{
    uint8_t i;
    uint8_t c[128];

    uint16_t fsize;
    uint16_t faddr;

    memset(&c, 0xff, sizeof(c));
    uconf_flash_offset = FLASH_RSVD_PAGES;

    for (i = 0; i < FLASH_RSVD_PAGES; i++) {
        // write config data
        fsize = FLASH_UCONF_BYTES;
        faddr = i * FLASH_UCONF_BYTES;

        while (fsize) {
            while (!eep16_is_ready());
            eep16_write(faddr, c, (fsize > 128) ? 128 : fsize);
            if (fsize <= 128) break;
            fsize -= 128;
            faddr += 128;
        }
    }
}

void uconf_write()
{
    uint32_t intr;

    uint16_t fsize;
    uint16_t faddr;
    uint8_t *uaddr;


    SYS_DisableAllIrq(&intr);

    // track writes and set up next page to write
    uconf.iter++;
    uconf_flash_offset++;
    if (uconf_flash_offset >= FLASH_RSVD_PAGES) {
        // if you need to erase flash, do it here
        // we have eeprom so we don't

        uconf_flash_offset = 0;
    }

    // calculate checksum
    uconf.checksum = checksum_gen((uint8_t *)&uconf, sizeof(uconf) - 2);

    // write config data
    fsize = FLASH_UCONF_BYTES;
    faddr = uconf_flash_offset * FLASH_UCONF_BYTES;
    uaddr = (uint8_t *)&uconf;

    while (fsize) {
        while (!eep16_is_ready());
        eep16_write(faddr, uaddr, (fsize > 128) ? 128 : fsize);
        if (fsize <= 128) break;
        fsize -= 128;
        faddr += 128;
        uaddr += 128;
    }

    SYS_RecoverIrq(intr);
}
