#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "utils.h"
#include "main.h"
#include "adc.h"
#include "uart.h"

volatile uint16_t syscounter = 0;
uint16_t voltage = 0;
uint16_t current_in = 0;
uint16_t current_out = 0;

uint8_t overvoltage_counter1 = 0;
uint8_t overvoltage_off_counter1 = 0;
uint8_t overvoltage_counter2 = 0;
uint8_t overvoltage_off_counter2 = 0;
uint8_t undervoltage_counter = 0;
uint8_t undervoltage_off_counter = 0;


static void timer_init(void) {
	// clock is 8MHz
	TCCR1B |= _BV(WGM12) | _BV(CS11) | _BV(CS10) ; // CTC Mode for Timer 1 (16Bit) with prescale of 64
	OCR1A   = 1250;                                // 100Hz
	TIMSK   = _BV(OCIE1A);
	sei();	// enable interrupts
}

static void ports_init(void) {
	DDR_SW |= _BV(LOADSW) | _BV(GENSW) | _BV(DUMPSW);
	PORT_SW &= ~(_BV(LOADSW) | _BV(GENSW) | _BV(DUMPSW));
}

void measure(void) {
    static int16_t temp;

    voltage = adc_read_avg(AD_V, 4);
    voltage *= VOLTAGE_PER_TICK;

    temp = adc_read_avg(AD_I_GEN, 4);
    temp -= CURRENT_OFFSET;
    if(temp < 0) temp = 0;
    current_in = temp * CURRENT_PER_TICK;

    temp = adc_read_avg(AD_I_LOAD, 4);
    temp -= CURRENT_OFFSET;
    if(temp < 0) temp = 0;
    current_out = temp * CURRENT_PER_TICK;
}

uint16_t get_power(uint16_t voltage, int16_t currents) {
    return (voltage/100 * (currents/100)) / 100 ;
}


void pretty_print_all_values(void) {
    uart_puts_P("Voltage:   ");
    uart_print_uint16(voltage);
    uart_puts_P("mV\r\n");

    uart_puts_P("Load:      ");
    uart_print_uint16(current_out);
    uart_puts_P("mA  ");
    uart_print_uint16( get_power(voltage, current_out));
    uart_puts_P("W\r\n");

    uart_puts_P("Generator: ");
    uart_print_uint16(current_in);
    uart_puts_P("mA  ");
    uart_print_uint16(get_power(voltage, current_in));
    uart_puts_P("W\r\n");

    uart_puts_P("switches (load, dump, gen): ");
    uart_putc(48 + (IS_LOAD_ON >> LOADSW));
    uart_putc(',');
    uart_putc(48 + (IS_DUMP_ON >> DUMPSW));
    uart_putc(',');
    uart_putc(48 + (IS_GEN_ON >> GENSW));
    uart_puts_P("\r\n");
}

void handle_over_and_undervoltage(void) {
    if(voltage > OVERVOLTAGE1) {
        overvoltage_off_counter1 = 0;
        if(overvoltage_counter1<OVERVOLTAGE_TIMEOUT1) overvoltage_counter1++;
    } else {
        overvoltage_counter1 = 0;
        if(overvoltage_off_counter1<OVERVOLTAGE_TIMEOUT1) overvoltage_off_counter1++;
    }

    if(voltage > OVERVOLTAGE2) {
        overvoltage_off_counter2 = 0;
        if(overvoltage_counter2<OVERVOLTAGE_TIMEOUT2) overvoltage_counter2++;
    } else {
        overvoltage_counter2 = 0;
        if(overvoltage_off_counter2<OVERVOLTAGE_TIMEOUT2) overvoltage_off_counter2++;
    }

    if(voltage < UNDERVOLTAGE) {
        undervoltage_off_counter = 0;
        if(undervoltage_counter<UNDERVOLTAGE_TIMEOUT) undervoltage_counter++;
    } else {
        undervoltage_counter = 0;
        if(undervoltage_off_counter<UNDERVOLTAGE_TIMEOUT) undervoltage_off_counter++;
    }


    if(overvoltage_counter1 >= OVERVOLTAGE_TIMEOUT1) {
        overvoltage_off_counter1 = 0;
        DUMP_ON;
    }

    if(overvoltage_off_counter1 >= OVERVOLTAGE_TIMEOUT1) {
        overvoltage_counter1 = 0;
        DUMP_OFF;
    }

    if(overvoltage_counter2 >= OVERVOLTAGE_TIMEOUT2) {
        overvoltage_off_counter2 = 0;
        GEN_OFF;
    }

    if(overvoltage_off_counter2 >= OVERVOLTAGE_TIMEOUT2) {
        overvoltage_counter2 = 0;
        GEN_ON;
    }

    if(undervoltage_counter >= UNDERVOLTAGE_TIMEOUT) {
        undervoltage_off_counter = 0;
        LOAD_OFF;
    }

    if(undervoltage_off_counter >= UNDERVOLTAGE_TIMEOUT) {
        undervoltage_counter = 0;
        LOAD_ON;
    }


#ifdef DEBUG
    uart_puts_P("ov1=");
    uart_print_uint8(overvoltage_counter1);
    uart_puts_P("  ovo1=");
    uart_print_uint8 (overvoltage_off_counter1);
    uart_puts_P("\r\n");

    uart_puts_P("ov2=");
    uart_print_uint8(overvoltage_counter2);
    uart_puts_P("  ovo2=");
    uart_print_uint8 (overvoltage_off_counter2);
    uart_puts_P("\r\n");

    uart_puts_P("uv =");
    uart_print_uint8(undervoltage_counter);
    uart_puts_P("  uvo =");
    uart_print_uint8(undervoltage_off_counter);
    uart_puts_P("\r\n");
#endif

}

static void work_uart(void) {
    uint16_t uart_char = uart_getc();

    if(uart_char != UART_NO_DATA) {
        switch(uart_char & 0xff) {
            case 'p':
                pretty_print_all_values();
                break;
            case 'a':
                uart_putc('A');
                uart_print_uint16(voltage);
                uart_putc(',');
                uart_print_uint16(current_in);
                uart_putc(',');
                uart_print_uint16(current_out);
                uart_putc(',');
                uart_print_uint16(get_power(voltage, current_in));
                uart_putc(',');
                uart_print_uint16(get_power(voltage, current_out));
                uart_putc(',');
                uart_putc(48 + (IS_LOAD_ON >> LOADSW));
                uart_putc(',');
                uart_putc(48 + (IS_DUMP_ON >> DUMPSW));
                uart_putc(',');
                uart_putc(48 + (IS_GEN_ON >> GENSW));
                uart_putc('B');
                break;
        }

    }
}


int main(void) {
	ports_init();
	adc_init();
	timer_init();
    uart_init(UART_BAUD_SELECT(19200,F_CPU));

	LOAD_OFF;
    GEN_ON;
    DUMP_OFF;


	while(1) {

        if(syscounter >= 100) {
            syscounter = 0;

            measure();

            //pretty_print_all_values();

            handle_over_and_undervoltage();
        }

        work_uart();
	}
	
	return(0);
}

// system timer
SIGNAL(TIMER1_COMPA_vect) {
	syscounter++;
	syscounter %= 60000;
}


