/*
 * Tetris.c
 *
 *  Created on: Dec 1, 2010
 *      Author: gmc895
 *		Gregory Christ
 *		
 */

#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <rtai.h>
#include <rtai_lxrt.h>
#include <semaphore.h>

#define row	13
#define col	16

char game_data[row][col];

typedef struct{
	int btn_push;
	char btn_dir;
}message;

struct rt_mailbox *btn_mbox;

int cur_top_row;
int cur_btm_row;
int cur_left_col;
int cur_right_col;
int cur_cntr;

int cur_shape;
int cur_dir;

int new_game;
int start_new_shape;

RT_TASK *drop;
RTIME drop_time;


void print_data(){

	int i;
	int j;

	if(new_game == 0){
		for(i = 0;i <= 16; i++){			//clears old data
			printf("%c[1A", 27);
			printf("%c[2K", 27);
		}
	}

	for(i = 0;i <= row; i++){				//prints new game data
		for(j = 0;j <= col; j++){
			printf("%c",game_data[i][j]);
		}
		printf("\n");
	}
	printf("%i%i\n", cur_shape, cur_dir);
	//printf("%i%c\n", btn_push, btn_dir);


}


void left(){

	int i;
	int j;
	if(cur_shape == 0){

		if(cur_dir == 0){

			if( (game_data[cur_top_row][cur_cntr-1] == ' ') && (game_data[cur_btm_row][cur_cntr-1] == ' ') &&
				(game_data[cur_top_row-1][cur_cntr-1] == ' ') && (game_data[cur_btm_row+1][cur_cntr-1] == ' ')) {

				cur_cntr--;
				for(i = cur_top_row; i <= cur_btm_row; i++){

						game_data[i][cur_cntr] = game_data[i][cur_cntr+1];
						game_data[i][cur_cntr+1] = ' ';
				}
			}
		}
		else if(cur_dir ==1){

			if( (game_data[cur_top_row][cur_left_col-1] == ' ')) {

				cur_left_col--;
				cur_right_col--;
				cur_cntr--;

				for(j = cur_left_col; j <= cur_right_col; j++){
					game_data[cur_top_row][j] = 'O';
					game_data[cur_top_row][j+1] = ' ';
				}
			}
		}
	}

	else if(cur_shape == 1){

		if(cur_dir == 0){

			if( (game_data[cur_top_row][cur_left_col-1] == ' ') && (game_data[cur_btm_row][cur_cntr-1] == ' ') ){

				game_data[cur_top_row][cur_right_col] = ' ';
				game_data[cur_btm_row][cur_cntr] = ' ';

				cur_right_col--;
				cur_cntr--;
				cur_left_col--;

				for(j = cur_left_col; j <= cur_right_col; j++){
					game_data[cur_top_row][j] = 'O';
				}
				game_data[cur_btm_row][cur_cntr] = 'O';

			}
		}
		else if(cur_dir == 1){

			if( (game_data[cur_top_row][cur_right_col-1] == ' ') && (game_data[cur_cntr][cur_left_col-1] == ' ') &&
					(game_data[cur_btm_row][cur_right_col-1] == ' ') ){

				cur_left_col--;
				cur_right_col--;

				for(i = cur_top_row; i <= cur_btm_row; i++){
					game_data[i][cur_right_col] = 'O';
					game_data[i][cur_right_col+1] = ' ';
				}
				game_data[cur_cntr][cur_left_col] = 'O';
				game_data[cur_cntr][cur_right_col+1] = ' ';

			}
		}
		else if(cur_dir == 2){

			if( (game_data[cur_top_row][cur_cntr-1] == ' ') && (game_data[cur_btm_row][cur_left_col-1] == ' ') ){

				game_data[cur_btm_row][cur_right_col] = ' ';
				game_data[cur_top_row][cur_cntr] = ' ';

				cur_right_col--;
				cur_cntr--;
				cur_left_col--;

				for(j = cur_left_col; j <= cur_right_col; j++){
					game_data[cur_btm_row][j] = 'O';
				}
				game_data[cur_top_row][cur_cntr] = 'O';

			}

		}
		else if(cur_dir == 3){

			if( (game_data[cur_top_row][cur_left_col-1] == ' ') && (game_data[cur_cntr][cur_left_col-1] == ' ') &&
					(game_data[cur_btm_row][cur_left_col-1] == ' ') ){

				cur_left_col--;
				cur_right_col--;

				for(i = cur_top_row; i <= cur_btm_row; i++){
					game_data[i][cur_left_col] = 'O';
					game_data[i][cur_left_col+1] = ' ';
				}
				game_data[cur_cntr][cur_right_col] = 'O';
				game_data[cur_cntr][cur_right_col+1] = ' ';

			}
		}
	}
	print_data();
}


