/********************************
*     AK Rotating LED POV       *
*********************************
* Author: Aki Korhonen          *
* First version: 20-07-2008     *
* Current:       29-07-2008     *
*********************************/

// ------------------------------------------------ //

// ------------------------------------------
// Configuration and Security bits for ATmega
// ------------------------------------------
// WDTON		= 1
// SPIEN		= 0 [X]
// CKOPT		= 0 [X]
// EESAVE		= 1
// BOOTSZ1	= 0 [X]
// BOOTSZ0	= 0 [X]
// BOOTRST	= 1
// BODLEVEL	= 1
// BODEN		= 1
// SUT1		= 1
// SUT0		= 1
// CKSEL3		= 1
// CKSEL2		= 0 [X]
// CKSEL1		= 1
// CKSEL0		= 1
// ------------------------------------------

// ------------------------------------------------ //

#define XTAL 14000000UL	// 14 MHz crystal
#define F_CPU XTAL // F_CPU is needed by delay.h etc
#define BAUD 9600 // UART speed
#define BUFFER_SIZE 12 // UART buffer size

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <inttypes.h>
#include <compat/deprecated.h> // for enable_external_int()
#include <avr/pgmspace.h>

// 74HC595 data lines
#define SR_595_PORT	PORTB
#define SR_595_SER	0 // Serial data
#define SR_595_RCK	1 // Latch
#define SR_595_SCK	2 // Serial clock
#define SR_595_CLR	3 // Clear

// Misc. lines
#define MISC_PORT	PORTD
#define IR_TRIGGER	2
#define PAD34		3
#define OK_IR_LED	5
#define PAD38		6
#define PAD36		7

// UART buffer, fill with 0x00
uint8_t cmd_buffer[BUFFER_SIZE] = {0};

// Misc
unsigned int i = 0;
unsigned int o = 0;
unsigned int p = 0;
unsigned int second_counter = 0;
int counter = 0;

// Display buffer
#define DISP_BUF_SIZE 128
uint8_t display_data_red[DISP_BUF_SIZE] = {0};
uint8_t display_data_green[DISP_BUF_SIZE] = {0};

//#define DISP_BUF_SIZE 114
//uint8_t display_data_red[] = {20,0,36,0,56,0,0,0,63,0,8,0,52,0,0,0,61,0,0,0,63,0,8,0,52,0,0,0,24,0,36,0,36,0,24,0,0,0,60,0,4,0,0,0,63,0,4,0,56,0,0,0,24,0,36,0,36,0,24,0,0,0,60,0,4,0,56,0,0,0,24,0,36,0,40,0,0,0,60,0,4,0,56,0,0,0,32,0,0,0,24,0,36,0,36,0,24,0,0,0,60,0,4,0,0,0,24,0,164,0,164,0,124,0};
//uint8_t display_data_green[] = {0,20,0,36,0,56,0,0,0,63,0,8,0,52,0,0,0,61,0,0,0,63,0,8,0,52,0,0,0,24,0,36,0,36,0,24,0,0,0,60,0,4,0,0,0,63,0,4,0,56,0,0,0,24,0,36,0,36,0,24,0,0,0,60,0,4,0,56,0,0,0,24,0,36,0,40,0,0,0,60,0,4,0,56,0,0,0,32,0,0,0,24,0,36,0,36,0,24,0,0,0,60,0,4,0,0,0,24,0,164,0,164,0,124};

// Space Invader animation
#define SPACE_INVADER_WIDTH 11
uint8_t space_invader_frame_1[] = {0x70, 0x18, 0x7D, 0xB6, 0xBC, 0x3C, 0xBC, 0xB6, 0x7D, 0x18, 0x70};
uint8_t space_invader_frame_2[] = {0x1E, 0xB8, 0x7D, 0x36, 0x3C, 0x3C, 0x3C, 0x36, 0x7D, 0xB8, 0x1E};
uint8_t space_invader_x = 0;
uint8_t space_invader_y = 0;
uint8_t space_invader_frame = 1;
uint8_t space_invader_counter = 0;
uint8_t space_invader_down = 1;
uint8_t space_invader_right = 1;

// HELLO
//#define DISP_BUF_SIZE 76
//uint8_t display_data_red[DISP_BUF_SIZE] = {0,0,254,127,2,64,2,64,242,95,130,64,130,64,2,95,2,64,2,64,2,79,130,84,130,84,2,87,2,64,2,64,2,64,2,64,18,80,242,95,2,80,2,64,2,64,2,64,18,80,242,95,2,80,2,64,2,64,2,64,2,79,130,80,130,80,2,79,2,64,2,64,254,127,0,0};
//uint8_t display_data_green[DISP_BUF_SIZE] = {255,255,255,255,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,3,192,255,255,255,255};

