/*
 * Copyright 2017 Oleg Borodin <onborodin@gmail.com>
 */

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <avr/interrupt.h>
#include <util/delay.h>

#include <eeprom.h>
#include <disp.h>
#include <tool.h>
#include <uart.h>
#include <temp.h>
#include <relay.h>
#include <timer.h>

#include <contr.h>

#define CONTR_BAND_OFF  0x01
#define CONTR_BAND_10M  0x02
#define CONTR_BAND_20M  0x03
#define CONTR_BAND_40M  0x04
#define CONTR_BAND_80M  0x05

contr_t contr;

void contr_key_init(void);
void contr_key_handle(void);
void contr_key_reset(void);

void contr_switch_band(void);
void contr_set_band(uint8_t band);

void contr_vcc_measure(void);
float contr_vcc_calc(void);

void contr_fwd_measure(void);
float contr_fwd_calc(void);

void contr_rev_measure(void);
float contr_rev_calc(void);

void contr_write_band(void);
void contr_read_band(void);


ISR(TIMER0_OVF_vect) {
    contr_key_handle();
    contr_fwd_measure();
    contr_rev_measure();
    contr_vcc_measure();

    volatile uint8_t xchar;
    while ((xchar = fifo_getc(&uart_outbuf)) > 0) {
        while (!REG_BIT_ISUP(UCSR0A, UDRE0));
        UDR0 = xchar;
    }
}


void contr_init(void) {
    contr.temp = 0.0F;
    contr.band = CONTR_BAND_OFF;

    contr.vcc_pos = 0;
    for (size_t i = 0; i < VCC_SIZE; i++) {
            contr.vcc[i] = 0;
    }
    contr.fwd_pos = 0;
    for (size_t i = 0; i < FWD_SIZE; i++) {
            contr.fwd[i] = 0;
    }
    contr.rev_pos = 0;
    for (size_t i = 0; i < REV_SIZE; i++) {
            contr.rev[i] = 0;
    }
    contr.key_was_pressed = false;
    contr.key_time_counter = 0;
    contr.key_tap_counter = 0;
    contr.key_strokes_ended = false;
}

void contr_setup(void) {
    contr_read_band();



    contr_key_init();
    adc_init();
    timer0_init();
}


void contr_switch_band(void) {
    relaytx_off();
    _delay_ms(100);
    relay80m_off();
    relay40m_off();
    relay20m_off();
    relay10m_off();
    _delay_ms(100);

    switch (contr.band) {
    case CONTR_BAND_10M:
        relay20m_on();
        contr.band = CONTR_BAND_20M;
        break;
    case CONTR_BAND_20M:
        relay40m_on();
        contr.band = CONTR_BAND_40M;
        break;
    case CONTR_BAND_40M:
        relay80m_on();
        contr.band = CONTR_BAND_80M;
        break;
    case CONTR_BAND_80M:
        relay10m_on();
        contr.band = CONTR_BAND_10M;
        break;
    default:
        break;
    }
    contr_write_band();
    _delay_ms(100);
    atten_on();
    _delay_ms(100);
}

char* contr_get_bandname(uint8_t band) {
    char* bandstr = NULL;
    switch (contr.band) {
    case CONTR_BAND_10M:
        bandstr = "10m";
        break;
    case CONTR_BAND_20M:
        bandstr = "20m";
        break;
    case CONTR_BAND_40M:
        bandstr = "40m";
        break;
    case CONTR_BAND_80M:
        bandstr = "80m";
        break;
    case CONTR_BAND_OFF:
        bandstr = "OFF";
        break;
    }
    return bandstr;
}

#define CONTR_KEY_SWBAND  2
#define CONTR_KEY_BUZZER  4
#define CONTR_KEY_FUN     5