void right(){

	int i;
	int j;
	if(cur_shape == 0){

		if(cur_dir == 0){

			if( (game_data[cur_top_row][cur_cntr+1] == ' ') && (game_data[cur_btm_row][cur_cntr+1] == ' ') &&
					(game_data[cur_top_row-1][cur_cntr+1] == ' ') && (game_data[cur_btm_row+1][cur_cntr+1] == ' ')) {

				cur_cntr++;

				for(i = cur_top_row; i <= cur_btm_row; i++){
					game_data[i][cur_cntr] = game_data[i][cur_cntr-1];
					game_data[i][cur_cntr-1] = ' ';
				}
			}
		}
		else if(cur_dir == 1){

			if( (game_data[cur_top_row][cur_right_col+1] == ' ') ) {

				cur_left_col++;
				cur_right_col++;
				cur_cntr++;

				game_data[cur_top_row][cur_left_col-1] = ' ';
				for(j = cur_left_col; j <= cur_right_col; j++){
					game_data[cur_top_row][j] = 'O';
				}

			}
		}
	}
	else if(cur_shape == 1){

		if(cur_dir == 0){

			if( (game_data[cur_top_row][cur_right_col+1] == ' ') && (game_data[cur_btm_row][cur_cntr+1] == ' ') ){

				game_data[cur_top_row][cur_left_col] = ' ';
				game_data[cur_btm_row][cur_cntr] = ' ';

				cur_right_col++;
				cur_cntr++;
				cur_left_col++;

				for(j = cur_left_col; j <= cur_right_col; j++){
					game_data[cur_top_row][j] = 'O';
				}
				game_data[cur_btm_row][cur_cntr] = 'O';

			}
		}
		else if(cur_dir == 1){

			if( (game_data[cur_top_row][cur_right_col+1] == ' ') && (game_data[cur_cntr][cur_right_col+1] == ' ') &&
					(game_data[cur_btm_row][cur_right_col+1] == ' ') ){

				cur_left_col++;
				cur_right_col++;

				for(i = cur_top_row; i <= cur_btm_row; i++){
					game_data[i][cur_right_col] = 'O';
					game_data[i][cur_right_col-1] = ' ';
				}
				game_data[cur_cntr][cur_left_col] = 'O';
				game_data[cur_cntr][cur_left_col-1] = ' ';

			}
		}
		else if(cur_dir == 2){

			if( (game_data[cur_top_row][cur_cntr+1] == ' ') && (game_data[cur_btm_row][cur_right_col+1] == ' ') ){

				game_data[cur_btm_row][cur_left_col] = ' ';
				game_data[cur_top_row][cur_cntr] = ' ';

				cur_right_col++;
				cur_cntr++;
				cur_left_col++;

				for(j = cur_left_col; j <= cur_right_col; j++){
					game_data[cur_btm_row][j] = 'O';
				}
				game_data[cur_top_row][cur_cntr] = 'O';

			}

		}
		else if(cur_dir == 3){

			if( (game_data[cur_top_row][cur_left_col+1] == ' ') && (game_data[cur_cntr][cur_right_col+1] == ' ') &&
					(game_data[cur_btm_row][cur_left_col+1] == ' ') ){

				cur_left_col++;
				cur_right_col++;

				for(i = cur_top_row; i <= cur_btm_row; i++){
					game_data[i][cur_left_col] = 'O';
					game_data[i][cur_left_col-1] = ' ';
				}
				game_data[cur_cntr][cur_right_col] = 'O';
			}
		}
	}

	print_data();
}


