/**
 * AVR program to provide lighting modes for an RGB LED
 * Copyright (c) 2011 Akoi Meexx (http://akoimeexx.com/)
 * 
 * Chip type           : ATtiny13
 * Clock frequency     : Internal clock 1 Mhz (factory default)
 * 
 * 
 * Project operational goal:
 * Power on->Jump to rainbow cycling of colors.
 * Button press should jump to solid color of current rgb values.
 * Button press again should jump to pulsing said color.
 * Button press again should go back to rainbow cycling.
 */

/**
 * AVR-specific defines and includes
 */
#include <avr/io.h>
#define F_CPU 1000000UL  // 1 MHz
// Some macros that make the code more readable
#define output_low(port,pin) port &= ~(1<<pin)
#define output_high(port,pin) port |= (1<<pin)
#define set_input(portdir,pin) portdir &= ~(1<<pin)
#define set_output(portdir,pin) portdir |= (1<<pin)


/**
 * Define and create a boolean structure.
 */
#define true 1
#define false 0
#define True 1
#define False 0
#define TRUE 1
#define FALSE 0
typedef int boolean;

/**
 * Alias our color channel pinouts
 */
#define RED PB0
#define GREEN PB1
#define BLUE PB2

/**
 * Create our rgb structure and declare it below as a global
 */
typedef struct {
	int red;
	int green;
	int blue;
} rgb;
rgb channels = { 0x00, 0x00, 0x00 };

/**
 * Define our lighting modes and declare our mode global
 */
#define DEBUG_MODE 0
#define RAINBOW_MODE 1
#define SOLID_MODE 2
#define PULSE_MODE 3
int lighting_mode;

/**
 * TODO: Optimize. This method just feels too 'clunky'.
 */
boolean rainbow_step(void) {
	//Fade from blue to red
	if(channels.blue > 0x00 && channels.red == 0xFF && channels.green == 0x00) {
		channels.blue--;
	}
	if(channels.blue == 0xFF && channels.red < 0xFF && channels.green == 0x00) {
		channels.red++;
	}
	
	//Fade from green to blue
	if(channels.green > 0x00 && channels.blue == 0xFF && channels.red == 0x00) {
		channels.green--;
	}
	if(channels.green == 0xFF && channels.blue < 0xFF && channels.red == 0x00) {
		channels.blue++;
	}
	
	// Fade from red to green
	if(channels.red > 0x00 && channels.green == 0xFF && channels.blue == 0x00) {
		channels.red--;
	}
	if(channels.red == 0xFF && channels.green < 0xFF && channels.blue == 0x00) {
		channels.green++;
	}
	return true;
}

/**
 * TODO: Build function
 */
boolean solid_step(void) {
	return True;
}

/**
 * TODO: Build function
 */
boolean pulse_step(void) {
	return True;
}

/**
 * Software Pulse Width Modulation for RGB - Pulled from Adafruit site.
 */
void do_pwm(int r_duty, int g_duty, int b_duty, int rate) {
	int i;

	while (rate != 0) {
		output_high(PORTB, RED);
		output_high(PORTB, GREEN);
		output_high(PORTB, BLUE);

		for (i=0; i < 255; i++) {
			if (i == r_duty)
				output_low(PORTB, RED);
			if (i == g_duty)
				output_low(PORTB, GREEN);
			if (i == b_duty)
				output_low(PORTB, BLUE);
		}
		rate--;
	}
}

void init(void) {
	channels.red = 0xFF;
	lighting_mode = RAINBOW_MODE;
	
	set_output(DDRB, RED);  
	set_output(DDRB, GREEN);
	set_output(DDRB, BLUE);
}

int main(void) {
	init();
	boolean mode_loop = True;
	while (mode_loop) {
		switch (lighting_mode) {
			case DEBUG_MODE:
				mode_loop = False;
				break;
			case RAINBOW_MODE:
				mode_loop = rainbow_step();
				break;
			case SOLID_MODE:
				mode_loop = solid_step();
				break;
			case PULSE_MODE:
				mode_loop = pulse_step();
				break;
			default:
				mode_loop = False;
		}
		do_pwm(channels.red, channels.green, channels.blue, 15);
	}
	return 0;
}