// Game of Life
#define GOL_WORLD_Y	16
#define GOL_WORLD_X	16
//uint8_t gol_area_now[GOL_WORLD_X][GOL_WORLD_Y];
//uint8_t gol_area_before[GOL_WORLD_X][GOL_WORLD_Y];
//uint8_t gol_initialized = 0;

// ------------------------------------------------ //

void SR_595_delay(void)
{
	uint8_t x = 0;
	
	while(x != 1)
	{
		x++;
	}
}

void SR_595_reset(void)
{
	SR_595_PORT &= ~(_BV(SR_595_CLR));
	SR_595_delay();
	SR_595_PORT |= _BV(SR_595_CLR);
}

void SR_595_clock(void)
{
	SR_595_PORT |= _BV(SR_595_SCK);
	//SR_595_delay();
	SR_595_PORT &= ~(_BV(SR_595_SCK));
}

void SR_595_send_bit(int bit)
{
	if(bit == 1)
	{
		SR_595_PORT |= _BV(SR_595_SER);
	}
	else
	{
		SR_595_PORT &= ~(_BV(SR_595_SER));
	}
	
	//SR_595_delay();
	
	SR_595_clock();
}

void SR_595_latch(void)
{
	SR_595_PORT |= _BV(SR_595_RCK);
	//SR_595_delay();
	SR_595_PORT &= ~(_BV(SR_595_RCK));
}

// ------------------------------------------------ //

void drawScreen(void)
{
	uint8_t i = 0;
	uint8_t o = 0;
	
	uint8_t value = 0;
	
	for(o=0 ; o<DISP_BUF_SIZE ; o+=2)
	{
		for(i=0 ; i<8 ; i++)
		{
			value = ((display_data_red[o] >> i) & 0x01);
			SR_595_send_bit(value);
		}
		for(i=0 ; i<8 ; i++)
		{
			value = ((display_data_red[o+1] >> i) & 0x01);
			SR_595_send_bit(value);
		}
		for(i=0 ; i<8 ; i++)
		{
			value = ((display_data_green[o] >> i) & 0x01);
			SR_595_send_bit(value);
		}
		for(i=0 ; i<8 ; i++)
		{
			value = ((display_data_green[o+1] >> i) & 0x01);
			SR_595_send_bit(value);
		}
		
		SR_595_latch();
		
		_delay_ms(0.3);
	}
	
	// Clear the display
	for(i=0 ; i<32 ; i++)
	{
		SR_595_send_bit(0);
	}
	
	SR_595_latch();
}

// ------------------------------------------------ //

