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

#define BUFSIZE 40
#define CURRENT_MAX     30000       // 30 Ampere is max

volatile uint16_t syscounter = 0;
volatile uint8_t digitbuffer[6] = { 0,0,0,0,0,0 };
volatile uint8_t leddigitbuffer[4] = { 0,0,0,0 };
uint8_t digit = 0;
uint8_t leddigit = 0;

// values send over uart from powerboard
uint16_t voltage = 0;
int16_t current_in = 0;
int16_t current_out = 0;
uint8_t dumpsw = 0; //TODO: make bitfield
uint8_t loadsw = 0; //TODO: make bitfield
uint8_t gensw = 0;  //TODO: make bitfield

uint16_t power_gen = 0;
uint16_t power_load = 0;

unsigned char data_count = 0;
unsigned char data_in[BUFSIZE];
char command_in[BUFSIZE];

const uint8_t segment_translate[10] = {
    63, 6, 91, 79, 102, 109, 125, 7, 127, 111
};

const uint8_t smallbar_translate[9] = {
    0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
};

const uint8_t bigbar_translate[15][2] = {
    { 0x00, 0xc0},
	{ 0x00, 0x60},
	{ 0x00, 0x30},
	{ 0x00, 0x18},
	{ 0x00, 0x0c},
	{ 0x00, 0x06},
	{ 0x00, 0x03},
	{ 0x01, 0x01},
	{ 0x03, 0x00},
	{ 0x06, 0x00},
	{ 0x0c, 0x00},
	{ 0x18, 0x00},
	{ 0x30, 0x00},
	{ 0x60, 0x00},
	{ 0xc0, 0x00}
};



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   = 250;    // 500Hz
	TIMSK   = _BV(OCIE1A);
	sei();	// enable interrupts
}

static void ports_init(void) {
    // make column / digit driver pins to output
    LEDDIG_DDR |= _BV(LEDS_MID1) | _BV(LEDS_MID2) | _BV(LEDS_LOAD) | _BV(LEDS_GEN);
    SEVENSEGDIG_DDR |= _BV(DIG0) | _BV(DIG1) | _BV(DIG2) | _BV(DIG3) | _BV(DIG4) | _BV(DIG5);

    // make data ports to output
    LED_DDR = 0xff;
    SEVENSEG_DDR = 0xff;

    SEVENSEGDIG_PORT = 0;
    SEVENSEG_PORT = 0;

    LEDDIG_PORT = 0;
    LED_PORT = 0;
}

static void print_sevenseg(uint8_t display, uint16_t value) {
    uint8_t d[3];
    d[2] = (value % 1000 / 100 );
    d[1] = (value % 100 / 10 );
    d[0] = (value % 10);

    if(display == 0) {
	    digitbuffer[0] = segment_translate[d[0]];
		if(d[1] == 0)
			digitbuffer[1] = 0x00;
		else
			digitbuffer[1] = segment_translate[d[1]];
		if(d[2] == 0)
			digitbuffer[2] = 0x00;
		else
			digitbuffer[2] = segment_translate[d[2]];
    } else {
		digitbuffer[3] = segment_translate[d[0]];
		if(d[1] == 0)	
			digitbuffer[4] = 0x00;
		else		
			digitbuffer[4] = segment_translate[d[1]];
		if(d[2] == 0)		
			digitbuffer[5] = 0x00;
		else
			digitbuffer[5] = segment_translate[d[2]];
        
    }
}

static void print_bar(uint8_t display, uint16_t value, uint16_t max) {
    uint16_t x = 0;
	float temp = max;

    x =  (uint16_t)( (value / temp) * 8.0);

    x%=8;

    switch(display) {
        case LEDS_LOAD:
            leddigitbuffer[2] = smallbar_translate[x];
            break;
        case LEDS_GEN:
            leddigitbuffer[3] = smallbar_translate[x];
            break;
    }

}
static void print_bar_bottom(uint16_t in, uint16_t out, uint16_t max) {
	uint16_t x;
	float temp = max;
/*
	uart_puts("in= ");
    uart_print_uint16(in);
	uart_puts("  out= ");
    uart_print_uint16(out);
	uart_puts("  max= ");
    uart_print_uint16(max);*/


	if(out == in) {
		leddigitbuffer[0] = bigbar_translate[7][0];
		leddigitbuffer[1] = bigbar_translate[7][1];
		return;
	} else if( out > in ) {		
	    x =  (uint16_t)( ((out - in) / temp) * 8.0);
		x = x+8;

	} else {
		x =  (uint16_t)( ((in - out) / temp) * 8.0);
		x = 7-x;
	}	

	if(x > 14) x = 14;
	if(x < 0) x = 0;		

/*	uart_puts_P("  x= ");
    uart_print_uint16(x);
    uart_puts_P("\r\n");
*/

	leddigitbuffer[0] = bigbar_translate[x][0];
	leddigitbuffer[1] = bigbar_translate[x][1];

}

