/* ttt.c
 
   A very simple tic-tac-toe game.
   
   bill kendrick
   bill@newbreedsoftware.com

   2003.Mar.14
   
   Ported to PSP by BlackPhoenix
   24/02/2005
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL/SDL.h>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_ttf.h>
#include <pspctrl.h>

/* Ints */
int winning_style, winning_pos, done, x, y, 
	player, winner, use_sound, yy, 
	vs_computer, want_x, want_y, r, c, 
	buttonDown, gameMode, xwins, owins;
/* Array Ints */
int grid[3][3], temp_grid[3][3], select[3][3];
/* SDL Images */
SDL_Surface * screen 		 = NULL;
SDL_Surface * img_x			 = NULL;
SDL_Surface * img_o			 = NULL;
SDL_Surface * img_grid		 = NULL;
SDL_Surface * img_title		 = NULL;
SDL_Surface * img_win_x		 = NULL;
SDL_Surface * img_win_o		 = NULL;
SDL_Surface * img_win_draw	 = NULL;
SDL_Surface * img_turn_x	 = NULL;
SDL_Surface * img_turn_o	 = NULL;
SDL_Surface * img_start_1ply = NULL;
SDL_Surface * img_start_2ply = NULL;
SDL_Surface * g1			 = NULL;
SDL_Surface * g2			 = NULL;
SDL_Surface * hand			 = NULL;
SDL_Surface * temp_img		 = NULL;
/* Area Descriptors */
SDL_Rect dest;
/* Audio */
Mix_Chunk * snd_intro 		 = NULL;
Mix_Chunk * snd_o			 = NULL;
Mix_Chunk * snd_x 			 = NULL;
Mix_Chunk * snd_badspot		 = NULL;
Mix_Chunk * snd_win			 = NULL;
Mix_Chunk * snd_draw		 = NULL;
Mix_Chunk * snd_start		 = NULL;
/* Fonts */
TTF_Font * font				 = NULL;
SDL_Surface * text	 = NULL;
/* Input */
SceCtrlData pad;
/* Timer */
Uint32 lastTock				 = 0;
unsigned const int speed	 = 150;
/* Prototypes */
int Initalise();
void debugReport(char * str, ...);
SDL_Surface * imgLoad(char * fname);
int loadImages();
int loadSounds();
int loadFonts();
int txt(char * str, int c, int v, int h);
int menuLoop();
void playTurn();
void aI();
void drawPieces();
void drawSelect();
void drawWinner();
void selectBlock();
void clrSelect();
void clrGrid();
void dupGrid(int src[3][3], int dest[3][3]);
int fullGrid(int grid[3][3]);
void swapPlayer();
int checkMove(int grid[3][3]);
void victory();

/* Enums */
enum Dimentions{
	HORIZ,
	VERT,
	DIAG
};
enum TxtColours{
	RED,
	GREEN,
	BLUE,
	WHITE,
	BLACK
};

int main(int argc, char *argv[]){
		
	/* Initialize: */
	if(!Initalise())
		debugReport("Initalisation Failed\n\r");
		
	/* --- MAIN GAME LOOP! --- */
	do {
		if(SDL_GetTicks()-lastTock > speed){
			if(winner){
				victory();
				gameMode = 0;
			}
			switch(gameMode){
				case 1:	//# Player Vs CPU
					switch(player){
						case 1: //# Player
							playTurn();
						break;
						case 2: //# CPU
							aI();
						break;
					}
				break;
				case 2: //# Player Vs Player
					switch(player){
						case 1: //# Player 1
							playTurn();
						break;
						case 2: //# Player 2
							playTurn();
						break;
					}
				break;
				default:
					gameMode = menuLoop();
					if(!gameMode){
						SDL_Quit();
						exit(1);
					} else {
						winner = 0;
						//player = 1;
						do {
							player = rand() % 2;
						} while(player == 0);
						clrSelect();
						clrGrid();
						r = c = 1;
						select[1][1] = 1;
						lastTock = 0;
					}
				break;
			}
			drawPieces();			
			SDL_Flip(screen);
		}
	} while(!done);
}

