/****************************Project*******************************
 * This Project is called "Sudoku Assistant" It provides a Sudoku 
 * 9x9 grid for the player to solve Sudoku puzzels. I provide tool
 * options for the player like error detection and valid number 
 * identification. This program is not ment to solve Sudoku puzzels
 * by itself but rather augment a human player's ability yo solve the
 * puzzel. The Program will also have the ability to store puzzels 
 * previously solved by the player and rehash the numbers to make it seem 
 * a new puzzel.
 * 04/21/05
 * By:
 * Wilson Saunders
 ******************************File********************************
 * I am the World class I have meathods of rendering an internal grid
 * aswell as storage for game options and user controll.
 * 
 * This File Was Written:
 * 04/21/05
 * By:
 * Wilson Saunders
 ******************************************************************/

#include <allegro.h>
#include "world.h"
#include "ScreenDiv.h"

/*****************************************************************
 * I am the constructor I set the internal defaults
 ******************************************************************/
World::World(){
	int itor, itor2;

	for (itor = 0; itor < 10; itor++){
		numdown[itor] = false;
	}

	for (itor = 0; itor < 81; itor++){
		error[itor] = false;
		search[itor] = false;
		for (itor2 = 0; itor2 <10; itor2++){
			notes[itor][itor2] = false;
		}
	}

	mousedown = false;

	DrawBuffer = create_bitmap( SCREEN_W, SCREEN_H);
	myGrid = new Grid;
	myUndo = new Grid;
	myDepGrid = new DepGrid;
	
	ink = load_bitmap("Textures\\ink.bmp", palette);
	pencil = load_bitmap("Textures\\pencil.bmp", palette);
	note = load_bitmap("Textures\\note.bmp", palette);
	arrow = load_bitmap("Textures\\arrow.bmp", palette);

	Lock = load_bitmap("Textures\\Lock.bmp", palette);
	Undo = load_bitmap("Textures\\Undo.bmp", palette);
	Clear= load_bitmap("Textures\\Clear.bmp", palette);

	temp = create_bitmap(ink->w/3,ink->h/3);
	curloc = 0;
	cursearch = 1;
	

}

/*****************************************************************
 * I am the destructor I exist for good form
 ******************************************************************/
World::~World(){}


/*****************************************************************
 * I am the Update function. I check for user input
 ******************************************************************/
