//  Original at: http://www.bin-co.com/javascript/sudoku/
// cosiderable mods by JAW
// bug: color IE and Moz orig
// var hinting = 1; // was getCookie- Now just hint s on for kids;
var nbrs_per_Box = 3; // 3=hard, 4=firm, 5=soft
var Firmness = new Array("Hard","Firm","Soft");
var repeated_at = ""; //The ID of the location where number was repeated.
var yurCooked = 0;   // You're cooked when you give up.
var selected_cell;   // The cell that was last clicked.


var xy = new Array(9);//  9x9 Array - Contains values of all cells
for(var i=0;i<9;i++) { xy[i] = new Array(9); }
// 9x9 Array - Contains references to the cells
//	- eg. cells[0][0] = $("c11"); etc. :OPTIMIZATION:
var cells = new Array(9);
for(var i=0;i<9;i++) { 	cells[i] = new Array(9);
}

// Base Games
var puzzles = new Array(
"abc.gdefihdeif.hagcbfgh.cibade.bf.iehdcg.ae.acbf.gid.hghdiace.bficgdabhefh.b.f.cg.eaidde.ahfibcg", //15
"a.bfhiec.g.ddcg.fbai.heiehdg.cabfdegi.hc.fabai.cb.df.eghhf.be.agcidbfi.edhgcac.adgf.iheb.g.hebcaf.di", //19
"bfi.edhgcac.adgf.iheb.g.hebcaf.dia.b.fhiec.g.ddcg.fbai.heiehdg.cabfdegi.hc.fabai.cb.df.eghhf.be.agcid", //20
"ib.ad.c.g.efhe.hc.fbiagd.fg.daehcibbhcgd.faiegf.ei.ab.dchdai.hcebfgfgdcai.he.bbeahd.gc.ifih.c.e.bf.gda", //21
"aeh.dfi.cgbcf.b.e.hgiad.gidc.abfeh.fc.g.b.aeihdb.eifdha.gchda.ig.cebfg.bfhd.aeic.dcegi.f.hbaah.ibce.dfg",
"dah.iegfcbbigfd.cehae.fchab.gi.dbh.fe.gacidcedi.bhgaf.ag.i.d.cfbh.egb.ch.dea.f.ihf.e.aci.d.gbid.a.fbgceh",
"cf.b.ag.eidhhd.eif.cbaga.g.ibdh.fceg.acb.ifhe.d.dehgcafb.i.ifbe.hdg.acdc.iebg.f.haegf.ahd.cibhba.ci.f.deg",
"chfgeai.d.be.gdib.hcf.ab.iacf.dh.gefa.d.hcg.ebigic.bd.eahfeb.hfa.i.gdcd.gh.bica.fe.fei.hagd.cb.a.cbdefihg",
"adbfheig.c.icfba.gdeh.he.gcidfa.bcb.ide.ah.fgag.dh.fbei.cefhigcbd.abi.he.cd.gafg.de.fbach.i.acf.g.hid.b.e",
"ied.a.bfghchcfgeia.b.dagbh.cdi.efb.ied.ca.f.ghcga.ef.hdib.fd.hbig.cae.hdie.fg.cabb.a.e.ihcf.dggfcd.b.aehi",
"h.g.bc.ea.idfa.ci.bf.dgehfe.dih.gcbab.fi.ehg.acdh.dcfabi.geag.edc.ib.fhdah.gbe.ficebf.ci.ad.hggi.ch.df.e.ab",
".e.cadbhfgi.fbhegiad.cg.di.afch.bei.h.fba.g.cd.e.badhceif.g.ec.g.dif.b.hag.ibhe.da.fc.chagifde.bfedcabi.g.h",
"ge.c.h.i.fdabaif.bg.d.echdh.bae.cf.gic.he.fbdig.a.dbageifh.c.ifgca.hb.deb.dh.efg.acicf.e.ia.bhdggia.h.c.d.ebf",
"deb.cg.a.ihff.ah.dbi.egccgi.h.fe.bda.gcdf.bihae.a.fb.hd.ec.i.geihg.acfb.dbi.ca.d.hefgge.dbc.fi.haah.f.ie.gdcb"
);

var chosen = 0;
var this_puzzle = "";   //The currently played game's fingerprint
var orginal_game = "";  //The current puzzle

////  Non Game Functions  QQ//

function $(id) { return document.getElementById(id); }