int Initalise(){
	/* Global Inits */
	
	//# TODO: Load var from config file...
	use_sound = 1;
	
	done = 0;
	//player = 1;
	do {
		player = rand() % 2;
	} while(player == 0);
	winner = 0;
	gameMode = 0;
	r = c = 1;
	clrSelect();
	select[r][c] = 1;
	lastTock = 0;
	
	/* SDL Initalise */
	if (SDL_Init(SDL_INIT_VIDEO) < 0)
		debugReport("SDL_Init\r\n");
	/* Load TTF Fonts */
	if(!loadFonts())
		debugReport("Load Fonts Failed\n\r");
	/* Load Images */
	if(!loadImages())
		debugReport("Load Images Failed\n\r");
	/* Load sounds: */
	if(!loadSounds())
		debugReport("Load Sounds Failed\n\r");
	
	/* Reset game: */
	clrGrid();
	
	/* Clear screen: */
	dest.x = 0;
	dest.y = 0;
	dest.w = 480;
	dest.h = 272;
	SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 0, 0, 0)); //# pesky cursor icon clear
	/* TicTacToe grid */
	dest.x = 60;
	dest.y = 16;
	SDL_BlitSurface(img_grid, NULL, screen, &dest);
	
	dest.x = 315;
	dest.y = 16;
	SDL_BlitSurface(img_title, NULL, screen, &dest);
	
	dest.x = 315;
	dest.y = 136;
	SDL_BlitSurface(img_start_1ply, NULL, screen, &dest);
	
	dest.x = 315;
	dest.y = 176;
	SDL_BlitSurface(img_start_2ply, NULL, screen, &dest);
	
	txt("SCORE", WHITE, 5, 5);
	drawWinner();
	
	/* Draw init screen */
	SDL_Flip(screen);
	
	/* Play init sound: */
	if (use_sound)
		Mix_PlayChannel(0, snd_intro, 0);
		
	return 1;
}

void debugReport(char * str, ...){
	fprintf(stderr, "Error: %s: %s\n", str, SDL_GetError());
	SDL_Quit();
	exit(1);
}

/* Load and convert a BMP: */
SDL_Surface * imgLoad(char * fname){
	return SDL_DisplayFormat(SDL_LoadBMP(fname));
}

int loadImages(){
	
	screen = SDL_SetVideoMode(480, 272, 16, SDL_SWSURFACE);
	if (screen == NULL)
		debugReport("SDL_SetVideoMode");
		
	txt("Loading...", GREEN, 225, 132);
	
	SDL_Flip(screen);
		
	img_grid = imgLoad("images/grid.bmp");
	if (img_grid == NULL)
		debugReport("grid.bmp");
	
	img_x = imgLoad("images/x.bmp");
	if (img_x == NULL)
		debugReport("x.bmp");

	img_o = imgLoad("images/o.bmp");
	if (img_o == NULL)
		debugReport("o.bmp");

	img_title = imgLoad("images/title.bmp");
	if (img_title == NULL)
		debugReport("title.bmp");

	img_turn_x = imgLoad("images/turn_x.bmp");
	if (img_turn_x == NULL)
		debugReport("turn_x.bmp");
  
	img_turn_o = imgLoad("images/turn_o.bmp");
	if (img_turn_o == NULL)
		debugReport("turn_o.bmp");
  
	img_win_x = imgLoad("images/win_x.bmp");
	if (img_win_x == NULL)
		debugReport("win_x.bmp");

	img_win_o = imgLoad("images/win_o.bmp");
	if (img_win_o == NULL)
		debugReport("win_o.bmp");

	img_win_draw = imgLoad("images/win_draw.bmp");
	if (img_win_draw == NULL)
		debugReport("win_draw.bmp");

	img_start_1ply = imgLoad("images/start_1ply.bmp");
	if (img_start_1ply == NULL)
		debugReport("win_start_1ply.bmp");

	img_start_2ply = imgLoad("images/start_2ply.bmp");
	if (img_start_2ply == NULL)
		debugReport("win_start_2ply.bmp");

	g1 = imgLoad("images/g1.bmp");
	if (g1 == NULL)
		debugReport("g1.bmp");

	g2 = imgLoad("images/g2.bmp");
	if (g2 == NULL)
		debugReport("g2.bmp");
	
	hand = imgLoad("images/hand.bmp");
	if (hand == NULL)
		debugReport("hand.bmp");
	
	if(SDL_SetColorKey(hand, SDL_SRCCOLORKEY, SDL_MapRGB(hand->format, 255, 0, 255)) == -1)
		fprintf(stderr, "ColourKey not set right...\r\n");
	
	return 1;
}