void contr_key_eval(void) {
    if (contr.key_strokes_ended) {
        switch (contr.key_tap_counter) {
        case CONTR_KEY_SWBAND:
            contr_key_reset();
            contr_switch_band();
            break;
        case CONTR_KEY_BUZZER:
            contr_key_reset();
            buzzer_onoff();
            break;
        case CONTR_KEY_FUN:
            contr_key_reset();
            fan_onoff();
            break;
        default:
            contr_key_reset();
            break;
        }
    }
}

void contr_show_logo(void) {
    char* dispstr = "Made by R2FDX";
    disp_string(1, 1, dispstr);
}

void contr_temp_measure(void) {
    contr.temp = ds18b20_get_temp();
}

float contr_temp_calc(void) {
    return contr.temp;
}

void contr_main(void) {
    uint16_t counter = 0;
    char dispstr[17] = { '\0' };
    char* bandstr = NULL;

    disp_clear();
    contr_show_logo();
    _delay_ms(500);
    disp_clear();

    printf("READY>\n\r# ");

    while (true) {
        contr_key_eval();

        if (contr.key_tap_counter) {
            sprintf(dispstr, "%2d", contr.key_tap_counter);
            disp_string(2, 12, dispstr);
        } else {
            sprintf(dispstr, "  ");
            disp_string(2, 12, dispstr);
        }

        if ((counter % 64) == 1) {
            contr_temp_measure();
        }

        if ((counter % 4) == 1) {

            disp_string(0, 12, contr_get_bandname(contr.band));

            sprintf(dispstr, "%3.2fV", contr_vcc_calc());
            disp_string(0, 0, dispstr);

            float fwd = contr_fwd_calc();
            sprintf(dispstr, "FWD %3.1fW  ", fwd);
            disp_string(1, 2, dispstr);

            float rev = contr_rev_calc();
            sprintf(dispstr, "REV %3.1fW  ", rev);
            disp_string(2, 2, dispstr);


            float vswr = 0.0F;
            if (fwd > 0.1F) {
                vswr = (fwd + rev) / (fwd - rev);
            }
            sprintf(dispstr, "%3.1f   ", vswr);
            disp_string(3, 10, dispstr);

            sprintf(dispstr, "%3.2fC", contr_temp_calc());
            disp_string(3, 0, dispstr);
        }
        counter++;
        _delay_ms(100);
    }
}

void contr_vcc_measure(void) {
    REG_SETUP_BIT(ADMUX, MUX0);
    REG_SETUP_BIT(ADMUX, MUX1);
    REG_SETDOWN_BIT(ADMUX, MUX2);
    REG_SETDOWN_BIT(ADMUX, MUX3);

    REG_SETUP_BIT(ADCSRA, ADSC);
    while (ADCSRA & BIT(ADSC));
    uint16_t lval = (uint16_t)ADCL;
    uint16_t hval = (uint16_t)ADCH;
    hval = (hval << 8) & 0xFF00;
    lval = lval & 0x00FF;

    if (++contr.vcc_pos > VCC_SIZE) {
        contr.vcc_pos = 0;
    }
    contr.vcc[contr.vcc_pos] = (hval | lval);
}

#define CONTR_VCC_SCALE 2673.0F

float contr_vcc_calc(void) {
    uint32_t vcc = 0;
    uint8_t n = 0;
    for (size_t i = 0; i < VCC_SIZE; i++) {
        if (contr.vcc[i]) {
            n++;
            vcc += contr.vcc[i];
        }
    }
    float xvcc = 0.0F;
    if (n) {
        vcc /= n;
        xvcc = (float)vcc / CONTR_VCC_SCALE;
    }
    return xvcc;
}

void contr_fwd_measure(void) {
    REG_SETUP_BIT(ADMUX, MUX0);
    REG_SETDOWN_BIT(ADMUX, MUX1);
    REG_SETDOWN_BIT(ADMUX, MUX2);
    REG_SETDOWN_BIT(ADMUX, MUX3);

    REG_SETUP_BIT(ADCSRA, ADSC);
    while (ADCSRA & BIT(ADSC));
    uint16_t lval = (uint16_t)ADCL;
    uint16_t hval = (uint16_t)ADCH;
    hval = (hval << 8) & 0xFF00;
    lval = lval & 0x00FF;

    if (++contr.fwd_pos > FWD_SIZE) {
        contr.fwd_pos = 0;
    }
    contr.fwd[contr.fwd_pos] = (hval | lval);
}

