#include <avr/io.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>
#include <string.h>
#include "pinout.h"
#include "uart.h"
#include "i2cmaster.h"
#include "DS3231.h"
#include "menu.h"
#include "display.h"

#define RX_INPUT_BUFFER_SIZE 92

void check_dip_switches(void);
void setup(void);
void set_PWM_duty(int dutycycle);

int8_t parse_nmea_gps(char input_string[RX_INPUT_BUFFER_SIZE], uint8_t output_buffer[3]){
    char* token = strsep(&input_string, ",");
    if(!strcmp("$GPRMC", token)){
        char* time = strsep(&input_string, ",");
        if(!strlen(time)){
            // no valid time available
            return -2;
        }

        // craft bcd values
        output_buffer[2]  = (time[0] - 48) << 4;
        output_buffer[2] |= time[1] - 48;

        output_buffer[1]  = (time[2] - 48) << 4;
        output_buffer[1] |= time[3] - 48;

        output_buffer[0]  = (time[4] - 48) << 4;
        output_buffer[0] |= time[5] - 48;

        return 0;
    }
    return -1;
}

int main(void){
    setup();
    printf("Moin\n\r");

    while(1){
        check_dip_switches();

        if(get_menu_active()){
            PORTC &= ~DOT;
            string_to_buffer(get_menu_text());
            _delay_ms(10);
        }
        else{
            uint8_t clock[3];
            cli();
            DS3231_read(clock);
            sei();

            // convert from bcd and back
            int8_t hour = (10 * (clock[2] & 0xF0)>>4) + (clock[2] & 0x0F);
            hour += menu_val_timezone;
            hour %= 24;
            if(hour < 0)
                hour = 24 + hour;
            clock[2] = ((hour/10)<<4)|(hour % 10);

            clock_to_buffer(clock);

            PORTC = (PORTC & (~DOT)) | (clock[0]%2) << 4; // blink dots
            _delay_ms(100);
        }
        print_SR_Buffer();
    }
}

void check_dip_switches() {
    if (PINB & DIP_1) {
        PORTC |=  ENABLE_TUBE_PSU;
        PORTA &= ~ENABLE_TUBE_SUPPLY;
        //  PORTB |=  STATUS_LED_B;
    } else {
        PORTC &= ~(ENABLE_TUBE_PSU);
        PORTA |=  (ENABLE_TUBE_SUPPLY);
        //   PORTB &= ~STATUS_LED_B;
    }
}

void setup(){
    menu_val_timezone = eeprom_read_byte((uint8_t*)&menu_val_timezone_ee);
    menu_val_format = eeprom_read_byte(&menu_val_format_ee);
    if(menu_val_format != 24 && menu_val_format != 12){
        menu_val_format = 24;
        eeprom_write_byte(&menu_val_format_ee, menu_val_format);
    }

    DDRA =   ENABLE_TUBE_SUPPLY;

    DDRB =   STATUS_LED_B
        | STATUS_LED_C
        | MOSI
        | CLK;

    DDRC =   LATCH_SR
        | ENABLE_SR
        | DOT
        | ENABLE_TUBE_PSU;

    DDRD =   RESET_SR 
        | TUBE_LED;

    //--------------------------
    //  		 SPI
    //--------------------------
    SPCR = (1 << SPE) |   //enable SPI
        (1 << MSTR) |  //SPI als Master starten
        (1 << SPR0) |  //clock-prescaler LSB
        (1 << SPR1) |  //clock-prescaler MSB
        (0 << SPIE) |  //complete transfer/ recive interrupt
        (0 << CPOL) |
        (0 << CPHA) |
        (0 << DORD);   //MSB first = 0
    SPSR = (1 << SPI2X);// | //clock- prescaler double speed

    PORTC &= ~LATCH_SR; //latch low, sonst startet SPI nicht
    PORTC |= LATCH_SR;
    PORTD |= RESET_SR;   //Setzte Reset auf HIGH (active LOW)
    PORTC &= ~(ENABLE_SR); //Aktiviert Shiftregister (active LOW)

    //--------------------------
    //    Externe Interrupts
    //--------------------------
    //External Interrupt Mask Register

    EIMSK |= (1 << INT0)  |        //Aktiviere Interrupt INT0
        (1 << INT1);          //Aktiviere Interrupt INT1

    //External Interrupt Control Register A
    EICRA |= (0 << ISC10) |        //Interrupt bei jedem Pegelwechsel auf INT1
        (1 << ISC11) |
        (1 << ISC00) |        //Interrupt bei jedem Pegelwechsel auf INT0
        (1 << ISC01);

    //--------------------------
    //  Timer + Zeitinterrupts
    //--------------------------

    //PWM-Timer
    //OC2A  set when upcounting, clear when down counting
    TCCR2A = (1<<COM2A1) 
        |(0<<COM2A0);

    TCCR2A |= (1<<WGM20)   //Wave form generartion mode
        |(0<<WGM21);   // Wave form generation mode

    TCCR2B = (1 << CS22)
        |(0 << CS21)
        |(0 << CS20);
    OCR2A   = 0; // Compare value 125

    uart_init();
    i2c_init();

    sei(); //Enable Interrupts
    clear_SR_Buffer();

}

//Encoder rotate
ISR(INT0_vect) {
    if (PIND & ENCODER_B) {
        if(get_menu_active())
            menu_up_down(1);
        else
            OCR2A++;
    } else {
        if(get_menu_active())
            menu_up_down(-1);
        else
            OCR2A--;
    }
}

//Encoder Button
ISR(INT1_vect) {
    printf("button\n\r");
    enter_menu();
}

ISR(USART0_RX_vect)
{
    PORTB ^= STATUS_LED_C;
    static char async_input_buffer[RX_INPUT_BUFFER_SIZE];
    static uint8_t async_buffer_pointer = 0;

    uint8_t data = UART_DATA_REG;
    if(!(data == '\n' || data == '\r')){
        async_input_buffer[async_buffer_pointer]=data;
        async_buffer_pointer++;
    }
    else if(async_buffer_pointer > 0){
        async_input_buffer[async_buffer_pointer] = 0; // Null terminate string
        async_buffer_pointer=0;
        uint8_t gps_time[3];
        int8_t err = parse_nmea_gps(async_input_buffer, gps_time);
        if(!err){
            PORTB |= STATUS_LED_C;
            //if(rtc_time[0] != gps_time[0] || rtc_time[1] != gps_time[1] || rtc_time[2] != gps_time[2])
            DS3231_write(gps_time);
        }
        else if(err == -2)
            PORTB &= ~STATUS_LED_C;
    }
}