void up(){

	if(cur_shape == 0){

		if(cur_dir == 0){
			cur_dir = 1;

			game_data[cur_top_row][cur_cntr] = ' ';
			game_data[cur_btm_row-1][cur_cntr] = ' ';
			game_data[cur_btm_row][cur_cntr] = ' ';

			cur_top_row = cur_top_row+1;
			cur_btm_row = cur_top_row;
			cur_left_col = cur_cntr-2;
			cur_right_col = cur_cntr+1;

		}
		else if(cur_dir == 1){
			cur_dir = 0;

			game_data[cur_top_row][cur_left_col] = ' ';
			game_data[cur_top_row][cur_left_col+1] = ' ';
			game_data[cur_top_row][cur_right_col] = ' ';

			cur_top_row = cur_top_row-1;
			cur_btm_row = cur_btm_row+2;
			cur_cntr = cur_right_col-1;
			cur_left_col = cur_cntr;

		}
	}

	else if(cur_shape == 1){

		if(cur_dir == 0){
			cur_dir = 1;

			game_data[cur_top_row][cur_left_col] = ' ';
			game_data[cur_top_row][cur_left_col+1] = ' ';

			cur_left_col = cur_cntr;
			cur_right_col = cur_cntr+1;
			cur_top_row = cur_top_row;
			cur_cntr = cur_btm_row;
			cur_btm_row = cur_cntr+1;

		}
		else if(cur_dir == 1){
			cur_dir = 2;

			game_data[cur_cntr-1][cur_right_col] = ' ';
			game_data[cur_cntr][cur_right_col] = ' ';

			cur_top_row = cur_cntr;
			cur_btm_row = cur_cntr+1;
			cur_cntr = cur_left_col;
			cur_right_col = cur_right_col;
			cur_left_col = cur_cntr-1;
		}
		else if(cur_dir == 2){
			cur_dir = 3;

			game_data[cur_btm_row][cur_right_col] = ' ';
			game_data[cur_btm_row][cur_right_col-1] = ' ';

			cur_right_col = cur_cntr;
			cur_cntr = cur_top_row;
			cur_left_col = cur_left_col;
			cur_top_row = cur_top_row-1;
			cur_btm_row = cur_btm_row;

		}
		else if(cur_dir == 3){
			cur_dir = 0;

			game_data[cur_cntr][cur_left_col] = ' ';
			game_data[cur_btm_row][cur_left_col] = ' ';

			cur_left_col = cur_left_col;
			cur_btm_row = cur_cntr;
			cur_cntr = cur_right_col;
			cur_right_col = cur_cntr+1;
			cur_top_row = cur_top_row;

		}

	}

	shape();
	print_data();

}

void child(int i, unsigned long *ptr, message msg){

	unsigned long t_name;
	int period;
	int offset;
	int button;
	int push;
	RT_TASK *task;

	period = nano2count(30000000);
	offset = nano2count(10000000);

	if(i == 0){
		t_name = nam2num("b0");
		task = rt_task_init(t_name, 2, 512, 256);
		rt_task_make_periodic(task, rt_get_time(), period);
	}
	else if(i == 1){
		t_name = nam2num("b1");
		task = rt_task_init(t_name, 3, 512, 256);
		rt_task_make_periodic(task, rt_get_time() + (offset*i), period);
	}
	else if(i == 2){
		t_name = nam2num("b2");
		task = rt_task_init(t_name, 4, 512, 256);
		rt_task_make_periodic(task, rt_get_time() + (offset*i), period);
	}

	while(1){

		if(i == 0){
			button = *ptr & 0x01;
			button = button >> i;
			if(button == 0x00){ push = 1;}
			if(push == 1){
				if(button ==0x01){
					push = 0;
					//printf("Button 0\n");

					msg.btn_push = 0;
					msg.btn_dir = 'L';
					rt_mbx_send(btn_mbox, &msg, sizeof(message));

				}
			}
		}
		else if(i == 1){
			button = *ptr & 0x02;
			button = button >> i;
			if(button == 0x00){ push = 1;}
			if(push == 1){
				if(button ==0x01){
					push = 0;
					//printf("Button 1\n");

					msg.btn_push = 1;
					msg.btn_dir = 'R';
					rt_mbx_send(btn_mbox, &msg, sizeof(message));

				}
			}
		}
		else if(i == 2){
			button = *ptr & 0x04;
			button = button >> i;
			if(button == 0x00){ push = 1;}
			if(push == 1){
				if(button ==0x01){
					push = 0;
					//printf("Button 2\n");

					msg.btn_push = 2;
					msg.btn_dir = 'U';
					rt_mbx_send(btn_mbox, &msg, sizeof(message));

				}
			}
		}

		rt_task_wait_period();
	}
}