function addEvent(elm, evType, fn) {
	if (elm.addEventListener) {
		elm.addEventListener(evType, fn, false);
		return true;
	}
	else if (elm.attachEvent) {
		var r = elm.attachEvent('on' + evType, fn);
		return r;
	}
	else {
		elm['on' + evType] = fn;
	}
}
function removeEvent(elm, evType, fn) {
	if (elm.removeEventListener) {
		elm.removeEventListener(evType, fn, false);
		return true;
	}
	else if (elm.detachEvent) {
		var r = elm.detachEvent('on' + evType, fn);
		return r;
	}
	else {
		elm['on' + evType] = '';
	}
}
//Returns the target of the event.
function findTarget(e) {
	var element;
	if (!e) var e = window.event;
	if (e.target) element = e.target;
	else if (e.srcElement) element = e.srcElement;
	if (element.nodeType == 3) element = element.parentNode;// defeat Safari bug
	return element;
}



//  Co-Ordinates Functions  /////
//  Get the index from the given two x and y co-ordinates and return it.
//  - Takes 2 and 3 and returns 6.
function getIndex(x,y) {
	var index = (y*3) + x - 3;
	return index;
}

//Returns the x and y co-ordinates based on the index given as argument.
//		- Takes 6 and returns 2 and 3 as an array
function getXY(index) {
	var x=1,y=1;
	switch (index) {
		case 1: y=1; x=1; break;
		case 2: y=1; x=2; break;
		case 3: y=1; x=3; break;
		case 4: y=2; x=1; break;
		case 5: y=2; x=2; break;
		case 6: y=2; x=3; break;
		case 7: y=3; x=1; break;
		case 8: y=3; x=2; break;
		case 9: y=3; x=3; break;
	}
	return Array(x,y);
}

function rand() {         // Returns a random number between 1 and 9(inclusive)
	var number = Math.floor(Math.random()*9)+1;
	return number;
}

function uniqueRand(list) {  // Return a random number(1-9) that is not in the "list" 
	var number = rand();
	for(var a=0;a<list.length;a++) {
		if(list[a] == number) { //If the random number was found in the list,
			number = rand(); //get a new number,
			a=-1; //and start over again.
		}
	}
	return number;
}
//See if the box and the box that should be checked is Horizontal to each other.
function Horizontals(box,check_in) {
	if((box == 1 || box == 2 || box == 3) & (check_in == 1 || check_in == 2 || check_in == 3)) return true;
	if((box == 4 || box == 5 || box == 6) & (check_in == 4 || check_in == 5 || check_in == 6)) return true;
	if((box == 7 || box == 8 || box == 9) & (check_in == 7 || check_in == 8 || check_in == 9)) return true;
	return false;
}

//See if the box and the box that should be checked is verical to each other.
function isVertical(box,check_in) {
	if((box == 1 || box == 4 || box == 7) & (check_in == 1 || check_in == 4 || check_in == 7)) return true;
	if((box == 2 || box == 5 || box == 8) & (check_in == 2 || check_in == 5 || check_in == 8)) return true;
	if((box == 3 || box == 6 || box == 9) & (check_in == 3 || check_in == 6 || check_in == 9)) return true;
	return false;
}

/*
	Returns false if the 'number' given as the argument appears
	anywhere in the previous row or column.
	Full_Check is done if it is called from the hinting system
	- then all the cells must be checked instead of
	just the ones before the current cell.
*/

