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

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

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

#include <tool.h>
#include <eeprom.h>
#include <disp.h>
#include <uart.h>
#include <temp.h>
#include <relay.h>
#include <timer.h>
#include <adc.h>
#include <button.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

#define CONTR_DELAY_OFF      0
#define CONTR_DELAY_100MS   22
#define CONTR_DELAY_200MS   44
#define CONTR_DELAY_300MS   66
#define CONTR_DELAY_500MS  110

#define CONR_FREQARR_SIZE 32
#define CONR_VCCARR_SIZE   4
#define CONR_PWRARR_SIZE  32
#define CONR_DISPSTR_SIZE 20


typedef struct {
    uint8_t band;
    uint16_t freq;
    uint16_t vcc;

    uint16_t freq_arr[CONR_FREQARR_SIZE];
    size_t freq_pos;

    uint16_t fwd_arr[CONR_PWRARR_SIZE];
    size_t fwd_pos;
    uint16_t fwd_awg;

    uint16_t rev_arr[CONR_PWRARR_SIZE];
    size_t rev_pos;
    uint16_t rev_awg;

    uint16_t swr_arr[CONR_PWRARR_SIZE];
    size_t swr_pos;
    uint16_t swr_awg;

    char disp_str[CONR_DISPSTR_SIZE];
    volatile uint16_t osc;

    uint16_t tx_delay;
    uint16_t tx_cnt;
} contr_t;

contr_t contr;

void contr_switch_band(void);
void contr_set_band(uint8_t band);
void contr_write_band(void);
void contr_read_band(void);
char* contr_get_bandname();

void contr_init(void) {
    contr.band = CONTR_BAND_OFF;
    contr.vcc = 0;

    contr.fwd_pos = 0;
    contr.fwd_awg = 0;
    for (size_t i = 0; i < CONR_PWRARR_SIZE; i++) {
        contr.fwd_arr[i] = 0;
    }
    contr.rev_pos = 0;
    contr.rev_awg = 0;
    for (size_t i = 0; i < CONR_PWRARR_SIZE; i++) {
        contr.rev_arr[i] = 0;
    }
    contr.swr_pos = 0;
    contr.swr_awg = 0;
    for (size_t i = 0; i < CONR_PWRARR_SIZE; i++) {
        contr.swr_arr[i] = 0;
    }

    contr.disp_str[0] = '\0';

    contr.tx_delay = CONTR_DELAY_200MS;
    contr.tx_cnt = 0;
}
void contr_setup(void) {
    contr_read_band();
}

#define CONTR_VCC_SCALE 270
void contr_measure_vcc(void) {
    contr.vcc = adc_read(ACD_CHANELL3) / CONTR_VCC_SCALE;
}

uint16_t contr_get_vcc(void) {
    return contr.vcc;
}

#define CONTR_FWD_SCALE 39
#define CONTR_REV_SCALE 39

