#include <avr/io.h>

#include <avr/io.h>
#include <stdlib.h> //for itoa
#include <stdlib.h> //for abs()
#define F_CPU 16000000UL
#include <util/delay.h>
#define BAUDRATE 9600
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)

#define Down_button 0
#define Left_button 770
#define Right_button 510
#define Center_button 680
#define Up_button 820
#define button_margin 20

#define rled_pwm_threshold OCR1A 
#define gled_pwm_threshold OCR1B
#define bled_pwm_threshold OCR2A 
char buffer[16]; //Output of the itoa function

uint16_t led_selected=0; 
/************************************************************************/
/* 
0 -> no led is selected
1 -> r selected 
2 -> g selected 
3 -> b selected                                                                     
*/
/************************************************************************/

void adc_init(void); //Function to initialize/configure the ADC
uint16_t read_adc(uint8_t channel); //Function to read an arbitrary analog channel/pin
void USART_init(void); //Function to initialize and configure the USART/serial
void USART_send( unsigned char data); //Function that sends a char over the serial port
void USART_putstring(char* StringPtr); //Function that sends a string over the serial port
void send_data_to_pc(uint32_t dump);

long map(long x, long in_min, long in_max, long out_min, long out_max)
{
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

uint16_t naviButton (uint16_t raw ){
    if(raw < 1020){  //one of navigation button is pressed

        if(abs(raw-Up_button)<button_margin){ // up button is pressed
            return 1;
        }else if(abs(raw-Left_button)<button_margin){
            return 2;
        }else if(abs(raw-Down_button)<button_margin){
            return 3;
        }else if(abs(raw-Right_button)<button_margin){
            return 4;
        }else if(abs(raw-Center_button)<button_margin){
            return 5;
        }

    }
        return 0;
}

void selectNextLed(uint8_t buttonSelected){
    if(buttonSelected==2){ //left circular shift  0 1 2 3
        if(led_selected==0){
            led_selected=3;
        }else if(led_selected==1){
            led_selected=3;
        }else if(led_selected==2){
            led_selected=1;
        }else if(led_selected==3){
            led_selected=2;
        }
    }else if(buttonSelected==4){//right circular shift  0 1 2 3

            if(led_selected==0){
            led_selected=1;
            }else if(led_selected==1){
            led_selected=2;
            }else if(led_selected==2){
            led_selected=3;
            }else if(led_selected==3){
            led_selected=1;
        }
    }
}

int main(void){
adc_init(); //Setup the ADC
USART_init(); //Setup the USART

//PWM1&2 initialisation
TCCR1A = 0b10100001;// set PWM mode for PB1 and PB2
TCCR1B = 0b00000010; //set clock:8 prescaler

//PWM3 initialisation
TCCR2A = 0b10000001; // set PWM mode for PB3
TCCR2B = 0b00000010; //set clock:8 prescaler
DDRB = 0xFF; //set portB as output

while(1){
    volatile uint16_t navi_adc= read_adc(1);
    /*
    * 
    Idle -> 0
    up button -> 1 
    Down button -> 3
    Left button -> 2
    Right button -> 4
    center button -> 5
    */
    uint8_t button_pressed =naviButton(navi_adc); 
    _delay_ms(200);
    if(button_pressed!=0){//either left or right button is pressed

        if((button_pressed == 2) | (button_pressed == 4) ){
            selectNextLed(button_pressed);
            }
        if(button_pressed == 5){ //center button is pressed
            led_selected=0;        //reset selected led
        }

    }

    if(led_selected!=0){ //atleast a led is selected
        uint16_t pot_value = map(read_adc(0),100,1023,0,255); // adc give 10bit converting it to 8 bit
        //100 is selected because of pot offset
        send_data_to_pc(pot_value);
        if(led_selected ==1){
            rled_pwm_threshold = pot_value;
        }else if(led_selected ==2){
            gled_pwm_threshold = pot_value;            
        }else if(led_selected ==3){
            bled_pwm_threshold = pot_value;
        }
        _delay_ms(50);

    }
}

return 0;
}

void send_data_to_pc(uint32_t dump){

itoa(dump, buffer, 10); //Convert the read value to an ascii string
/*char * itoa ( int value, char * str, int base );*/
USART_putstring(buffer); //Send the converted value to the terminal
USART_putstring(" "); //Some more formatting

USART_send('\r');
USART_send('\n');
}

void adc_init(void){
ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); //16Mhz/128 = 125Khz the ADC reference clock
ADMUX |= (1<<REFS0); //Voltage reference from Avcc (5v)
ADCSRA |= (1<<ADEN); //Turn on ADC
ADCSRA |= (1<<ADSC); //Do an initial conversion because this one is the slowest
//and to ensure that everything is up and running
}

uint16_t read_adc(uint8_t channel){
ADMUX &= 0xF0; //Clear the older channel that was read
ADMUX |= channel; //Defines the new ADC channel to be read
ADCSRA |= (1<<ADSC); //Starts a new conversion
while(ADCSRA & (1<<ADSC)); //Wait until the conversion is done
return ADCW; //Returns the ADC value of the chosen channel
}

void USART_init(void){
UBRR0H = (uint8_t)(BAUD_PRESCALLER>>8);
UBRR0L = (uint8_t)(BAUD_PRESCALLER);
UCSR0B = (1<<RXEN0)|(1<<TXEN0);
UCSR0C = (3<<UCSZ00);
}

void USART_send( unsigned char data){

while(!(UCSR0A & (1<<UDRE0)));
UDR0 = data;
}

void USART_putstring(char* StringPtr){

while(*StringPtr != 0x00){
USART_send(*StringPtr);
StringPtr++;}

}