/*<script>*/

//////////////////////////////////// Global Variables /////////////////////////////////////////
var debug = 0;

var horizondal_line_width = 350;
var vertical_line_height = 370;
var default_number_per_box = 4;
var repeated_at = ""; //The ID of the location where number was repeated.
var compleated = 0;   //This will be one when the User hits the 'Quit' Button

//Create a 9x9 2 dimentional Array
var xy = new Array(9);
for(i=0;i<9;i++) {
	xy[i] = new Array(9);
}

//Base Games
var puzzles = new Array(
"abc.gdefihdeif.hagcbfgh.cibadebf.iehdcg.ae.acbf.gid.hghdiace.bficgdabhefh.b.f.cg.eaidde.ahfibcg", //14
"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

//////////////////////////////////// Cookie Functions ////////////////////////////////
var bites = document.cookie.split(";");   // This is to break cookie down into an array of bites
var sudoku_fingerprint = getCookie("sudoku_fingerprint"); 	  // retrieve all the values
if (sudoku_fingerprint == null || sudoku_fingerprint == "") sudoku_fingerprint=""; //or define default values
function getCookie(cookie) {
	for (i=0; i < bites.length; i++) {
		nextbite = bites[i].split("=");         // break into the "name & value"
		if (nextbite[0] == cookie)              // if the name = true
			return unescape(nextbite[1]);       // return this value
	}
	return null;                             // if there is no match return null value
}
var today = new Date();
var expiry = new Date(today.getTime() + 28 * 24 * 60 * 60 * 1000); // 28 days

// Set cookie with given name = value pair
function setCookie(name,value) {
	if((value!=null) && (value!="")) document.cookie = name + "=" + escape(value) + "; expires=" + expiry.toGMTString();
	bites = document.cookie.split(";"); 	  // update all the bites
}

//////////////////////////////////// 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 = (x*3) + y - 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: x=1; y=1; break;
		case 2: x=1; y=2; break;
		case 3: x=1; y=3; break;
		case 4: x=2; y=1; break;
		case 5: x=2; y=2; break;
		case 6: x=2; y=3; break;
		case 7: x=3; y=1; break;
		case 8: x=3; y=2; break;
		case 9: x=3; y=3; break;
	}
	var xy_coods = new Array(x,y);
	return xy_coods;
}

////////////////////////////////////// Random Number Genarators //////////////////////////////////////
//Returns a random number between 1 and 9(inclusive)
function rand() {
	var number = Math.round(Math.random()*10);
	while (number < 1 || number > 9) { //If the number is 0 or 10, get another number.
		number = Math.round(Math.random()*10);
	}
	return number; 
}

//Returns a random number(1-9) that is not in the list given as the argument
function uniqueRand(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;
}

/////////////////////////////////////// GUI Functions ////////////////////////////////////
//Display the given data in the said colour 
function show(data,color) {
	document.getElementById("display_area").innerHTML = data + "<br />\n";
	document.getElementById("display_area").style.color = color;
}

//Display the Vertical help line
function helpLineV(line) {
	var item = document.getElementById("lv-"+line).style;
	if(item.position == "absolute") {
		item.position = "relative";
		item.height = "10px";
	} else {   
		item.position = "absolute";
		item.height = vertical_line_height + "px";
	}
}
//Display the Horizondal Help line
function helpLineH(line) {
	var item = document.getElementById("lh-"+line).style;
	if(item.position == "absolute") {
		item.position = "relative";
		item.width = "5px";
	} else {
		item.position = "absolute";
		item.width = horizondal_line_width + "px";
	}
}