int loadSounds(){
	
	if (SDL_Init(SDL_INIT_AUDIO) < 0)
		use_sound = 0;

	if (use_sound)
		if (Mix_OpenAudio(44100, AUDIO_S16, 2, 512) < 0)
			debugReport("Mix_OpenAudio");

	if (use_sound){
		snd_intro = Mix_LoadWAV("sounds/intro.wav");
		if (snd_intro == NULL)
			debugReport("intro.wav");
		
		snd_x = Mix_LoadWAV("sounds/x.wav");
		if (snd_x == NULL)
			debugReport("x.wav");
		
		snd_o = Mix_LoadWAV("sounds/o.wav");
		if (snd_o == NULL)
			debugReport("o.wav");
		
		snd_badspot = Mix_LoadWAV("sounds/badspot.wav");
		if (snd_badspot == NULL)
			debugReport("badspot.wav");
		
		snd_win = Mix_LoadWAV("sounds/win.wav");
		if (snd_win == NULL)
			debugReport("win.wav");
		
		snd_draw = Mix_LoadWAV("sounds/draw.wav");
		if (snd_draw == NULL)
			debugReport("draw.wav");
		
		snd_start = Mix_LoadWAV("sounds/start.wav");
		if (snd_start == NULL)
			debugReport("start.wav");
	} else {
	    snd_intro = NULL;
	    snd_x = NULL;
	    snd_o = NULL;
	    snd_badspot = NULL;
	    snd_win = NULL;
	    snd_draw = NULL;
	    snd_start = NULL;
	}
	return 1;
}

int loadFonts(){
	if (TTF_Init() != 0)
		debugReport("TTF_Init error\r\n");

	if (!(font = TTF_OpenFont("arial.ttf", 12)))
		debugReport("TTF_OpenFont error\r\n");
	
	TTF_SetFontStyle(font, TTF_STYLE_NORMAL);
	
	return 1;
}

int txt(char * str, int colour, int v, int h){
	SDL_Rect rect;
	SDL_Color c;

	switch(colour){
		case 0:
			c.r = 255;
			c.g = 0;
			c.b = 0;
		break;
		case 1:
			c.r = 0;
			c.g = 255;
			c.b = 0;
		break;
		case 2:
			c.r = 0;
			c.g = 0;
			c.b = 255;
		break;
		case 3:
			c.r = 255;
			c.g = 255;
			c.b = 255;
		break;
		case 4:
			c.r = 0;
			c.g = 0;
			c.b = 0;
		break;
	}
	
	if(!(text = TTF_RenderText_Blended(font, str, c)))
		debugReport("TTF_RenderText error");
		
	rect.x = v;
	rect.y = h;
	
	SDL_BlitSurface(text, NULL, screen, &rect);
	
	return 1;
}

int menuLoop(){
	SDL_Rect dest1, dest2;
	int pos = 1;
	
	dest1.x = 315;
	dest1.y = 136;
	dest2.x = 315;
	dest2.y = 176;
	SDL_BlitSurface(img_start_1ply, NULL, screen, &dest1);
	SDL_BlitSurface(img_start_2ply, NULL, screen, &dest2);
	SDL_Delay(1000);

	while(1){
		if(SDL_GetTicks()-lastTock > speed){
			sceCtrlReadBufferPositive(&pad, 1);
			if (pad.Buttons != 0 && buttonDown != 1){
				
				if(pad.Buttons & PSP_CTRL_UP){
					pos += (pos > 1 ? -1 : 1);
					lastTock = SDL_GetTicks();
					buttonDown = 1;
				}
				if(pad.Buttons & PSP_CTRL_DOWN){
					pos += (pos < 2 ? 1 : -1);
					lastTock = SDL_GetTicks();
					buttonDown = 1;
				}
				if(pad.Buttons & PSP_CTRL_CROSS){
					lastTock = SDL_GetTicks();
					buttonDown = 1;
					return pos;
				}
				if(pad.Buttons & PSP_CTRL_START){
					lastTock = SDL_GetTicks();
					return 0;
				}
			} else if(pad.Buttons != 0)
				buttonDown = 0;
			if(pos < 2){
				SDL_BlitSurface(img_start_1ply, NULL, screen, &dest1);
				SDL_BlitSurface(g2, NULL, screen, &dest2);
			} else {
				SDL_BlitSurface(g1, NULL, screen, &dest1);
				SDL_BlitSurface(img_start_2ply, NULL, screen, &dest2);
			}
			SDL_Flip(screen);
		}
	}
	return 0;
}

