#include "stm32f4xx_flash.h"
#include "partition.h"
#include "led.h"
#include "file.h"
#include "config.h"
#include "ff.h"

static struct file_config_s file_config_flash;
static struct file_config_s file_config_sd;

static void file_config_write(uint32_t addr, struct file_config_s *cfg)
{
    volatile uint32_t *p;

    p = (volatile uint32_t *)cfg;
    FLASH_Unlock();
    FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | \
                FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);
    for (uint32_t i = 0; i < 4; i++) {
        if (FLASH_ProgramWord(addr + i * 4, p[i]) != FLASH_COMPLETE) {
            led_indicate_error();
        }
    }
    FLASH_Lock();
    p = (volatile uint32_t *)addr;
    if (p[0] != addr || (p[0] + p[1] + p[2]) != p[3]) {
        /* program flash error */
        led_indicate_error();
    }
}

static uint32_t str10_to_uint(uint8_t *str)
{
    uint32_t val = 0;
    uint32_t i = 0;

    while (1) {
        uint8_t c = str[i++];
        if (c >= '0' && c <= '9') {
            c = c - '0';
            val *= 10;
            val += c;
        } else {
            return val;
        }
    }
    return val;
}

static uint32_t str16_to_uint(uint8_t *str)
{
    uint32_t val = 0;
    uint32_t i = 0;

    while (1) {
        uint8_t c = str[i++];
        if (c >= '0' && c <= '9') {
            c = c - '0';
        } else if (c >= 'A' && c <= 'F') {
            c = c - 'A' + 10;
        } else if (c >= 'a' && c <= 'f') {
            c = c - 'a' + 10;
        } else {
            return val;
        }
        val *= 16;
        val += c;
    }
    return val;
}

static void file_config_parse(uint8_t *p, uint32_t len, struct file_config_s *cfg)
{
    uint32_t item = 0; /* 0:null, 1:addr, 2:baudrate, 3:parity, 4:stopbits */
    uint32_t stage = 0; /* 0:null, 1:name_ing, 2:name_end, 3:numing, 4:num_end */
    uint32_t i = 0; /* index of every char */
    uint32_t j = 0; /* index in one word */
    uint32_t val32;
    uint8_t val[16];

    for (uint32_t k=0; k<sizeof(val); k++) {
        val[k] = 0;
    }
    p[len] = 0;
    /* default value */
    cfg->baudrate = UART_DEFAULT_BAUDRATE;
    cfg->parity = UART_DEFAULT_PARITY;
    cfg->stopbits = UART_DEFAULT_STOPBITS;
    cfg->addr = UART_DEFAULT_ADDR;
    /* state machine for parse config file */
    while (1) {
        if (stage == 0) { /* null */
            if (p[i] == 'a' || p[i] == 'A') {
                item = 1; /* addr */
                stage = 1; /* name_ing */
                j = 1;
            } else if (p[i] == 'b' || p[i] == 'B') {
                item = 2; /* baudrate */
                stage = 1; /* name_ing */
                j = 1;
            } else if (p[i] == 'p' || p[i] == 'P') {
                item = 3; /* parity */
                stage = 1; /* name_ing */
                j = 1;
            } else if (p[i] == 's' || p[i] == 'S') {
                item = 4; /* stopbits */
                stage = 1; /* name_ing */
                j = 1;
            } else {
                item = 0; /* null */
            }
        } else if (stage == 1) { /* name_ing */
            if (item == 1) {
                if (p[i] == "addr"[j] || p[i] == "ADDR"[j]) {
                    if (j < 3) {
                        j++;
                    } else {
                        j = 0;
                        stage = 2; /* name_end */
                    }
                } else {
                    j = 0;
                    item = 0;
                    stage = 0;
                }
            } else if (item == 2) {
                if (p[i] == "baudrate"[j] || p[i] == "BAUDRATE"[j]) {
                    if (j < 7) {
                        j++;
                    } else {
                        j = 0;
                        stage = 2; /* name_end */
                    }
                } else {
                    j = 0;
                    item = 0;
                    stage = 0;
                }
            } else if (item == 3) {
                if (p[i] == "parity"[j] || p[i] == "PARITY"[j]) {
                    if (j < 5) {
                        j++;
                    } else {
                        j = 0;
                        stage = 2; /* name_end */
                    }
                } else {
                    j = 0;
                    item = 0;
                    stage = 0;
                }
            } else if (item == 4) {
                if (p[i] == "stopbits"[j] || p[i] == "STOPBITS"[j]) {
                    if (j < 7) {
                        j++;
                    } else {
                        j = 0;
                        stage = 2; /* name_end */
                    }
                } else {
                    j = 0;
                    item = 0;
                    stage = 0;
                }
            } else {
                j = 0;
                item = 0;
                stage = 0;
            }
        } else if (stage == 2) { /* name_end */
            if (item == 1 || item == 2 || item == 4) { /* addr, baudrate, stopbits */
                if (p[i] >= '0' && p[i] <= '9') {
                    stage = 3; /* numing */
                    j = 1;
                    val[0] = p[i];
                }
            } else if (item == 3) { /* parity */
                if (p[i] == 'N' || p[i] == 'n') {
                    cfg->parity = UART_PARITY_NONE;
                    stage = 4; /* num_end */
                } else if (p[i] == 'O' || p[i] == 'o') {
                    cfg->parity = UART_PARITY_ODD;
                    stage = 4; /* num_end */
                } else if (p[i] == 'E' || p[i] == 'e') {
                    cfg->parity = UART_PARITY_EVEN;
                    stage = 4; /* num_end */
                } else {
                    if (p[i] == '\n' || p[i] == '\r') {
                        j = 0;
                        item = 0;
                        stage = 0;
                    }
                }
            } else {
                j = 0;
                item = 0;
                stage = 0;
            }
        } else if (stage == 3) { /* numing */
            if ((p[i] >= '0' && p[i] <= '9') || \
                (p[i] >= 'A' && p[i] <= 'F') || \
                (p[i] >= 'a' && p[i] <= 'f') || \
                 p[i] == 'x' || p[i] == 'X') {
                val[j++] = p[i];
            } else { /* num_end */
                val[j] = 0; /* set 0 for the end of string */
                if (val[1] == 'x' || val[1] == 'X') { /* 16, hexadecimal */
                    val32 = str16_to_uint(val + 2);
                } else { /* 10, decimalism */
                    val32 = str10_to_uint(val);
                }
                if (item == 1) { /* addr */
                    cfg->addr = (uint8_t)(val32 & 0xFF);
                } else if (item == 2) { /* baudrate */
                    cfg->baudrate = val32;
                } else if (item == 4) { /* stopbits */
                    cfg->stopbits = (uint8_t)(val32 & 0xFF);
                }
                stage = 4; /* num_end */
            }
        } else if (stage == 4) { /* num_end */
            j = 0;
            item = 0;
            stage = 0;
        } else {
            stage = 0;
        }
        i++;
        if (i > len) {
            return;
        }
    }
}