void World::Update(){
	int X,Y;
	int itor, itor2;
		
	if(	mouse_x > GRID_X && mouse_y > GRID_Y &&
		mouse_x < GRID_X+GRID_W && mouse_y < GRID_Y+GRID_H)
	{
		X = (mouse_x - GRID_X)/NODE_W;
		Y = (mouse_y - GRID_Y)/NODE_H;
		curloc = X + Y*9;
	} else{
		curloc =-1;
	}
		// keyboard input
	for (itor = 1; itor < 10; itor++){
		if(key[KEY_0+itor] && !numdown[itor]){
			myUndo->CopyGrid(myGrid);
			cursearch = itor;
			if(curloc >=0){
				if(key[KEY_TAB] ){
					myGrid->data[curloc]= itor +10;
				}else if(key[KEY_LSHIFT] ){
					notes[curloc][itor] = !notes[curloc][itor];
				}else {
					if(myGrid->data[curloc] < 10)
						myGrid->data[curloc]= itor ;
				}
			}
			for (itor2 = 0; itor2 < 81; itor2++){
				error[itor2] = ChkConflict(itor2);
				search[itor2] = ChkSearch(itor2);
				oneleft[itor2] = ChkOneLeft(itor2);
			}
		}
		numdown[itor] = key[KEY_0+itor];

	}
		// empty
	if(key[KEY_SPACE] && !numdown[0]){
		myUndo->CopyGrid(myGrid);
		if(curloc >=0){
			if(key[KEY_TAB]){
				myGrid->data[curloc]= 0;
			}else {
				if(myGrid->data[curloc] < 10)
					myGrid->data[curloc]= 0 ;
			}
		}
		for (itor = 0; itor < 81; itor++){
			error[itor] = ChkConflict(itor);
			search[itor] = ChkSearch(itor);
			oneleft[itor] = ChkOneLeft(itor);
		}
	}

	if( (mouse_b & 1) && !mousedown ){
		// undo button
		if( mouse_x > SCREEN_W - NODE_W - Undo->w && 
			mouse_x < SCREEN_W - NODE_W  &&
			mouse_y > GRID_Y+GRID_H+NODE_H/2 && 
			mouse_y < Undo->h +GRID_Y+GRID_H+NODE_H/2 )
		{
			myGrid->CopyGrid(myUndo);
		}
		// clear grid
		if( mouse_x > NODE_W && 
			mouse_x < NODE_W + Undo->w  &&
			mouse_y > GRID_Y+GRID_H+4*NODE_H/2 && 
			mouse_y < Undo->h +GRID_Y+GRID_H+4*NODE_H/2 ){

			myUndo->CopyGrid(myGrid);
			myGrid->ClearGrid();
			//clear notes
			for (itor = 0; itor < 81; itor++){
				error[itor] = false;
				search[itor] = false;
				for (itor2 = 0; itor2 <10; itor2++){
					notes[itor][itor2] = false;
				}
			}
		}
				// lock numbers
		if( mouse_x > NODE_W && 
			mouse_x < NODE_W + Undo->w  &&
			mouse_y > GRID_Y+GRID_H+NODE_H/2 && 
			mouse_y < Undo->h +GRID_Y+GRID_H+NODE_H/2 ){

			myUndo->CopyGrid(myGrid);
			for (itor = 0; itor < 81; itor++){
				if(myGrid->data[itor] < 10 && myGrid->data[itor] != 0)
					myGrid->data[itor] += 10;
			}
		}
	}
	mousedown = (mouse_b & 1);

	return;
}


/*****************************************************************
 * I am the one number option left checker. If there is only one 
 * number option left for the grid location specified I return true.
 ******************************************************************/
bool World::ChkOneLeft (int loc){
	int itor;
	int count = 0;
	bool numbers[10];

	if(myGrid->data[loc] != 0) return false;
		// clear numbers to 0
	for (itor = 0; itor < 10; itor++){
		numbers[itor] = false;
	}
	for (itor = 1; itor < 21; itor ++){
		if(0 != myGrid->data[ myDepGrid->GetVal(loc, itor)]){
			if(myGrid->data[ myDepGrid->GetVal(loc, itor)]>9){
				numbers[myGrid->data[ myDepGrid->GetVal(loc, itor)]-10] = true;
			}else{
				numbers[myGrid->data[ myDepGrid->GetVal(loc, itor)]] = true;
			}
		}
	}
	for (itor = 1; itor < 10; itor++){
		if(numbers[itor] == true) count++;
	}

	return (count == 8);
}



/*****************************************************************
 * I am the search checker function. I check the square indexed 
 * by loc to see if it could house the number stored in cursearch
 ******************************************************************/
bool World::ChkSearch (int loc){
	int itor;
		// not valid search if we are searching for 0 or 
		// the grid location is not empty
	if(cursearch == 0 || myGrid->data[loc] != 0) return false;
		// check if any of the dependant nodes have the cursearch value
	for (itor = 1; itor < 21; itor ++){
		if(	 cursearch == myGrid->data[ myDepGrid->GetVal(loc, itor)] ||
			 cursearch+10 == myGrid->data[ myDepGrid->GetVal(loc, itor)]
		) return false;
	}
	return true;
}



/*****************************************************************
 * I am the Conflict checker function. I check the square indexed 
 * by loc to see if there are any matching conflicts.
 ******************************************************************/