void playTurn(){
	int playTurn = 1;
	lastTock = 0;
	
	do {
		if(SDL_GetTicks()-lastTock > speed){
			sceCtrlReadBufferPositive(&pad, 1);
			if(pad.Buttons != 0 && buttonDown != 1){
			
				if(pad.Buttons & PSP_CTRL_UP){
					r -= (r > 0 ? 1 : 0);
					buttonDown = 1;
					lastTock = SDL_GetTicks();
				}
					
				if(pad.Buttons & PSP_CTRL_DOWN){
					r += (r < 2 ? 1 : 0);
					buttonDown = 1;
					lastTock = SDL_GetTicks();
				}
				
				if(pad.Buttons & PSP_CTRL_LEFT){
					c -= (c > 0 ? 1 : 0);
					buttonDown = 1;
					lastTock = SDL_GetTicks();
				}
				
				if(pad.Buttons & PSP_CTRL_RIGHT){
					c += (c < 2 ? 1 : 0);
					buttonDown = 1;
					lastTock = SDL_GetTicks();
				}
				
				if(pad.Buttons & PSP_CTRL_CROSS){
					if(grid[r][c] == 0){
						grid[r][c] = player;
						swapPlayer();
						playTurn = 0;
						if(use_sound)
							Mix_PlayChannel(0, snd_x, 0);
					} else {
						if (use_sound)
							Mix_PlayChannel(0, snd_badspot, 0);
					}
					buttonDown = 1;
					lastTock = SDL_GetTicks();
				}
				
				if(pad.Buttons & PSP_CTRL_START){
					SDL_Quit();
					exit(1);
				}

			} else if(pad.Buttons == 0)
				buttonDown = 0;
			
			/* Clear Select Grid */
			for (y = 0; y < 3; y++)
				for (x = 0; x < 3; x++)
					select[y][x] = 0;
			
			select[r][c] = 1;
			
			/* Clear screen: */
			dest.x = 60;
			dest.y = 16;
			SDL_BlitSurface(img_grid, NULL, screen, &dest);
			
			drawPieces();
			drawSelect();
			
			SDL_Flip(screen);
		}
	} while(playTurn);
	
}

void aI(){

	int n = 0;
	int k = 0;
	
	do {
		want_x = rand() % 3;
		want_y = rand() % 3;
	} while (grid[want_y][want_x] != 0);
	
	/* Look for a BLOCKING move: */
	for (n = 0; n < 3; n++){
		for (k = 0; k < 3; k++){
			if (grid[n][k] == 0){
				dupGrid(grid, temp_grid);
				temp_grid[n][k] = 1;
				if (checkMove(temp_grid) == 1){
					/* Player will win if they pick this spot! */
					/* Let's choose it, to block: */
					want_x = k;
					want_y = n;
				}
			}
		}
	}
	
	/* Look for a WINNING move: */
	for (n = 0; n < 3; n++){
		for (k = 0; k < 3; k++){
			if (grid[n][k] == 0){
				dupGrid(grid, temp_grid);
				temp_grid[n][k] = 2;
				if (checkMove(temp_grid) == 2){
					/* Computer will win if it picks this spot! */
					/* Choose it!  We'll win!  Yay! */
					want_x = k;
					want_y = n;
				}
			}
		}
	}
	/* Place the computer's spot: */
	grid[want_y][want_x] = 2;

	/* Pretend to think about it for a random amount of time... */
	SDL_Delay(1000 + (rand() % 1000));
	
	if (use_sound)
		Mix_PlayChannel(0, snd_o, 0);

	/* Switch back to the human player: */
	swapPlayer();
}

void drawPieces(){
	for(y = 0; y < 3; y++){
		for(x = 0; x < 3; x++){
			if(grid[y][x] == 1)
				temp_img = img_x;
			else if(grid[y][x] == 2)
				temp_img = img_o;
			else if(grid[y][x] == 0)
				continue;
			dest.y = 16 + (84 * y);
			dest.x = 60 + (84 * x);
			SDL_BlitSurface(temp_img, NULL, screen, &dest);
		}
	}
	SDL_BlitSurface(temp_img, NULL, screen, &dest);
	temp_img = NULL;
}

void drawSelect(){ /* Draw hand on selected block */
	if(select[r][c] == 1){
		dest.y = 16 + (84 * r);
		dest.x = 60 + (84 * c);
		SDL_BlitSurface(hand, NULL, screen, &dest);
	}
}

void drawWinner(){
	char tmpStr[256];
	
	dest.x = 5;
	dest.y = 20;
	dest.w = 55;
	dest.h = 55;
	SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 0, 0, 0));
	
	sprintf(tmpStr, "X :  %d", xwins);
	txt(tmpStr, BLUE, 15, 25);
	
	sprintf(tmpStr, "O :  %d", owins);
	txt(tmpStr, RED, 15, 40);
}