void file_config_copy(struct file_config_s *dst, struct file_config_s *src)
{
    uint32_t *s, *d;

    s = (uint32_t *)src;
    d = (uint32_t *)dst;
    for (uint32_t i = 0; i < sizeof(struct file_config_s) / 4; i++) {
        *d = *s;
        s++;
        d++;
    }
}

uint32_t file_config_get_free_addr(struct file_config_s *cfg)
{
    uint32_t addr;
    volatile uint32_t *p;

    addr = ADDR_CONFIG;
    p = (volatile uint32_t *)addr;
    /* first run or data was dirty */
    if (p[0] != addr || (p[0] + p[1] + p[2]) != p[3]) {
        file_secctor_erase(SECTOR_CONFIG);
        p = (volatile uint32_t *)cfg;
        cfg->id = addr;
        cfg->baudrate = UART_DEFAULT_BAUDRATE;
        cfg->parity = UART_DEFAULT_PARITY;
        cfg->stopbits = UART_DEFAULT_STOPBITS;
        cfg->addr = UART_DEFAULT_ADDR;
        cfg->resv = UART_DEFAULT_RESV;
        cfg->checksum = p[0] + p[1] + p[2];
        file_config_write(addr, cfg);
    }

    for (addr = ADDR_CONFIG; addr < (ADDR_CONFIG + SIZE_CONFIG - sizeof(struct file_config_s)); addr += sizeof(struct file_config_s)) {
        p = (volatile uint32_t *)addr;
        if (addr == p[0]) {
            continue;
        } else if (0xFFFFFFFF == p[0]) {
            file_config_copy(cfg, (struct file_config_s *)(addr - sizeof(struct file_config_s)));
            return addr;
        } else {
            return 0;
        }
    }
    return 0;
}

void file_config_update(void)
{
    FRESULT ret;
    UINT bc;
    uint32_t addr;
    volatile uint32_t *p;

    ret = f_open(&f, file_name[FILE_IDX_CONFIG], FA_READ);
    if (ret != FR_OK) {
        return;
    }
    ret = f_read(&f, file_buffer, FILE_BUFF_SIZE, &bc);
    if (ret != FR_OK || bc == 0) {
        f_close(&f);
        return;
    }
    f_close(&f);
    file_config_parse(file_buffer, (uint32_t)bc, &file_config_sd);
    addr = file_config_get_free_addr(&file_config_flash);
    if ((file_config_sd.baudrate == file_config_flash.baudrate) && \
        (file_config_sd.parity == file_config_flash.parity) && \
        (file_config_sd.stopbits == file_config_flash.stopbits) && \
        (file_config_sd.addr == file_config_flash.addr)) {
            return;
        }
    /* sector full or dtaa dirty */
    if (addr == 0) {
        file_secctor_erase(SECTOR_CONFIG);
        addr = ADDR_CONFIG;
    }
    p = (volatile uint32_t *)(&file_config_sd);
    file_config_sd.id = addr;
    file_config_sd.resv = UART_DEFAULT_RESV;
    file_config_sd.checksum = p[0] + p[1] + p[2];
    file_config_write(addr, &file_config_sd);
}