float contr_fwd_calc(void) {
    uint32_t fwd = 0;
    uint8_t n = 0;
    for (size_t i = 0; i < FWD_SIZE; i++) {
        fwd += contr.fwd[i];
    }
    float xfwd = 0.0F;
    xfwd = (float)fwd / 3760.0F / FWD_SIZE;
    return xfwd;
}

void contr_rev_measure(void) {
    REG_SETDOWN_BIT(ADMUX, MUX0);
    REG_SETDOWN_BIT(ADMUX, MUX1);
    REG_SETDOWN_BIT(ADMUX, MUX2);
    REG_SETDOWN_BIT(ADMUX, MUX3);

    REG_SETUP_BIT(ADCSRA, ADSC);
    while (ADCSRA & BIT(ADSC));
    uint16_t lval = (uint16_t)ADCL;
    uint16_t hval = (uint16_t)ADCH;
    hval = (hval << 8) & 0xFF00;
    lval = lval & 0x00FF;

    if (++contr.rev_pos > REV_SIZE) {
        contr.rev_pos = 0;
    }
    contr.rev[contr.rev_pos] = (hval | lval);
}

float contr_rev_calc(void) {
    uint32_t rev = 0;
    for (size_t i = 0; i < REV_SIZE; i++) {
        rev += contr.rev[i];
    }
    float xrev = 0.0F;
    xrev = (float)rev / 164.0F / REV_SIZE;
    return xrev;
}


void contr_write_band(void) {
    eeprom_write_bytes(0x00, &contr.band, sizeof(contr.band));
}

void contr_read_band(void) {
    eeprom_read_bytes(0x00, &contr.band, sizeof(contr.band));
}

void contr_key_init(void) {
    /* D3 PD3: Set key input */
    REG_SETDOWN_BIT(DDRD, PD3);
    REG_SETUP_BIT(PORTD, PD3);
}

bool contr_key_is_pressed(void) {
    if (REG_BIT_VALUE(PIND, PD3)) {
        return true;
    }
    return false;
}

#define CONTR_KEY_CNTINPRESS      100
#define CONTR_KEY_CNTRELEASE      100
#define CONTR_KEY_CNTUNTAP_READY 1200
#define CONTR_KEY_CNTUNTAP_CLEAN 4000


void contr_key_handle(void) {
    if (!contr.key_was_pressed) {
        if (contr_key_is_pressed()) {
            contr.key_time_counter++;
            contr.key_time_untap++;
        }
        if (contr.key_time_counter > CONTR_KEY_CNTINPRESS) {
            contr.key_time_counter = 0;
            contr.key_was_pressed = true;
        }
    } else {
        if (!contr_key_is_pressed()) {
            contr.key_time_counter++;
        }
        if (contr.key_time_counter > CONTR_KEY_CNTRELEASE) {
            contr.key_time_counter = 0;
            contr.key_was_pressed = false;
            contr.key_tap_counter++;
            contr.key_time_untap = 0;
        }
    }
    if (contr.key_tap_counter > 0) {
        contr.key_time_untap++;
        if (contr.key_time_untap > CONTR_KEY_CNTUNTAP_READY) {
            contr.key_strokes_ended = true;
        }
        if (contr.key_time_untap > CONTR_KEY_CNTUNTAP_CLEAN) {
            contr.key_was_pressed = false;
            contr.key_tap_counter = 0;
            contr.key_time_untap = 0;
            contr.key_strokes_ended = false;
        }
    } else {
        contr.key_time_untap = 0;
    }
}

void contr_key_reset(void) {
    contr.key_was_pressed = false;
    contr.key_tap_counter = 0;
    contr.key_time_untap = 0;
    contr.key_strokes_ended = false;
}