bool World::ChkConflict(int loc){
	int itor;
	bool numbers[10]; 
	for (itor = 0; itor < 10; itor++){
		numbers[itor] = false;
	}
	for (itor = 1; itor < 21; itor ++){
		if(	myGrid->data[loc] > 0){
			if( myGrid->data[loc] == myGrid->data[ myDepGrid->GetVal(loc, itor)] ||
				myGrid->data[loc]-10 == myGrid->data[ myDepGrid->GetVal(loc, itor)] ||
				myGrid->data[loc]+10 == myGrid->data[ myDepGrid->GetVal(loc, itor)])
					return true;
		} else {
			if(0 != myGrid->data[ myDepGrid->GetVal(loc, itor)]){
				if(myGrid->data[ myDepGrid->GetVal(loc, itor)]>9){
					numbers[myGrid->data[ myDepGrid->GetVal(loc, itor)]-10] = true;
				}else{
					numbers[myGrid->data[ myDepGrid->GetVal(loc, itor)]] = true;
				}
			}
		}
	}
	if(	myGrid->data[loc] == 0){
		if(	numbers[1] == true && numbers[2] == true && numbers[3] == true &&
			numbers[4] == true && numbers[5] == true && numbers[6] == true &&
			numbers[7] == true && numbers[8] == true && numbers[9] == true) return true;
	}
	return false;
}

/*****************************************************************
 * I am the Draw function. Depending on what the display options are
 * I draw the board to the DrawBuffer
 ******************************************************************/
void World::Draw(){
	int itor;
	clear_to_color(DrawBuffer, makecol(250, 250, 250));

		// set the background
	for (itor =0; itor < 81; itor++){
		if(error[itor] == true){
		rectfill(DrawBuffer, 
			GRID_X + ((itor%9)*NODE_W), 
			GRID_Y + ((itor/9)*NODE_H),
			GRID_X + ((1+(itor%9))*NODE_W), 
			GRID_Y + ((1+(itor/9))*NODE_H), makecol(250, 100, 100)); 
		}
		if(search[itor] == true){
		rectfill(DrawBuffer, 
			GRID_X + ((itor%9)*NODE_W), 
			GRID_Y + ((itor/9)*NODE_H),
			GRID_X + ((1+(itor%9))*NODE_W), 
			GRID_Y + ((1+(itor/9))*NODE_H), makecol(250, 250, 100)); 
		}
		if(oneleft[itor] == true){
		rectfill(DrawBuffer, 
			GRID_X + ((itor%9)*NODE_W), 
			GRID_Y + ((itor/9)*NODE_H),
			GRID_X + ((1+(itor%9))*NODE_W), 
			GRID_Y + ((1+(itor/9))*NODE_H), makecol(100, 250, 250)); 
		}
		if(oneleft[itor] == true && search[itor] == true){
		rectfill(DrawBuffer, 
			GRID_X + ((itor%9)*NODE_W), 
			GRID_Y + ((itor/9)*NODE_H),
			GRID_X + ((1+(itor%9))*NODE_W), 
			GRID_Y + ((1+(itor/9))*NODE_H), makecol(100, 250, 100)); 
		}
	}
	if(curloc >=0){
		rectfill(DrawBuffer, 
			GRID_X + ((curloc%9)*NODE_W), 
			GRID_Y + ((curloc/9)*NODE_H),
			GRID_X + ((1+(curloc%9))*NODE_W), 
			GRID_Y + ((1+(curloc/9))*NODE_H), makecol(150, 150, 250)); 
	}else {
		rectfill(DrawBuffer, 
			NODE_W*4 + GRID_X, NODE_H*10 + GRID_Y,
			NODE_W*5 + GRID_X, NODE_H*11 + GRID_Y, makecol(150, 150, 250)); 
	}


	DrawNumbers();
	DrawLines();

	DrawNumAt(NODE_W*4 + GRID_X, NODE_H*10 + GRID_Y, 10+cursearch);

	draw_sprite(DrawBuffer, Lock, NODE_W, GRID_Y+GRID_H+NODE_H/2);
	draw_sprite(DrawBuffer, Clear, NODE_W, GRID_Y+GRID_H+4*NODE_H/2);
	draw_sprite(DrawBuffer, Undo, SCREEN_W - NODE_W - Undo->w, GRID_Y+GRID_H+NODE_H/2);

	draw_sprite(DrawBuffer, arrow, mouse_x, mouse_y);
}