function checkForUnique(box,cell,full_check) {
	box ++;
	cell++;

	var number = cells[box-1][cell-1].value;

	//There is nothing before 1, so we don't have to check it - unless we are doing a full check.
	if(box>1 || full_check) {
	// All the boxes we will have to check for duplicates for this box
	// If it is NOT a full search, we just need to check the boxes directly above or left of this box.
	var boxes_to_check = new Array();
	switch (box) {
		case 1 : if(full_check) {boxes_to_check.push(2,3,4,7);}
			   	 break;
		case 2 : boxes_to_check.push(1);
			   	 if(full_check) {boxes_to_check.push(3,5,8); }
			   	 break;
		case 3 : boxes_to_check.push(1,2);
			   	 if(full_check) {boxes_to_check.push(6,9);}
				 break;
		case 4 : boxes_to_check.push(1);
			   	 if(full_check) { boxes_to_check.push(5,6,7); }
				 break;
		case 5 : boxes_to_check.push(2,4);
			   	 if(full_check) {boxes_to_check.push(6,8); }
				 break;
		case 6 : boxes_to_check.push(3,4,5);
			   	 if(full_check) { boxes_to_check.push(9); }
				 break;
		case 7 : boxes_to_check.push(1,4);
			   	 if(full_check) {boxes_to_check.push(8,9); }
				 break;
		case 8 : boxes_to_check.push(2,5,7);
			   	 if(full_check) {boxes_to_check.push(9); }
				 break;
		case 9 : boxes_to_check.push(3,6,7,8);break;
	}

	var value="",xy_coods,cell_to_check;
	next_box:
	for(var i=0;i<boxes_to_check.length;i++) {
		xy_coods = getXY(cell);
		var check_box = boxes_to_check[i];
		for(var j=1; j<=3; j++) {
			//Horizontal Checks
			if(Horizontals(box,check_box)) {
				//Non-full checks will check only the cells before them - will not check all the cells.
				if(!full_check) {
					if(box==1 || box==4 || box==7) continue next_box;
					else if(box==5 && check_box==6) continue next_box;
					else if(box==8 && check_box==9) continue next_box;
				}
				//Check the data for redundency here.
				cell_to_check = getIndex(j,xy_coods[1]);
				value = cells[(check_box-1)][cell_to_check-1].value;

				if(value == number) {
					repeated_at = "c" + check_box + cell_to_check;//Get the id of the cells that matched
					return false; //There is number repeatation - return with error.
				}

			//Verical Checks
			} else {
				if(!full_check) {
					if(box<4) continue;
					else if(box==4 && check_box==7) continue next_box;
					else if(box==5 && check_box==8) continue next_box;
					else if(box==6 && check_box==9) continue next_box;
				}
 				//Check the data for redundency here.
 				cell_to_check = getIndex(xy_coods[0],j);
 				value = cells[check_box-1][cell_to_check-1].value;

 				if(value == number) {
 					repeated_at = "c" + check_box + cell_to_check;//Get the id of the cells that matched
 					return false; //There is number repeatation - return with error.
 				}
			}
		}
	}
	}

	//Check within the box
	for(var k=0;k<9;k++) {
		var val = cells[box-1][k].value;
		if(number == val && (k+1 != cell)) { //If the nubmer is found at some other cell
			repeated_at = "c"+box+(k+1);
			return false;
		}
	}

	return true;
}

//Insert the value given as the 'value' argument into the entry by the id of 'id'
function insert(id,value) {
	var ele = document.getElementById(id);
	ele.style.color="black"; //This is the input color (always black?)
	ele.value = value
	if(value)
		ele.disabled = true; //Make it uneditable
	else
		ele.disabled = false; //Make the fomerly uneditable cells editable
}

//Clear all the fields - and enable all the disabled cells
function clearer() {
	var id = ""
	document.mesa.reset(); //Clear the fields
	for(var i=0; i<9; i++) {
		for(var j=0; j<9; j++) {
			cells[i][j].disabled = false;
		}
	}

}
//Change the background color of cells to white
function discolorCells(cell1,cell2) {
	document.getElementById(cell1).style.background = "#fff";
	if(cell2) document.getElementById(cell2).style.background = "#fff";
}

//Records the values of all box and cells by copying all values to the 'xy' array.
function recordPosition() {
	var value=0;
	for(var a=0;a<9;a++) {
		for(var b=0;b<9;b++) {
			value = cells[a][b].value;
			if(!isNaN(value) && value.length==1) {
				xy[a][b] = value;
			} else {
				xy[a][b] = 0;
			}
		}
	}
}

/* Convert the current position to a string that we can save and use later on. This function will create a finger print for this game and will save that as a Cookie. The finger print will have two types of data...
	Number(1-9) - The number for that cell. If the number is 3, a 3 digit will be entered there.
	Aphabet(a-z)- The number of empty cells that must be left before the next number is inserted.
Argument :  Action
	- 1 = Record the game to a database.
	- 0 = Just get the fingerprint of the game and return it
*/

function pos2str(action) {
	var str = "";
	var zero_count = 0;
	var alpha = " abcdefghijklmnopqrstuvwxyz";

	if(action) {
		recordPosition(); //Get the position in the array
	}

	for(var a=0;a<9;a++) {
		for(var b=0;b<9;b++) {
			if(xy[a][b]) {
				if(zero_count) {
					str += alpha.charAt(zero_count); //Include the number of continous zeros - a means 1 zero, b means 2 etc.
					zero_count = 0;
				}
				str += xy[a][b];
			} else {
				zero_count++; //Count the empty places
			}
		}
	}

	return str;
}

