#include <msp430.h>

/*
 * main.c
 */
volatile unsigned long doublemillis = 0;
volatile unsigned int j = 0;
const unsigned int brainFq[8][63] = {
{1,15,14,13,12,11,10,9,8,7,6,5,4,7,6,5,4,6,5,4,5,6,7,8,9,10,11,12,13,14,15,},
{1,14,13,15,12,11,14,11,10,9,11,9,8,7,10,7,6,5,7,6,8,5,8,7,9,11,10,12,},
{1,15,14,13,12,14,11,10,9,8,11,10,9,8,9,8,9,10,8,10,9,8,9,10,11,12,13,10,15,10,11,10,11,12},
{1,12,14,11,13,12,10,9,8,7,6,5,4,8,7,6,5,4,7,6,5,4,7,6,5,4,5,6,7,8,9,12,10,11,12,15,12,15,12,},
{1,12,14,11,13,12,10,9,8,7,6,5,4,8,7,6,5,4,7,6,5,4,7,6,5,4,5,6,7,8,9,12,10,11,12,15,12,15,12,},
{1,12,14,11,13,12,10,9,8,7,6,5,4,8,7,6,5,4,7,6,5,4,7,6,5,4,5,6,7,8,9,12,10,11,12,15,12,15,12,},
{1,12,14,11,13,12,10,9,8,7,6,5,4,8,7,6,5,4,7,6,5,4,7,6,5,4,5,6,7,8,9,12,10,11,12,15,12,15,12,},
{1,10,1,1,1,1,1,1,1,1,1,1,1,1,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,}
};
const unsigned int brainDuration[8][63] = {
{0,20,20,20,20,20,20,20,30,30,30,30,40,5,10,20,40,5,10,30,5,5,5,5,5,5,5,5,5,5,25,},
{1,15,15,5,15,15,5,15,15,15,5,15,15,15,3,15,10,10,5,10,5,10,5,10,10,5,15,15,},
{2,15,15,15,15,10,20,20,20,20,5,5,10,10,10,10,10,10,10,5,10,10,10,20,20,20,20,10,15,10,10,10,10,30,},
{3,5,15,10,5,10,8,10,10,12,5,4,15,5,5,10,10,10,5,5,5,10,15,5,5,5,5,5,5,10,10,10,5,5,15,5,10,5,25,},
{4,5,15,10,5,10,8,10,10,12,5,4,15,5,5,10,10,10,5,5,5,10,15,5,5,5,5,5,5,10,10,10,5,5,15,5,10,5,25,},
{5,5,15,10,5,10,8,10,10,12,5,4,15,5,5,10,10,10,5,5,5,10,15,5,5,5,5,5,5,10,10,10,5,5,15,5,10,5,25,},
{6,5,15,10,5,10,8,10,10,12,5,4,15,5,5,10,10,10,5,5,5,10,15,5,5,5,5,5,5,10,10,10,5,5,15,5,10,5,25,},
{7,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,},
};
const unsigned int ArrayLength[8] = {31,28,34,39,39,39,39,39,};
volatile unsigned int on = 0;
// Declaration of functions
unsigned int FreqTime(unsigned int Hz);
unsigned int FreqCycle(unsigned int Hz , unsigned long speed);
unsigned long millis();
unsigned long blinkms(unsigned long Millis);
unsigned int CountByTime(unsigned long duration, unsigned int ArrayLength);
// MAIN program
int main(void) {
	// Set Watchdog as timer with an interrupt every 0.5ms
	BCSCTL1 = CALBC1_1MHZ;
	DCOCTL = CALDCO_1MHZ;
	WDTCTL = WDT_MDLY_0_5; // (WDTPW+WDTTMSEL+WDTCNTCL+WDTIS1) /* 0.5ms
	IFG1 &= ~WDTIFG;
	IE1 |= WDTIE; // Enable WDT interrupt
	P1SEL = 0x00; // Set all Pins to GPIO Pins, no clock sources, nothing
	P1SEL2 = 0x00; // Set all Pins to GPIO Pins, no clock sources, nothing
	P2SEL = 0x00;
	P2SEL2 = 0x00;
	P3SEL = 0x00;
	P3SEL2 = 0x00;
    P1DIR = 0xCF; // Set P1.0 - P1.3 as output P1.4 - P1.5 as input and P1.6 - P1.7 as output
	P1REN = 0x30; // Set P1.4 - P1.5 pull down resistor
 	P1OUT = 0x00; // Set P1.0 - P1.7 to 0
	P2DIR = 0xF8; // Set P2.0 - P2.2 as Intput and P2.3 - P2.7 as Output
	P2REN = 0x07; // Set P2.0 - P2.2 pull down resistor
	P2OUT = 0x00; // Set P2.0 - P2.7 to 0
    P3DIR = 0xFF;
    P3OUT = 0x00;
 	// Timer_A
	TA0CCR0 = 1250; // Count limit for 400Hz at !MHz (1000000 / 400 / 2)
	TA0CCTL0 = 0x10;  // Enable Timer A0 interrupts, bit 4=1
	TA0CTL = TASSEL_2 + MC_1; // Timer A0 with SMCLK, count UP
	TA1CCR0 = 1250; // Count limit for 400Hz predefinitoin, will be overwriten later
	TA1CCTL0 = 0x10;  // Enable Timer A0 interrupts, bit 4=1
	TA1CTL = TASSEL_2 + MC_1; // Timer A1 with SMCLK, count UP
	_EINT(); //enable interrupts
// convert input of PIN 2.0 - 2.2 to programnumber. Onöy once at startup. So no switching during program
    volatile int ProgNo = P2IN & 0x07;
// Endless loop
 	while(1){
		j = CountByTime(brainDuration[ProgNo][j], ArrayLength[ProgNo]);
 		if (j < ArrayLength[ProgNo]){
			on = blinkms(FreqTime(brainFq[ProgNo][j]));
			TA1CCR0 = FreqCycle(brainFq[ProgNo][j]+400, 1000000);
			int brightness = ((P1IN & 0x30) >> 4)+5;  // brightness encoded on switches on PIN 1.4 and 1.5
			if (on){
            for (int i = 0; i < 10; i++){
					// poor mans PWM
					if (i<brightness){
						P1OUT |= 0x03;
					}

					else{
						P1OUT &= 0xFC;
					}
				}
			}
			else{
				P1OUT &= 0xFC;
			}
		}
		else{
            P1DIR = 0xFF; // Set P1.0 - P1.7 as output
            P2DIR = 0xFF; // Set P2.0 - P2.7 as output
 			P1OUT = 0x00;
 			P2OUT = 0x00;
            __dint();
			_BIS_SR(LPM4_bits);
		}
	}
 	return 0;
}