/*
void GameOfLife_updateDisplay(void)
{
	uint8_t y;
	uint8_t x;
	uint8_t disp_buf = 0;
	
	for(x=0 ; x<GOL_WORLD_X ; x++)
	{
		for(y=0 ; y<8 ; y++)
		{
			if(gol_area_now[x][y] == 1) display_data_red[disp_buf] |= (1<<y);
			else display_data_red[disp_buf] &=~ (1<<y);
		}
		
		for(y=8 ; y<16 ; y++)
		{
			if(gol_area_now[x][y] == 1) display_data_red[x] |= (1<<(y-8));
			//if(gol_area_now[x][y] == 1) display_data_red[disp_buf+1] = (display_data_red[disp_buf+1]<<1) | 1;
			else display_data_red[disp_buf+1] &=~ (1<<(y-8));
		}
		
		disp_buf += 2;
	}
}

void GameOfLife_addGlider(void)
{
	uint8_t y;
	uint8_t x;
	
	//x = rand(2, (GOL_WORLD_X-3));
	//y = rand(2, (GOL_WORLD_Y-3));
	
	x = 4;
	y = 10;
	
	gol_area_now[x][y-1] = 1;
	gol_area_now[x+1][y] = 1;
	gol_area_now[x-1][y+1] = 1;
	gol_area_now[x][y+1] = 1;
	gol_area_now[x+1][y+1] = 1;
	
	GameOfLife_updateDisplay();
}

void GameOfLife(void)
{
	uint8_t friends = 0;
	uint8_t y;
	uint8_t x;
	uint8_t friend_count[8][2];
	uint8_t alive = 0;
	
	for(y=0 ; y<GOL_WORLD_Y ; y++)
	{
		for(x=0 ; x<GOL_WORLD_X ; x++)
		{
			gol_area_before[x][y] = gol_area_now[x][y];
		}
	}
	
	for(y=0 ; y<GOL_WORLD_Y ; y++)
	{
		for(x=0 ; x<GOL_WORLD_X ; x++)
		{
			alive = gol_area_before[x][y];
			friends = 0;
			
			friend_count[0][0] = x-1; friend_count[0][1] = y-1;
			friend_count[1][0] = x;   friend_count[1][1] = y-1;
			friend_count[2][0] = x+1; friend_count[2][1] = y-1;
			
			friend_count[3][0] = x-1; friend_count[3][1] = y;
			friend_count[4][0] = x+1; friend_count[4][1] = y;
			
			friend_count[5][0] = x-1; friend_count[5][1] = y+1;
			friend_count[6][0] = x;   friend_count[6][1] = y+1;
			friend_count[7][0] = x+1; friend_count[7][1] = y+1;
			
			for(o=0 ; o<8 ; o++)
			{
				if(friend_count[o][0]<0) friend_count[o][0] = (GOL_WORLD_X-1);
				else if((GOL_WORLD_X-1) < friend_count[o][0]) friend_count[o][0] = 0;
				
				if(friend_count[o][1]<0) friend_count[o][1] = (GOL_WORLD_Y-1);
				else if((GOL_WORLD_Y-1) < friend_count[o][1]) friend_count[o][1] = 0;
			}
			
			for(o=0 ; o<8 ; o++)
			{
				if(gol_area_before[friend_count[o][0]][friend_count[o][1]] == 1) friends++;
			}
			
			if(alive == 1)
			{
				if((friends < 2) || (3 < friends)) alive = 0;
				else alive = 1;
			}
			else
			{
				if(friends == 3) alive = 1;
				else alive = 0;
			}
			
			gol_area_now[x][y] = alive;
		}
	}
	
	GameOfLife_updateDisplay();
}

void GameOfLife_init(void)
{
	uint8_t y;
	uint8_t x;
	uint8_t i;
	
	for(x=0 ; x<GOL_WORLD_X ; x++)
	{
		for(y=0 ; y<GOL_WORLD_Y ; y++)
		{
			gol_area_now[x][y] = 0;
			gol_area_before[x][y] = 0;
		}
	}
	
	for(i=0 ; i<DISP_BUF_SIZE ; i++)
	{
		display_data_red[i] = 0x00;
		display_data_green[i] = 0x00;
	}
	
	gol_initialized = 1;
}
*/

// ------------------------------------------------ //

void SpaceInvader_animation(void)
{
	for(i=0 ; i<DISP_BUF_SIZE ; i++)
	{
		display_data_red[i] = 0x00;
		display_data_green[i] = 0x00;
	}
	
	uint8_t i;
	uint8_t o;
	uint8_t frame_part;
	uint8_t frame_part_bit;
	
	for(i=0 ; i<SPACE_INVADER_WIDTH ; i++)
	{
		// Red flyer
		if(space_invader_frame == 1) frame_part = space_invader_frame_1[i];
		else frame_part = space_invader_frame_2[i];
		
		frame_part_bit = 0;
		
		for(o=space_invader_y ; o<8 ; o++)
		{
			if( ((frame_part >> frame_part_bit) & 0x01) == 1) display_data_red[(space_invader_x+i)*2] |= (1 << o);
			frame_part_bit++;
		}
		
		for(o=0 ; o<space_invader_y ; o++)
		{
			if( ((frame_part >> frame_part_bit) & 0x01) == 1) display_data_red[(space_invader_x+i)*2+1] |= (1 << o);
			frame_part_bit++;
		}
		
		// Green flyer
		if(space_invader_frame == 1) frame_part = space_invader_frame_2[i];
		else frame_part = space_invader_frame_1[i];
		
		frame_part_bit = 0;
		
		for(o=(8-space_invader_y) ; o<8 ; o++)
		{
			if( ((frame_part >> frame_part_bit) & 0x01) == 1) display_data_green[((DISP_BUF_SIZE/2)-space_invader_x-i)*2] |= (1 << o);
			frame_part_bit++;
		}
		
		for(o=0 ; o<(8-space_invader_y) ; o++)
		{
			if( ((frame_part >> frame_part_bit) & 0x01) == 1) display_data_green[((DISP_BUF_SIZE/2)-space_invader_x-i)*2+1] |= (1 << o);
			frame_part_bit++;
		}
	}
	
	/*
	space_invader_x++;
	if(space_invader_x == (DISP_BUF_SIZE/2)-SPACE_INVADER_WIDTH)
	{
		space_invader_x = 0;
	}
	*/
	
	if(space_invader_right == 1)
	{
		space_invader_x++;
		
		if(space_invader_x == (DISP_BUF_SIZE/2)-SPACE_INVADER_WIDTH)
		{
			space_invader_right = 0;
		}
	}
	else
	{
		space_invader_x--;
		
		if(space_invader_x == 0)
		{
			space_invader_right = 1;
		}
	}
	
	if(space_invader_down == 1)
	{
		space_invader_y++;
		
		if(space_invader_y == 8)
		{
			space_invader_down = 0;
		}
	}
	else
	{
		space_invader_y--;
		
		if(space_invader_y == 0)
		{
			space_invader_down = 1;
		}
	}
	
	space_invader_counter++;
	if(space_invader_counter == 4)
	{
		if(space_invader_frame == 1) space_invader_frame = 2;
		else space_invader_frame = 1;
		
		space_invader_counter = 0;
	}
}