//Convert the given string to a position that can be displayed on the board
//Argument : str 	- FINGERPRINT = Use this fingerprint to create the game.
function str2pos(str) {
	var zero_flag = 0;
	var alpha = "abcdefghijklmnopqrstuvwxyz";
	var pos = 0;
	//The id of the current cell will be calculated from these variables.
	var a = 0;
	var b = 0;
	str = str.replace(/\./g,"");

	while(a<9) {
		ch = str.charAt(pos);
		id = "c" + (a+1) + (b+1);

		if(!isNaN(ch) && !zero_flag) {
			insert(id,ch);
		} else if(zero_flag) {
			insert(id,"");
			zero_flag--;
		} else {
			insert(id,"");
			zero_flag = alpha.indexOf(ch);
		}

		//Get next number if the zero flag is not up
		if(!zero_flag) {
			pos++;
		}

		//Update the positions
		b++;
		if(b>=9) {
			b=0;
			a++;
		}
	}
}

function checkCell(e) {
	var cell = findTarget(e);
	var val = cell.value;
	if(val<1 || val>9 || isNaN(val)) {    //Some error in this cell
		cell.value="";
		tagProblemCell(cell,1);
		return 0;
	}
	else {
		var id = cell.id;
		var a =  Number(id.charAt(1)) - 1;
		var b =  Number(id.charAt(2)) - 1;

		if(!checkForUnique(a,b,true)) {
			cell.style.background = "orange"; //  Repeated number found here.
			$(repeated_at).style.background = "orange"; //  and here.
			setTimeout("discolorCells('"+repeated_at+"','"+id+"')",2000);
			//Change the background back to white after 2 secs
			return 0;
		}
	}
	tagProblemCell(cell,0);
	return 1;
}
function makeProblemCell(id) {
	$(id).className += " problem-cell";
}
//Tags the given cell as having problem or not.
function tagProblemCell(cell,on) {
	var current_classes = cell.className.toString();
	var have_problem = 0;
	if(current_classes.indexOf("problem-cell") >= 0) have_problem = 1;
	if(on) {
		if(!have_problem) cell.className += " problem-cell";
	} else {
		if(have_problem) cell.className = current_classes.replace(" problem-cell","");
	}
}


function checker() {     //Check all the Rows/Cols for a repeated number
	var found = 0;
	loop:	
	//  show("Checking game...","#f80");
	for(var a=0;a<9;a++) {
		for(var b=0;b<9;b++) {
			var ele = cells[a][b];
			var id = "c" + (a+1) + (b+1);
			var value = ele.value
			if(isNaN(value) || value<1 || value>9) {
			
			/*	if(value == "") {
					show("Empty cells found.","aqua");
				} else {
					show("Invalid entries found.","aqua");
				}*/
				found = 1;
				alert("Ooops. Problem cells  were found.");
				ele.style.background = "red"; //Problem Cells found here.
				setTimeout("discolorCells('"+id+"')",2000);
				break loop;
			}
			else if(value) {
				if(!checkForUnique(a,b,false)) {
					alert("Ooops. Repeated numbers were found.");
					//setTimeout("discolorCells('"+repeated_at+"','"+id+"')",2000);//Change background back to white after 2 secs
					found++;
					break loop;
				}
			} else {
			alert("Empty cells were found.");
				ele.style.background = "red"; //Empty
				setTimeout("discolorCells('"+id+"')",2000);
				found = 1;
				break loop;
			}
		}
	}

	if(!found) {
		if(yurCooked)
			alert("Sorry - You can't win after giving up.");
		else
			alert("CONGRATULATIONS \n- You HAVE completed the puzzle.");
	}
}

//Clear all the unwanted cells.
function reloadGame() {
	str2pos(orginal_game); //Use that to rebuild the game
}

function solve() {	//Solve the game
	if(confirm("This will automaticaly solve the puzzle for you.\nAre you sure you want to do this?\n")) {
		str2pos(this_puzzle);
		yurCooked = 1;
	}
}

// :EVENT: Happens on the onclick event for all cells at all times.
function cellClicked(e) {	selected_cell = findTarget(e);	}

function revealCell() {
	if(!selected_cell) return;

	var str = this_puzzle.replace(/\./g,"");//Remove the '.' in the active puzzle.
	var c_xy = selected_cell.id.split(""); //The id will be split - if the id is 'c13' the resulting array will be c,1,3
	var box = c_xy[1]-1;
	var cell= c_xy[2]-1;
	var rest = str.substr(box*9);//Remove the numbers of all the boxes before it.
	var number = rest.charAt(cell); //Now get the cell.
	
	selected_cell.value = number;
}

