#include "term.h"

// Includes
#include "cfg.h"

// ============================================================================
//                                   GLOBALS
// ============================================================================

term_t term;

// ============================================================================
//                                PROTOTYPES
// ============================================================================


// ============================================================================
//                                 FUNCTION
// ============================================================================

void term_init()
{
    GPIO_InitTypeDef gpio = {0};
    // GPIO
    gpio.Mode        = GPIO_MODE_AF_PP;
    gpio.Pull        = GPIO_PULLUP;
    gpio.Speed       = GPIO_SPEED_FREQ_LOW;
    gpio.Alternate   = TERM_UART_AF;
    SYSTEM_GPIO_INIT(gpio, TERM_TX_PORT, TERM_TX_PIN);
    SYSTEM_GPIO_INIT(gpio, TERM_RX_PORT, TERM_RX_PIN);
    // UART
    TERM_UART_CLK_ENABLE();
    term.uart.Instance                       = TERM_UART;
    term.uart.Init.BaudRate                  = 115200;
    term.uart.Init.WordLength                = UART_WORDLENGTH_8B;
    term.uart.Init.StopBits                  = UART_STOPBITS_1;
    term.uart.Init.Parity                    = UART_PARITY_NONE;
    term.uart.Init.Mode                      = UART_MODE_TX_RX;
    term.uart.Init.HwFlowCtl                 = UART_HWCONTROL_NONE;
    term.uart.Init.OverSampling              = UART_OVERSAMPLING_16;
    term.uart.Init.OneBitSampling            = UART_ONE_BIT_SAMPLE_DISABLE;
    term.uart.Init.ClockPrescaler            = UART_PRESCALER_DIV1;
    term.uart.AdvancedInit.AdvFeatureInit    = UART_ADVFEATURE_NO_INIT;
    HAL_UART_Init(&term.uart);
    term_out_msg(TITLE " V" VERSION " - " COPYRIGHT);

}

void term_task()
{
    if (TERM_IS_RX_NOT_EMPTY()) {
        char key = TERM_GET_RX_REG();
        switch(key){
            case 'h':
            case 'H':
            case '?':
                // HELP
                term_out_msg("HELP");
                term_out_msg(" h  : help");
                term_out_msg(" a  : new address");
                term_out_msg(" b  : boot mode");
                term_out_msg(" r  : reboot");
                term_out_msg(" s  : shutdown");
                term_out_msg(" z  : reset to factory");
                break;
            case 'a':
                {
                    // NEW ADDRESS
                    term_out_str("New addr : ");
                    uint32_t val;
                    if (term_in_uint8(&val) == 0){ term_out_msg("abort !"); return; }
                    term_out_str("\r\n");
                    cfg.factory.addr = (uint8_t)val;
                    if (cfg_write_factory()){
                        term_out_msg("OK");
                    }else{
                        term_out_msg("ERR");
                    }
                }
                break;
            case 'b':
                {
                    // BOOT MODE
                    term_out_str("Boot mode [a=AUTO/r=RF] : ");
                    char c = term_in_char();
                    if (c == 0){
                        term_out_msg("abort !");
                        return;
                    }
                    if (c == 'a'){
                        term_out_msg("AUTO");
                        cfg.user.boot = CFG_BOOT_AUTO;
                    }else if (c == 'r'){
                        term_out_msg("RF");
                        cfg.user.boot = CFG_BOOT_RF;
                    }else{
                        term_out_str("\r\n");
                        return;
                    }
                    if (cfg_write_user()){
                        term_out_msg("OK");
                    }else{
                        term_out_msg("ERR");
                    }
                    system_reboot();
                }
                break;
            case 'r':
                // REBOOT
                term_out_str("Reboot [Y/n] : ");
                if (term_in_char() != 'Y'){ term_out_str("abort !\r\n"); return; }
                term_out_msg("\r\nOK");
                system_reboot();
                break;
            case 's':
                // SHUTDOWN
                term_out_str("Shutdown [Y/n] : ");
                if (term_in_char() != 'Y'){ term_out_str("abort !\r\n"); return; }
                term_out_msg("\r\nOK");
                system_shutdown();
                break;
            case 'z':
                // RESET
                term_out_str("Reset [Y/n] : ");
                if (term_in_char() != 'Y'){ term_out_str("abort !\r\n"); return; }
                term_out_msg("\r\nOK");
                cfg_user_reset();
                system_reboot();
                break;
        }
    }
}

// ============================================================================
//                                 INPUT
// ============================================================================

char term_in_char(void)
{
    uint32_t tick = HAL_GetTick();
    while(TERM_IS_RX_NOT_EMPTY() == 0){
        if ((HAL_GetTick() - tick) >= 10000) {
            return 0;
        }
    }
    return TERM_GET_RX_REG();
}

int term_in_uint8(uint32_t *val)
{
    char c;
    *val = 0;
    while(1) {
        c = term_in_char();
        if (c == 0){
            return false;
        }else if(c == '\r' || c == '\n') {
            break;
        }else if(c >= '0' && c <= '9') {
            if (*val * 10 + (c - '0') <= 255) {
                term_out_char(c); // Echo
                *val = *val * 10 + (c - '0');
            }
        }
    }
    return true;
}

// ============================================================================
//                                 OUTPUT
// ============================================================================

void term_out_char(char c)
{
    while(TERM_IS_TX_EMPTY()==0);
    TERM_SET_TX_REG(c);
}

void term_out_str(char *str)
{
    while(*str != 0){
        term_out_char(*str);
        str++;
    }
}

void term_out_msg(char *str)
{
    term_out_str(str);
    term_out_str("\r\n");
}

void term_out_uint(uint32_t val)
{
    char buf[11];
    int8_t i = 9;
    buf[10] = '\0';
    if(val == 0){
        term_out_char('0');
        return;
    }
    while(val > 0) {
        buf[i--] = (val % 10) + '0';
        val /= 10;
    }
    term_out_str(&buf[i + 1]);
}

void term_out_int(int32_t val)
{
    if (val < 0){
        term_out_char('-');
        term_out_uint((uint32_t)(-val));
    }else{
        term_out_uint((uint32_t)val);
    }
}

void term_out_hex(uint32_t val, uint8_t digits)
{
    const char hex_chars[] = "0123456789ABCDEF";
    while (digits-- > 0) {
        uint8_t nibble = (val >> (digits * 4)) & 0xF;
        term_out_char(hex_chars[nibble]);
    }
}

void term_out_flush()
{
    while(TERM_IS_TX_EMPTY()==0);
    while(TERM_IS_TX_COMPLET());
}

void term_field_uint(char *name, uint32_t value)
{
    term_out_char(' ');
    term_out_str(name);
    term_out_str(" : ");
    term_out_uint(value);
    term_out_str("\r\n");

}


/*
void term_debug_mem(size_t offset, uint8_t *base, size_t sz)
{
    char c;
    size_t i, m, n = 0;
    uint8_t *ptr = base;
    while(n < sz){
        m = sz - n;
        if (m > 16) m = 16;
        printf("%08X ", offset);
        for(i = 0; i < m; i++){
            printf("%02X ", *(ptr+i));
        }
        for(i = 0; i < m; i++){
            c = *(ptr+i);
            printf("%c", (((c < ' ') || (c > '~')) ? '.' : c) );
        }
        printf("\r\n");
        n += m;
        ptr += m;
        offset += m;

    }
}
*/

// ============================================================================
//                                 PRINTF STUB
// ============================================================================

int __io_putchar(int ch)
{
    while(TERM_IS_TX_EMPTY()==0);
    TERM_SET_TX_REG(ch);
    return(ch);
}