void contr_measure_swr(void) {
    uint16_t fwd = adc_read(ACD_CHANELL1) / CONTR_FWD_SCALE;
    uint16_t rev = adc_read(ACD_CHANELL0) / CONTR_REV_SCALE;
    contr.fwd_arr[contr.fwd_pos++] = fwd;
    if ((contr.fwd_pos) > CONR_PWRARR_SIZE) {
        contr.fwd_pos = 0;
    }
    contr.rev_arr[contr.rev_pos++] = rev;
    if ((contr.rev_pos) > CONR_PWRARR_SIZE) {
        contr.rev_pos = 0;
    }

}
void contr_calc_swr(void) {
    uint32_t fwd_awg = 0;
    for (size_t i = 0; i < CONR_PWRARR_SIZE; i++) {
        fwd_awg += contr.fwd_arr[i];
    }
    fwd_awg = (fwd_awg + CONR_PWRARR_SIZE - 1) / CONR_PWRARR_SIZE;
    contr.fwd_awg = (uint16_t)(fwd_awg);

    uint32_t rev_awg = 0;
    for (size_t i = 0; i < CONR_PWRARR_SIZE; i++) {
        rev_awg += contr.rev_arr[i];
    }
    rev_awg = (rev_awg + CONR_PWRARR_SIZE - 1) / CONR_PWRARR_SIZE;
    contr.rev_awg = (uint16_t)(rev_awg);

    uint32_t pwr_diff = (fwd_awg - rev_awg);
    uint32_t swr = 0;
    if (pwr_diff != 0) {
        swr = ((fwd_awg + rev_awg) * 100) / pwr_diff;
    }

    contr.swr_arr[contr.swr_pos++] = swr;
    if ((contr.swr_pos) > CONR_PWRARR_SIZE) {
        contr.swr_pos = 0;
    }
    uint32_t swr_awg = 0;
    for (size_t i = 0; i < CONR_PWRARR_SIZE; i++) {
        swr_awg += contr.swr_arr[i];
    }
    swr_awg /= CONR_PWRARR_SIZE;
    contr.swr_awg = (uint16_t)(swr_awg);
}

uint16_t contr_get_fwd(void) {
    return contr.fwd_awg;
}
uint16_t contr_get_rev(void) {
    return contr.rev_awg;
}
uint16_t contr_get_swr(void) {
    return contr.swr_awg;
}


#define CONTR_FREQ_SCALE 11
void contr_measure_freq(void) {
    uint16_t osc = contr.osc;
    contr.osc = 0;
    contr.freq_arr[contr.freq_pos++] = osc;
    if ((contr.freq_pos) > CONR_FREQARR_SIZE) {
        contr.freq_pos = 0;
    }
    uint32_t tmp = 0;
    for (size_t i = 0; i < CONR_FREQARR_SIZE; i++) {
        tmp += contr.freq_arr[i];
    }
    contr.freq = tmp / CONR_FREQARR_SIZE / CONTR_FREQ_SCALE;
}

uint16_t contr_get_freq(void) {
    return contr.freq;
}

void contr_tx_handle(void) {
    if (ptt_is_pressed()) {
        contr.tx_cnt = contr.tx_delay;
        buzzer_on();
    }
    if (contr.tx_cnt > 0) {
        contr.tx_cnt--;
    } else if (contr.tx_cnt == 0) {
        buzzer_off();
    }
}



void contr_switch_delay(void) {
    switch (contr.tx_delay) {
    case CONTR_DELAY_100MS:
        contr.tx_delay = CONTR_DELAY_200MS;
        break;
    case CONTR_DELAY_200MS:
        contr.tx_delay = CONTR_DELAY_300MS;
        break;
    case CONTR_DELAY_300MS:
        contr.tx_delay = CONTR_DELAY_500MS;
        break;
    case CONTR_DELAY_500MS:
        contr.tx_delay = CONTR_DELAY_100MS;
        break;
    default:
        contr.tx_delay = CONTR_DELAY_200MS;
        break;
    }
}

char* contr_get_delayname(void) {
    switch (contr.tx_delay) {
    case CONTR_DELAY_100MS:
        return "100ms";
        break;
    case CONTR_DELAY_200MS:
        return "200ms";
        break;
    case CONTR_DELAY_300MS:
        return "300ms";
        break;
    case CONTR_DELAY_500MS:
        return "500ms";
        break;
    default:
        break;
    }
    return "  0ms";
}



ISR(TIMER0_OVF_vect) {
    button_handle();
    contr_measure_vcc();
    contr_measure_swr();
    contr_tx_handle();
    contr_calc_swr();
    uart_handle();
}

/*
ISR(TIMER1_OVF_vect) {
    contr.osc++;
    REG_SETUP(TCNT1, TIMER1_PRESIZE);
}
*/