void shape(int start_new_shape){

	int i;
	int j;


	if(cur_shape == 0){						// place new shape I into array

		if(cur_dir == 0){					// down I
			if(start_new_shape == 1){		//
				cur_top_row = 0;			//  O
				cur_btm_row = 3;			//  O
				cur_cntr = 8;				//  O
			}						//  O

			for(i = cur_top_row; i <= cur_btm_row; i++){
				game_data[i][cur_cntr] = 'O';
			}
		}
		else if(cur_dir == 1){				// sideways I
			if(start_new_shape == 1){		//
				cur_top_row = 0;			//  OOOO
				cur_btm_row = 0;
				cur_left_col = 7;
				cur_right_col = 10;
				cur_cntr = 8;
			}

			for(j = cur_left_col; j <= cur_right_col; j++){
				game_data[cur_top_row][j] = 'O';
			}
		}
	}

	else if (cur_shape == 1){				// place new shape T into array

		if(cur_dir == 0){					// down T
			if(start_new_shape == 1){		//
				cur_top_row = 0;			//  OOO
				cur_btm_row = 1;			//   O
				cur_left_col = 7;
				cur_right_col = 9;
				cur_cntr = 8;
			}

			for(j = cur_left_col; j <= cur_right_col; j++){
				game_data[cur_top_row][j] = 'O';
			}
			game_data[cur_btm_row][cur_cntr] = 'O';
		}
		else if(cur_dir == 1){				// left T
			if(start_new_shape == 1){		//
				cur_top_row = 0;			//   O
				cur_btm_row = 2;			//  OO
				cur_left_col = 8;			//   O
				cur_right_col = 9;
				cur_cntr = 1;
			}
			for(i = cur_top_row; i <= cur_btm_row; i++){
				game_data[i][cur_right_col] = 'O';
			}
			game_data[cur_cntr][cur_left_col] = 'O';


		}
		else if(cur_dir == 2){				// up T
			if(start_new_shape == 1){		//
				cur_top_row = 0;			//  O
				cur_btm_row = 1;			// OOO
				cur_left_col = 7;			//
				cur_right_col = 9;
				cur_cntr = 8;
			}

			for(j = cur_left_col; j <= cur_right_col; j++){
				game_data[cur_btm_row][j] = 'O';
			}
			game_data[cur_top_row][cur_cntr] = 'O';


		}
		else if(cur_dir == 3){				// right T
			if(start_new_shape == 1){		//
				cur_top_row = 0;			//  O
				cur_btm_row = 2;			//  OO
				cur_left_col = 7;			//  O
				cur_right_col = 8;
				cur_cntr = 1;
			}
			for(i = cur_top_row; i <= cur_btm_row; i++){
				game_data[i][cur_left_col] = 'O';
			}
			game_data[cur_cntr][cur_right_col] = 'O';

		}
	}
	//else if(cur_shape == 2){		//place new shape }
	//else if(cur_chape == 3){		//place new shape }
	//else if(cur_chape == 4){		//place new shape }
	//else if(cur_chape == 5){		//place new shape }
	//else if(cur_chape == 6){		//place new shape }
}