// Functions

// blink in the rhythm of given ms
unsigned long blinkms(unsigned long Millis){
	static unsigned long PreviousMillis = 0;
	static unsigned int back = 0;
	unsigned long CurrentMillis = millis();
	if (CurrentMillis - PreviousMillis >= Millis){
		PreviousMillis = CurrentMillis;
		back ^= 0x01;
	}
	return back;
}
// count 1 up, if time in ms done
unsigned int CountByTime(unsigned long duration, unsigned int ArrayLength){
	static unsigned long PreviousMillis = 0;
	static unsigned int back = 0;
	unsigned long Millis = duration * 1000;
	unsigned long CurrentMillis = millis();
	if (CurrentMillis - PreviousMillis >= Millis){
		PreviousMillis = CurrentMillis;
		if (back <= ArrayLength) back++;
	}
	return back;
}

// Convert frequency to ms
unsigned int FreqTime(unsigned int Hz){
	unsigned int v = 1000 / Hz / 2;
	return v;
}
// Convert frequency to Cycles by given Clockspeed
unsigned int FreqCycle(unsigned int Hz, unsigned long speed){
	unsigned int v = speed / Hz / 2;
	return v;
}

// Function that is reuturning ms since startup
unsigned long millis(){
	return doublemillis >> 1; // Double Millisecondy divided by 2
}

// -------------------------- Interrupts ------------------------------------------
#pragma vector=WDT_VECTOR
__interrupt void WDT(void){
		doublemillis++;
}

#pragma vector=TIMER0_A0_VECTOR    // Timer0 A0 interrupt service routine
__interrupt void Timer0_A0 (void) {
   P1OUT ^= 0x04;                  // Toggle output P1.2
}

#pragma vector=TIMER0_A1_VECTOR    // Timer1 A0 interrupt service routine
__interrupt void Timer1_A0 (void) {
   P1OUT ^= 0x08;                  // Toggle output P1.3
}