/*****************************************************************
 * I am the grid draw function. I use the ScreenDiv macros to render
 * the 9x9 sudoky gridlines
 ******************************************************************/
void World::DrawLines(){
	int itor;
		// draw horizontal lines
	for (itor=0; itor< 10; itor++){
		hline(DrawBuffer, GRID_X, GRID_Y+(itor*NODE_H), GRID_X+GRID_W, makecol(5,5,15)); 
		if( itor%3 == 0){
			hline(DrawBuffer, GRID_X, GRID_Y+(itor*NODE_H)+1, GRID_X+GRID_W, makecol(0,0,5));
		}

	}
		// draw verticle lines
	for (itor=0; itor< 10; itor++){
		vline(DrawBuffer, GRID_X+(itor*NODE_W), GRID_Y, GRID_Y+GRID_H, makecol(5,5,15)); 
		if( itor%3 == 0){
			vline(DrawBuffer, GRID_X+(itor*NODE_W)+1, GRID_Y, GRID_Y+GRID_H, makecol(0,0,5)); 
		}
	}


}

/*****************************************************************
 * I am the draw number function. I render the grid of numbers
 ******************************************************************/
void World::DrawNumbers(){
	int itor, itor2;

	for (itor =0; itor < 81; itor++){
		if(myGrid->data[itor] == 0){
			for(itor2 = 1; itor2 < 10; itor2++){
				if(notes[itor][itor2])
					DrawNumAt(
						GRID_X + ((itor%9)*NODE_W),
						GRID_Y + ((itor/9)*NODE_H),
						itor2+20);
			}
		}else {
			DrawNumAt(
				GRID_X + ((itor%9)*NODE_W),
				GRID_Y + ((itor/9)*NODE_H),
				myGrid->data[itor]);
		}
	}
	return;

}


/*****************************************************************
 * I am the draw number function. I render the selected number at the
 * selected spot
 ******************************************************************/
void World::DrawNumAt( int X, int Y, int val )
{
	BITMAP * font = pencil;
	if(val > 20) {
		font = note;
		val -= 20;
	}
	if(val > 10) {
		font = ink;
		val -= 10;
	}
		// exit early if empty
	if( val < 1) return;
		// blit from font to link to temp based on val
	switch (val){
		case 1:
			blit(font, temp, 0*font->w/3, 0*font->h/3, 0, 0, temp->w, temp->h);
		break;
		case 2:
			blit(font, temp, 1*font->w/3, 0*font->h/3, 0, 0, temp->w, temp->h);
		break;
		case 3:
			blit(font, temp, 2*font->w/3, 0*font->h/3, 0, 0, temp->w, temp->h);
		break;
		case 4:
			blit(font, temp, 0*font->w/3, 1*font->h/3, 0, 0, temp->w, temp->h);
		break;
		case 5:
			blit(font, temp, 1*font->w/3, 1*font->h/3, 0, 0, temp->w, temp->h);
		break;
		case 6:
			blit(font, temp, 2*font->w/3, 1*font->h/3, 0, 0, temp->w, temp->h);
		break;
		case 7:
			blit(font, temp, 0*font->w/3, 2*font->h/3, 0, 0, temp->w, temp->h);
		break;
		case 8:
			blit(font, temp, 1*font->w/3, 2*font->h/3, 0, 0, temp->w, temp->h);
		break;
		case 9:
			blit(font, temp, 2*font->w/3, 2*font->h/3, 0, 0, temp->w, temp->h);
		break;

	}
		// now send the sprite to screen
	stretch_sprite(DrawBuffer, temp, X, Y, NODE_W, NODE_H);
	return;
}