//Returns false if the 'number' given as the argument appears anywhere in the previous row or column.
function checkForUnique(box,cell,number) {
	//There is nothing before 1, so we don't have to check it.
	if(box>1) {
	//All the boxes we will have to check for duplicates for this box - all boxes directly above or left of this box.
	var boxes_to_check = new Array(); 
	switch (box) {
		case 2 : boxes_to_check.push(1);break;
		case 3 : boxes_to_check.push(1,2);break;
		case 4 : boxes_to_check.push(1);break;
		case 5 : boxes_to_check.push(2,4);break;
		case 6 : boxes_to_check.push(3,4,5);break;
		case 7 : boxes_to_check.push(1,4);break;
		case 8 : boxes_to_check.push(2,5,7);break;
		case 9 : boxes_to_check.push(3,6,7,8);break;
	}

	var id="", value="";
	for(i=0;i<boxes_to_check.length;i++) {
		xy_coods = getXY(cell);
		for(j=1; j<=3; j++) {
			//Horizondal Checks
			if(box==2 || box==3 || box==5 || box==6 || box==8 || box==9) { 
				//Nullifier conditions - don't do the checking if the following conditions are true.
				if(box==5 && boxes_to_check[i]==2);
				else if(box==6 && boxes_to_check[i]==3);
				else if(box==8 && (boxes_to_check[i]==2 || boxes_to_check[i]==5));
				else if(box==9 && (boxes_to_check[i]==3 || boxes_to_check[i]==6));
				else {
					//Check the data for redundency here.
					cell_to_check = getIndex(xy_coods[0],j);
					id = "c" + boxes_to_check[i] + cell_to_check; //Get the id of the cells that must be checked.
					value = document.getElementById(id).value;

					if(value == number) {
						repeated_at = id;
						return false; //There is number repeatation - return with error.
					}
				}
			}

			//Verical Checks
			if(box>=4) {
				//Nullifier conditions - don't do the checking if the following conditions are true.
				if(box==5 && (boxes_to_check[i]==4)); //The 5th box should only check the 2nd Box in vertical mode
				else if(box==6 && (boxes_to_check[i]==4 || boxes_to_check[i]==5));
				else if(box==8 && boxes_to_check[i]==7);
				else if(box==9 && (boxes_to_check[i]==7 || boxes_to_check[i]==8));
				else {
					cell_to_check = getIndex(j,xy_coods[1]);
					id = "c" + boxes_to_check[i] + cell_to_check; //Get the id of the cells that must be checked.
					value = document.getElementById(id).value;

					if(value == number) {
						repeated_at = id;
						return false; //There is number repeatation - return with error.
					}
				}
			}
		}
	}
	}
	
	//Check within the box
	for(k=1;k<=9;k++) {
		id = "c" + box + k;
		if(number == document.getElementById(id).value && k != cell) { //If the nubmer is found at some other cell
			repeated_at = id;
			return false;
		}
	}

	return true;
}

//Insert the value given as the 'value' argument into the entry by the id of 'id'
function insert(id,value) {
	document.getElementById(id).value = value
	if(value)
		document.getElementById(id).disabled = true; //Make it uneditable
	else 
		document.getElementById(id).disabled = false; //Make the fomerly uneditable cells editable
}

//Clear all the fields - and enable all the disabled cells
function clearer() {
	var id = ""
	document.f.reset(); //Clear the fields
	for(var i=1; i<=9; i++) {
		for(var j=1; j<=9; j++) {
			id = "c"+i+j;
			document.getElementById(id).disabled = false;
		}
	}
}
//Change the background color of cells to white
function discolorCells(cell1,cell2) {
	document.getElementById(cell1).style.background = "#fff";
	document.getElementById(cell2).style.background = "#fff";
}