//This will make new puzzles by replacing all numbers in a base game with other numbers 
// - in effect creating an entirely new game. This function is sneaky?
function makeNewOrder(str) {	         //Inits
	var alpha = " abcdefghijklmnopqrstuvwxyz";
	var new_order = "";	//Get random numbers for all alphas - and store it in a array.
	numbers = new Array("0");
	for(j=0;j<9;j++) {
		new_numbers = uniqueRand(numbers); //Give random position for the numbers
		numbers.push(new_numbers);
	}

for(i=0;i<str.length;i++) {	//Change alphas back to numbers - new digits
	if(str.charAt(i)=="." || str.charAt(i)=="*" || str.charAt(i)=="x" ||
		str.charAt(i)=="_" || str.charAt(i)=="-" || str.charAt(i)=="+") { //It is a special char
		new_order += "."
	}
	else {	new_order += numbers[alpha.indexOf(str.charAt(i))]}
	}
	return new_order;
}

function newGame() {      //Creates the puzzle!
	chosen = Math.floor((Math.random()*10) / (10/puzzles.length)); 
	//'chosen' puzzle should be random
	this_puzzle = makeNewOrder(puzzles[chosen]);
	// show("","");    // Clear the display.(make it null )
	yurCooked = 0; //You are not cooked yet.
	clearer();

	var last_box_ended_at = 0;
	var extra_number_count = 0;
	var x=Math.floor((Math.random()*3));
	
	var nbrs_per_Box = x+3            //$("difficulty").value;
	document.topBtns.Diff.value = Firmness[x];
	for(var i=0;i<9;i++) { 	//Initialisations
		var b=0,location_of_fixed_number=0;
		var arr_b = new Array();

		//Get the numbers for this box from the 'this_puzzle' varaible
		var limit = 9;
		var this_box = "";
		var dot_count = 0;
		for(var j=last_box_ended_at; j<last_box_ended_at+limit; j++) {
			if(this_puzzle.charAt(j) == ".") {
				limit++;
				dot_count++;
			}
			this_box = this_box + this_puzzle.charAt(j);
		}
		last_box_ended_at = last_box_ended_at + limit;

		//Decide how many numbers must appear in this box
		number_of_numbers = nbrs_per_Box - extra_number_count;
		extra_number_count = 0;
		// 'extra_number_count' is used for reducing the number 
		// of populated cells in the next box if
		//	the current box has more than 4 numbers.
		//  If the number of dots are more than 4, use it
		if (dot_count > number_of_numbers) {
			if(rand() > 5) {
				extra_number_count = dot_count - number_of_numbers;
			}
			number_of_numbers = dot_count;
		}

		arr_b = new Array();		//Empty the array.
		//Get the positions in the box and insert the numbers there
		for(b=0;b<number_of_numbers;b++) {
			location_of_fixed_number = this_box.indexOf(".");

			if(location_of_fixed_number + 1) {//If there are dots...
				this_box =  this_box.substring(0,location_of_fixed_number) +
							this_box.substring(location_of_fixed_number+1,this_box.length);

			} else { //No more dots - get some random locations
				location_of_fixed_number = uniqueRand(arr_b); //Give random position for the numbers
				location_of_fixed_number--;//uniqueRand gives 1-9. We need it from 0
			}

			arr_b.push(location_of_fixed_number+1);//Put the number into the don't repeat array

			//Get the numbers that should be inserted
			insertion_number = this_box.charAt(location_of_fixed_number);
			location_of_fixed_number++;//We need this to start from 1 - not from 0
			id = "c" + (i+1) + location_of_fixed_number
			insert(id,insertion_number); //Show the numbers
		}
	}
	orginal_game = pos2str(2);  // Save the game before beginning - for restarting the puzzle

}

function init() {
	//First get the references of all the cells
	var cell_ref;
	for(var i=1;i<=9;i++) {
		for(var j=1;j<=9;j++) {
			cell_ref = document.getElementById("c"+i+j);
			addEvent(cell_ref,"change",checkCell);//Call the checkCell() function when the user changes the value of a cell.
			addEvent(cell_ref,"click",cellClicked);
			cells[i-1][j-1] = cell_ref;// :OPTIMIZATION:
		}
	}
	newGame();
}

window.onload=init;