void process_command() {
    if(strstr(command_in,"A") != NULL) {
        // we have an A and B (from check in work_uart()
        // so our message should be complete and consist of:
        // A$voltage,$current_in,$current_out,$power_in,$power_out,loadsw,dumpsw,gensw\n

        //A12.5,65464,00000,00000,00000,1,0,1B

        char *token;
        uint8_t tokencounter = 0;

        char *start = strrchr(command_in, 'A');

        // remove first (B is ignored by atoi)
        start++;

        token = strtok(start, ",");

        while( token ) {
            switch(tokencounter) {
                case 0:
                    voltage = atoi(token);
                    break;
                case 1:
                    current_in = atoi(token);
                    break;
                case 2:
                    current_out = atoi(token);
                    break;
                case 3:
                    power_gen = atoi(token);
                    break;
                case 4:
                    power_load = atoi(token);
                    break;
                case 5:
                    if(atoi(token) == 1) loadsw = 1;
                    else loadsw = 0;
                    break;
                case 6:
                    if(atoi(token) == 1) dumpsw = 1;
                    else dumpsw = 0;
                    break;
                case 7:
                    if(atoi(token) == 1) gensw = 1;
                    else gensw = 0;
                    break;
            }

            tokencounter++;
            token = strtok(NULL, ",");
        }
    }
}

static void work_uart(){
    unsigned int c = uart_getc();

    if ( !(c & UART_NO_DATA) ) {
        data_in[data_count] = c;

        if (data_in[data_count] == 'B') {
            data_count = 0;

            memcpy(command_in, data_in, BUFSIZE);
            memset(data_in, 0, BUFSIZE);

            process_command();
        } else {
            data_count++;
        }
    }
}


static void demo_display(void) {

	for(uint8_t i = 0; i< 16;i++) {
		leddigitbuffer[0] = bigbar_translate[i][0];
		leddigitbuffer[1] = bigbar_translate[i][1];
		wait(5);
	}

	for(uint8_t i = 0; i< 9;i++) {
		leddigitbuffer[2] = smallbar_translate[i];
		wait(5);
	}

	for(uint8_t i = 0; i< 9;i++) {
		leddigitbuffer[3] = smallbar_translate[i];
		wait(5);
	}

	for(uint8_t j = 0; j< 3;j++) {
		for(uint8_t i = 0; i< 6; i++) {
	    	digitbuffer[i] = 0xff;		
    	}
		wait(20);
		for(uint8_t i = 0; i< 6; i++) {
			digitbuffer[i] = 0x00;
    	}
		wait(20);
	}

	for(uint8_t i = 0; i< 3;i++) {
		leddigitbuffer[i] = 0x00;
	}

}

int main(void) {
	ports_init();
	timer_init();
    uart_init(UART_BAUD_SELECT(19200,F_CPU));
    memset(data_in, 0, BUFSIZE);


	//demo_display();

	while(1) {

	    work_uart();

        if(syscounter >= 100) {
            uart_putc('a'); // send a to receive values

            print_sevenseg(0, power_gen);
            print_sevenseg(1, power_load);

            print_bar(LEDS_GEN, current_in, CURRENT_MAX);
            print_bar(LEDS_LOAD, current_out, CURRENT_MAX);

			print_bar_bottom(current_in, current_out, 10000);

            syscounter = 0;
        }
	}
	
	return(0);
}

// system timer
SIGNAL(TIMER1_COMPA_vect) {
	syscounter++;

	// output to sevensegment and leds
	// make this here to reduce display flicker
    digit++;
    if(digit >5) digit = 0;
    leddigit++;
    if(leddigit >3) leddigit = 0;

    SEVENSEG_PORT = digitbuffer[digit];
    SEVENSEGDIG_PORT = _BV(digit+DIG0);

    LED_PORT = leddigitbuffer[leddigit];
    LEDDIG_PORT = _BV(leddigit);
}