//Records the values of all box and cells by coping all values to the 'xy' array.
function recordPosition() {
	var value=0;
	for(a=0;a<9;a++) {
		for(b=0;b<9;b++) {
			value = document.getElementById("c"+(a+1)+(b+1)).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 latter 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(a=0;a<9;a++) {
		for(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 form these varabales. 
	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++;
		}
	}
}

//Create a string we will put into the cookie - a little different for the pos2str()
//		- This format will have the values of all cells sperated by a ';' char. The provided 
//			numbers will be prefixed with a dot like - '.5'
function makeCookieString() {
	var str = "";
	var puzzle_cells = new Array(0);

	for(a=0;a<9;a++) {
		for(b=0;b<9;b++) {
			value = document.getElementById("c"+(a+1)+(b+1)).value;
			if(document.getElementById("c"+(a+1)+(b+1)).disabled) {
				value = "."+value;
			}
			puzzle_cells.push(value);
		}
	}
	str = puzzle_cells.join(';');
	//alert(str);
	alert("Game Saved");

	return str;
}

//Save the Cookie Format string created with the makeCookieString() function to a cookie
function save() {
	show("Saving Game...");
	var str = makeCookieString();
	setCookie("sudoku_fingerprint",str);
	show("Game Saved...");
}
//Load the game from the cookie string
function load() {
	show("Loading Saved Game...");
	//Clear the previous data
	clearer();

	//Get the game fingerprint from the Cookie.
	var str = getCookie("sudoku_fingerprint");
	if(str == "" || str == null) {
		alert("No saved games found!")
		return false;
	}

	var index = 0;
	var puzzle_cells = str.split(";");
	for(a=0;a<9;a++) {
		for(b=0;b<9;b++) {
			id = "c"+(a+1)+(b+1);//The cell location

			number = puzzle_cells[index];
			if(number.charAt(0) == ".") { //If there is a '.' char, it ia a provided number
				number = number.charAt(1);
				document.getElementById(id).disabled = true;
			}

			if(number) {
				//alert(id + " = " + number);
			}

			document.getElementById(id).value = number;
			index++;
		}
	}
	show("");
}

//Check all the Rows/Cols for a repeated number
function checker() {
	var found = 0;
	
	show("Checking game...","#a84efa");
	loop:
	for(a=1;a<=9;a++) {
		for(b=1;b<=9;b++) {
			id = "c"+a+b;
			value = document.getElementById(id).value
			if(isNaN(value) || value > 9 || value < 1) {
				if(value == "") {
					show("Found Empty cells","#d78601");
					alert("Found Empty cells");

				} else {
					show("Found Invalid entries","#d78601");
					alert("Found Invalid entries");
				}
				found = 1;

				document.getElementById(id).style.background = "red"; //Repated number found here.
				setTimeout("discolorCells(id,id)",2000);
				
				break loop;
			}
			else if(value) {
				if(!checkForUnique(a,b,value)) {
					document.getElementById(id).style.background = "red"; //Repated number found here.
					document.getElementById(repeated_at).style.background = "red"; //and here.

					setTimeout("discolorCells(repeated_at,id)",2000);//Change the background back to white after 2 secs
					found++;
					break loop;
				}
			} else {
				show("Empty cells were found. Please complete the puzzle.","#d78601");
				document.getElementById(id).style.background = "red"; //Empty
				setTimeout("discolorCells(id,id)",2000);				
				found = 1;
				break loop;
			}  
		}
	}

	if(!found) {
		if(compleated)
			alert("Sorry - you can't win after giving up. But it is solved.");
		else
			alert("Congratulations - You have completed the puzzle.");
		show("Game Over","#000000"); 
	}
}

//Clear all the unwanted cells.
function reloadGame() {
	str2pos(orginal_game); //Use that to rebuild the game
}

//Solve the game
function solve() {
	show("Finding the solution to the game...");
	if(confirm("This will automaticaly solve the puzzle for you.\nAre you sure you want to do this?\n")) {
		str2pos(this_puzzle);
		compleated = 1;
		show("Puzzle Solved");
	}
}

//This will make new puzzles but replacing all numbers in a base game with other numbers - in effect creating
//	a entirely new game. This function is sneaky - Big Time
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);
	}

	//Now change all the alphas back to numbers - with new digits
	for(i=0;i<str.length;i++) {
		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;	
}

//Creates the puzzle
function init() {
	show("");
	chosen = Math.floor((Math.random()*10) / (10/puzzles.length)); //'chosen' puzzle should be random
	chosen = 2;
	this_puzzle = makeNewOrder(puzzles[chosen]);
	
	//Clear the existing numbers first
	compleated = 0;
	clearer();

	var last_box_ended_at = 0;
	var extra_number_count = 0;
	for(var i=0;i<9;i++) { //Change 1 to 9 :DEBUG:
		//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 much numbers must appear in this box
		number_of_numbers = default_number_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;
		}
		

		//Empty the array.
		arr_b = new 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...
				//Remove this dot
				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
}