int drop_shape(int start_new_shape){

	int i;
	int j;

	if(start_new_shape == 1){									//determine new shape

		//rand()%2 => using 2 since I only have I and T shapes defined
		cur_shape = rand()%2;									//generate random shape

		//cur_shape = 1;
		//cur_dir = 3;

		//rand()%2 => using 2 since I only have I and using 4 since T shapes defined
		if(cur_shape == 0){ cur_dir = rand()%2; }				//random direction for I
		else if(cur_shape == 1){ cur_dir = rand()%4; }			//random direction for T

		//else if(cur_shape == 2){ cur_dir = rand()%4 : }		//random direction for L1
		//else if(cur_shape == 3){ cur_dir = rand()%4 : }		//random direction for L2
		//else if(cur_shape == 4){ cur_dir = rand()%2 : }		//random direction for Z1
		//else if(cur_shape == 5){ cur_dir = rand()%2 : }		//random direction for Z2
		//else if(cur_shape == 6){ cur_dir = 0; }				//only direction for S

		shape(start_new_shape);
		start_new_shape = 0;
	}
	else{
		start_new_shape = 0;

		cur_top_row++;								//move position of shape to look like dropping
		cur_btm_row++;

		if(cur_shape == 0){											//shape I falling

			if(cur_dir == 0){										//prints I0 falling

				if( (cur_btm_row <= row) && (game_data[cur_btm_row][cur_cntr] == ' ') ){
					for(i = cur_btm_row; i >=cur_top_row; i--){
						game_data[i][cur_cntr] = game_data[i-1][cur_cntr];
						game_data[i-1][cur_cntr] = ' ';
					}
				}
				else{start_new_shape = 1;}
			}

			else if(cur_dir == 1){									//prints I1 falling

				if( (cur_btm_row <= row) && (game_data[cur_btm_row][cur_left_col] == ' ') &&
						(game_data[cur_btm_row][cur_right_col] == ' ') && (game_data[cur_btm_row][cur_cntr] == ' ')
							&& (game_data[cur_btm_row][cur_left_col+1] == ' ')){

					for(j = cur_left_col; j <= cur_right_col; j++){
						game_data[cur_top_row][j] = game_data[cur_top_row-1][j];
						game_data[cur_top_row-1][j] = ' ';
					}
				}
				else{start_new_shape = 1;}
			}

		}

		else if(cur_shape == 1){									//shape T falling

			if(cur_dir == 0){										//prints T0, down T, falling

				if( (cur_btm_row <= row) && (game_data[cur_btm_row][cur_cntr] == ' ') &&
						(game_data[cur_top_row][cur_left_col] == ' ') && (game_data[cur_top_row][cur_right_col] == ' ') ){

					game_data[cur_btm_row][cur_cntr] = game_data[cur_btm_row-1][cur_cntr];
					for(j = cur_left_col; j <= cur_right_col; j++){
						game_data[cur_top_row][j] = game_data[cur_top_row-1][j];
						game_data[cur_top_row-1][j] = ' ';
					}
				}
				else{ start_new_shape = 1; }
			}
			else if(cur_dir == 1){									//prints T1, left T, falling

				cur_cntr++;

				if( (cur_btm_row <= row) && (game_data[cur_btm_row][cur_right_col] == ' ') &&
						(game_data[cur_cntr][cur_left_col] == ' ') ){

					for(i = cur_btm_row; i >= cur_top_row; i--){
						game_data[i][cur_right_col] = game_data[i-1][cur_right_col];
					}
					game_data[cur_top_row-1][cur_right_col] = ' ';

					game_data[cur_cntr][cur_left_col] = game_data[cur_cntr-1][cur_left_col];
					game_data[cur_cntr-1][cur_left_col] = ' ';
				}
				else{ start_new_shape = 1; }
			}

			else if(cur_dir == 2){										//prints T2, up T, falling

				if( (cur_btm_row <= row) && (game_data[cur_btm_row][cur_left_col] == ' ') &&
						(game_data[cur_btm_row][cur_cntr] == ' ') && (game_data[cur_btm_row][cur_right_col] == ' ') ){

					for(j = cur_left_col; j <= cur_right_col; j++){
						game_data[cur_btm_row][j] = game_data[cur_btm_row-1][j];
						game_data[cur_btm_row-1][j] = ' ';
					}
					game_data[cur_top_row][cur_cntr] = game_data[cur_top_row-1][cur_cntr];
					game_data[cur_top_row-1][cur_cntr] = ' ';
				}
				else{ start_new_shape = 1; }
			}

			else if(cur_dir == 3){									//prints T3, right T, falling

				cur_cntr++;

				if( (cur_btm_row <= row) && (game_data[cur_btm_row][cur_left_col] == ' ') &&
						(game_data[cur_cntr][cur_right_col] == ' ') ){

					for(i = cur_btm_row; i >= cur_top_row; i--){
						game_data[i][cur_left_col] = game_data[i-1][cur_left_col];
					}
					game_data[cur_top_row-1][cur_left_col] = ' ';

					game_data[cur_cntr][cur_right_col] = game_data[cur_cntr-1][cur_right_col];
					game_data[cur_cntr-1][cur_right_col] = ' ';
				}
				else{ start_new_shape = 1; }
			}
		}
		//else if(){

			// shape L1
				// direction 0
				// direction 1
				// direction 2
				// direction 3
		//}
		//else if(){

			// shape L2
				// direction 0
				// direction 1
				// direction 2
				// direction 3
		//}
		else{
			//
			//check line in array if completely full for all lines applicable for shape
			//check_next_line();
			//
			start_new_shape = 1;
		}


	}
	return start_new_shape;
}