// ------------------------------------------------ //

SIGNAL(SIG_INTERRUPT0)
{
	// Disable interrupts
	cli();
	
	// Start of display area
	
	drawScreen();
	
	// Enable interrupts
	sei();
}

// ------------------------------------------------ //

ISR(TIMER0_OVF_vect)
{
	if(counter == 100)
	{
		//if(gol_initialized == 1) GameOfLife_addGlider();
		
		SpaceInvader_animation();
		
		counter = 0;
	}
	else
	{
		counter++;
	}
	
	// When second_counter reaches 800, about one second has passed
	second_counter++;
	
	if(second_counter == 800) // 800
	{
		// One second passed, do what ever needed
		
		//if(gol_initialized == 1) GameOfLife();
		
		second_counter = 0;
	}
}

// ------------------------------------------------ //

int Uart_putchar(uint8_t character)
{
	// Send characters to the serial line
	
	loop_until_bit_is_set(UCSRA, UDRE); // Wait until the last character is sent
	UDR = character;
	return 0;
}

SIGNAL(SIG_UART_RECV)
{
	// UART interrupt function to receive characters from the serial line
	
	uint8_t character;
	character = UDR;
	
	//drawScreen();
	
	//cmd_buffer[o] = character;
	//Uart_putchar(character);
	
	//o++;
	//if(o == BUFFER_SIZE) o = 0;
	
	/*
	if(character != '\r')
	{
		// Receive all characters until newline and carriage return
		
		if(character != '\n')
		{
			cmd_buffer[o] = character;
			
			o++;
			if(o == BUFFER_SIZE) o = 0;
		}
	}
	else if(character == '\r')
	{
		cli();
		
		if(cmd_buffer[0] == 'D' && cmd_buffer[1] == 'S')
		{
			drawScreen();
		}
		
		for(o=0 ; o<BUFFER_SIZE ; o++)
		{
			cmd_buffer[o] = 0;
		}
		
		o = 0;
		
		sei();
	}
	*/
}

void Init_USART(void)
{
	// Initialize UART
	
	// USART Mode: Asynchronous
	UBRRH = 0x00; // write high byte first
	UBRRL = ((XTAL/16)/BAUD)-1; // baud rate 9600
	UCSRA = 0x00;
	UCSRB |= (1 << 3); // TXEN ON, TX ON,        xxxx 1xxx
	UCSRB |= (1 << 4); // RXEN, RX ON,           xxx1 1xxx
	UCSRB |= (1 << 7); // RXIE, RX interrupt ON, 1xx1 1xxx
	UCSRC = 0x86; // 8 Data, 1 Stop, No Parity   1xxx x11x
	//SREG = (1 << 7); // global interrupt ON      1xxx xxxx
}

// ------------------------------------------------ //

int main(void)
{
	DDRC = 0x00;	// Port C input
	PORTC = 0xFF;	// Enable internal pull-ups
	
	DDRB = 0xFF;	// Port B output
	PORTB = 0x00;	// Set all outputs low
	
	DDRD = 0b00100000;
	PORTD = 0b00001100;
	
	SR_595_PORT |= _BV(SR_595_CLR); // Set 595 clear high
	
	// Disable global interrupts
	cli();
	
	// Initialize timers
	TCCR0 = 0x00;
	TCNT0 = 0x00;
	TCCR0 = 0b00000011;
	
	MCUCR |= _BV(ISC01); // INT0 on falling edge
	GICR |= _BV(INT0); // Enable INT0
	TIMSK = 0b00000001;
	
	// Initialize UART
	Init_USART();
	
	// Enable global interrupts
	sei();
	
	// Start
	
	for(o=0 ; o<32 ; o++)
	{
		SR_595_send_bit(0);
	}
	SR_595_latch();
	
	// Initialize Game of Life
	//GameOfLife_init();
	
	while(1)
	{
		//
	}
}