void contr_switch_band(void) {
    switch (contr.band) {
    case CONTR_BAND_10M:
        contr_set_band(CONTR_BAND_20M);
        break;
    case CONTR_BAND_20M:
        contr_set_band(CONTR_BAND_40M);
        break;
    case CONTR_BAND_40M:
        contr_set_band(CONTR_BAND_80M);
        break;
    case CONTR_BAND_80M:
        contr_set_band(CONTR_BAND_10M);
        break;
    case CONTR_BAND_OFF:
        contr_set_band(CONTR_BAND_10M);
        break;
    default:
        contr_set_band(CONTR_BAND_OFF);
        break;
    }
}

void contr_set_band(uint8_t band) {
    ampl_off();
    contr.band = band;

    switch (contr.band) {
    case CONTR_BAND_10M:
        filter80m_off();
        filter40m_off();
        filter20m_off();
        filter10m_on();
        break;
    case CONTR_BAND_20M:
        filter80m_off();
        filter40m_off();
        filter10m_off();
        filter20m_on();
        break;
    case CONTR_BAND_40M:
        filter80m_off();
        filter20m_off();
        filter10m_off();
        filter40m_on();
        break;
    case CONTR_BAND_80M:
        filter40m_off();
        filter20m_off();
        filter10m_off();
        filter80m_on();
        break;
    default:
        break;
    }
    contr_write_band();
    atten_off();
}


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

void contr_button_eval(void) {
    uint8_t push_count = button_get();
    switch (push_count) {
    case CONTR_KEY_SWDELAY:
        contr_switch_delay();
        break;
    case CONTR_KEY_SWBAND:
        contr_switch_band();
        break;
    case CONTR_KEY_BUZZER:
        buzzer_onoff();
        break;
    case CONTR_KEY_FUN:
        fan_onoff();
        break;
    }
}

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

void contr_show_band(void) {
    disp_string(0, 13, contr_get_bandname());
}

void contr_show_vcc(void) {
    sprintf(contr.disp_str, "%3.1fv", (float)contr_get_vcc() / 10);
    disp_string(3, 11, contr.disp_str);
}

void contr_show_ptt(void) {
    if (ptt_is_pressed()) {
        disp_string(3, 0, "TX");
    } else {
        disp_string(3, 0, "  ");
    }
}

void contr_show_button(void) {
    if (button_is_pressed()) {
        disp_string(3, 3, "BT");
    } else {
        disp_string(3, 3, "  ");
    }
    //if (button.strokes_ended) {
        sprintf(contr.disp_str, "%1u", button.push_counter);
        disp_string(3, 5, contr.disp_str);
    //}
}


void contr_show_freq(void) {
    uint16_t freq = contr_get_freq();
    sprintf(contr.disp_str, "%2uM", freq);
    disp_string(0, 0, contr.disp_str);
}

void contr_show_delay(void) {
    disp_string(0, 0, contr_get_delayname());
}

void contr_show_fwd(void) {
    sprintf(contr.disp_str, "F=%4.1f", (float)contr_get_fwd() / 100.0F);
    disp_string(1, 0, contr.disp_str);
}

void contr_show_rev(void) {
    sprintf(contr.disp_str, "R=%4.1f", (float)contr_get_rev() / 100.F);
    disp_string(1, 8, contr.disp_str);
}

void contr_show_swr(void) {
    sprintf(contr.disp_str, "S=%2.2f", (float)contr_get_swr() / 100.0F);
    disp_string(2, 0, contr.disp_str);
}

void contr_main(void) {
    contr_show_logo();
    uint16_t counter = 0;
    while (true) {
        contr_show_button();
        contr_show_ptt();
        contr_show_delay();
        contr_button_eval();
        contr_show_vcc();
        //contr_show_fwd();
        //contr_show_rev();
        //contr_show_swr();
        contr_show_band();
        counter++;
        _delay_ms(150);
    }
}

#define CONTR_BAND_EEPROM_ADDR 0x00

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

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

char* contr_get_bandname() {
    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;
    default:
        bandstr = "UNK";
        break;
    }
    return bandstr;
}