void *timer(void *buffer){

	drop_time = start_rt_timer(nano2count(1000000000));
	drop = rt_task_init(nam2num("t1"), 0, 512, 256);
	rt_task_make_periodic(drop, rt_get_time(), nano2count(1000000000));

	int start_new_shape = 1;

	while(1){

		new_game = 0;
		print_data();
		rt_task_wait_period();
		start_new_shape = drop_shape(start_new_shape);

	}

}


void create_gameboard(){

	int i;
	int j;

	for(i = 0;i <= row; i++){						//creates original game board
		for(j = 0;j <= col; j++){
			if( (j == 0) || (j == 2) || (j == 14) || (j == 16) ){
				game_data[i][j] = '*';
			}
			else if((j == 1) || (j == 15) ){
				game_data[i][j] = '|';
			}
			else{
				game_data[i][j] = ' ';
			}
		}
	}

}



int main(void){


	int period;
	int i;
	int fd;
	unsigned long *ptr;
	RT_TASK *btn_time;
	pid_t fork_id;
	message msg;
	char buffer[256];
	pthread_t thread;

	fd = open("/dev/mem", O_RDWR);
	ptr = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0x80840000);
	ptr += 5;
	*ptr |= 0xF8;
	ptr -= 4;

	period = nano2count(1000000);

	btn_mbox = rt_typed_named_mbx_init("bmb", sizeof(message)*15, FIFO_Q);

	new_game = 0;
	create_gameboard();

	pthread_create(&thread, NULL, timer, (void*)buffer);

	for(i = 0; i <= 2; i++){
		if((fork_id = fork()) == 0){
			child(i, ptr, msg);
		}
	}

	btn_time = rt_task_init(nam2num("btn"), 0, 512, 256);
	rt_task_make_periodic(btn_time, rt_get_time(), period);

	while(1){

		rt_mbx_receive(btn_mbox, &msg, sizeof(message));

		if(msg.btn_push == 0){
			//cur_cntr--;
			//printf("Left, %i\n", cur_cntr);
			left();
		}
		else if(msg.btn_push == 1){
			//cur_cntr++;
			//printf("Right, %i\n", cur_cntr);
			right();
		}
		if(msg.btn_push == 2){

			//printf("Up, %i\n", cur_cntr);
			up();
		}

		rt_task_make_periodic(btn_time, rt_get_time(), period);
		rt_task_wait_period();

	}

	return 0;

}