void clrSelect(){ /* Clear the hand select cursor */
	for (y = 0; y < 3; y++)
		for (x = 0; x < 3; x++)
			select[y][x] = 0;

}

void clrGrid(){ /* Reset TicTacToe board */
	for (y = 0; y < 3; y++)
		for (x = 0; x < 3; x++)
			grid[y][x] = 0;
}

void dupGrid(int src[3][3], int dest[3][3]){ /* Duplicate board for test conditions */
	for (y = 0; y < 3; y++)
		for (x = 0; x < 3; x++)
			dest[y][x] = src[y][x];
}

void swapPlayer(){
	player = (player == 1 ? 2 : 1);
	dest.x = 315;
	dest.y = 216;
	if (player == 1)
		SDL_BlitSurface(img_turn_x, NULL, screen, &dest);
	else if (player == 2)
		SDL_BlitSurface(img_turn_o, NULL, screen, &dest);

	winner = (checkMove(grid) != 0 ? checkMove(grid) : 0);
	if(winner == 1)
		xwins++;
	else if(winner == 2)
		owins++;
	if(winner != 0 && winner != 9)
		drawWinner();
}

int fullGrid(int grid[3][3]){ /* No more moves? */
	for (y = 0; y < 3; y++)
		for (x = 0; x < 3; x++)
			if(grid[y][x] == 0)
				return 0;
	return 9;
}

int checkMove(int g[3][3]){
	int i;
	
	/* Check for horizontal and vertical lines: */
	for (i = 0; i < 3; i++){
		if (g[i][0] == g[i][1] && g[i][0] == g[i][2] &&	g[i][0] != 0){
			winning_style = HORIZ;
			winning_pos = i;
			return(g[i][0]);
		}
		if (g[0][i] == g[1][i] && g[0][i] == g[2][i] &&	g[0][i] != 0){
			winning_style = VERT;
			winning_pos = i;
			return(g[0][i]);
		}
	}
	
	/* Check top-left-to-bottom-right diagonal: */
	if (g[0][0] == g[1][1] && g[0][0] == g[2][2] && g[0][0] != 0){
		winning_style = DIAG;
		winning_pos = 0;
		return(g[0][0]);
	}
	
	/* Check top-right-to-bottom-left diagonal: */
	if (g[2][0] == g[1][1] && g[2][0] == g[0][2] && g[2][0] != 0){
		winning_style = DIAG;
		winning_pos = 1;
		return(g[2][0]);
	}
	
	if(fullGrid(g) == 9){
		winner = 9;
		return 9;
	}
	
	return 0;
}

void drawShade(int shadeX, int shadeY){ /* blank out block */
	for (yy = 0; yy < 72; yy = yy + 3){
		dest.x = 60 + (84 * shadeX);
		dest.y = 16 + (84 * shadeY + yy);
		
		dest.w = 72;
		dest.h = 2;
		SDL_FillRect(screen, &dest,SDL_MapRGB(screen->format, 0, 0, 0));
	}
}

void victory(){
	/* Show who won: */
	dest.x = 315;
	dest.y = 216;
	if (winner == 1){
		SDL_BlitSurface(img_win_x, NULL, screen, &dest);
		if (use_sound)
			Mix_PlayChannel(0, snd_win, 0);
	} else if (winner == 2) {
		SDL_BlitSurface(img_win_o, NULL, screen, &dest);
		if (use_sound){
			if(gameMode == 2)
				Mix_PlayChannel(0, snd_win, 0);
			else
				Mix_PlayChannel(0, snd_badspot, 0);
		}
	} else if (winner == 9){
		SDL_BlitSurface(img_win_draw, NULL, screen, &dest);
		if(use_sound)
			Mix_PlayChannel(0, snd_draw, 0);
	}
	/* Shade non-winning pieces: */
	for (y = 0; y < 3; y++){
		for (x = 0; x < 3; x++){
			if(winner == 9)
				drawShade(x,y);
			else {
				if ((winning_style == HORIZ && winning_pos == y) ||
				(winning_style == VERT && winning_pos == x) ||
				(winning_style == DIAG && winning_pos == 0 && x == y) ||
				(winning_style == DIAG && winning_pos == 1 && x == 2 - y)){
					/* A winning spot...  Do nothing */
				} else
					drawShade(x, y);
			}
		}
	}
}
