// A two dimentional array (9x9) to contain all the values of all the cells
var vals = new Array( 9 );
for( var i = 0; i < 9; i++ ){
	vals[ i ] = new Array( 9 );
}

// A two dimentional array (9x9) to contain the references to all the cells
// Eg. cells[ 0 ][ 0 ] is htmlItem( "c_1_1" );
var cells = new Array( 9 );
for( var i = 0; i < 9; i++ ){
	cells[ i ] = new Array( 9 );
}

var current_game = ""; // The currently being played game without solution
var current_game_solution = ""; // The currently being played game with solution
var game_status = "";

var LEFT = 37;
var UP = 38;
var RIGHT = 39;
var DOWN = 40;

// Clears all the cells and enables them therefore preparing the field for a new game
function clearGrid(){
	document.sudoku_form.reset(); // Clear the fields
	for( var i = 1; i <= 9; i++ ){
		for( var j = 1; j <= 9; j++ ){
			cells[ i - 1 ][ j - 1 ].disabled = false;
			cells[ i - 1 ][ j - 1 ].style.background = "";
		}
	}
}

// Initializations ... and then start a new game.
function initialize() {
	//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, "keyup", checkCell ); // Call the checkCell() function when the user changes the value of a cell.
			addEvent( cell_ref, "keydown", navigate ); // This is used to implement Arrow Key Navigation
			cells[ i - 1 ][ j - 1 ] = cell_ref;
		}
	}
	newGame();
}

// Inserts a given value into a given cell of the grid
function insert( id, value ){
	var cell = document.getElementById( id );
	cell.style.color = "";
	cell.value = value;
	if( value ){
		cell.disabled = true; // Make it uneditable
	}else{
		cell.disabled = false; // Make the fomerly uneditable cells editable
	}
}

// Convert the given string to a position that can be displayed on the board 
// Argument : str - fingerprint
function stringToPosition( str ){
	var pos = 0;
	var x = 0, y = 0;
	var i = 0, j = 0, k = 0;
	
	for( i = 0; i < 9; i++ ){
		for( j = 0; j < 3; j++ ){
			for( k = 0; k < 3; k++ ){
				x = parseInt( i / 3 ) * 3 + j;
				y = k + ( i % 3 ) * 3;
				ch = str.charAt( pos );
				id = "c_" + ( x + 1 ) + '_' + ( y + 1 );
				//verbose( id );
				if( !isNaN( ch ) && ( 0 != Number( ch ) ) ){
					insert( id, ch );
				}else{
					insert( id, "" );
				}
				pos++;
			}
		}
	}
}

// Recreates the game afresh from the saved fingerprint of the current game
function resetGame(){
	stringToPosition( current_game );
	game_status = "Started";
	verbose( "" );
}

// Shows a solution of the game currently being played by using the previously saved game solution
function solve(){
	stringToPosition( current_game_solution );
	game_status = "Computer Solved";
	verbose( "Game Over" );
}

// Creates a new game
function newGame(){
	chosen = htmlItem( "difficulty" ).value; // Get the currently set difficulty from the combo box on page
	current_game = puzzles[ chosen ][ 0 ]; // Save the game before beginning - for 'Reset'ing the puzzle	
	current_game_solution = puzzles[ chosen ][ 1 ];
	verbose( "" );
	clearGrid();
	stringToPosition( current_game );
	game_status = "Started";
}

/*// :EVENT: Happens on the onclick event for all cells at all times.
function cellClicked(e) {
	selected_cell = findTarget(e);
}*/

// Displays certain status messages on page using a designated 'debug' text input
function verbose( str ){
	htmlItem( "debug" ).value = str;
	//htmlItem( "debug2" ).value += ( "\n" + str );
}

// Extracts the array indices of a cell from the cell id
function particularsFromId( id ){
	var box_and_cell = new Array();
	box_and_cell[ 0 ] = id.substring( id.indexOf( '_' ) + 1, id.lastIndexOf( '_' ) );
	box_and_cell[ 1 ] = id.substring( id.lastIndexOf( '_' ) + 1, id.length );
	return box_and_cell;
}

// This function is called when the user changes the value of a cell.
// :EVENT: called by the onkeyup event
function checkCell( e ){
	var cell = findTarget( e );
	var val = cell.value;
	
	if( val < 1 || val > 9 || isNaN( val ) ){ //Some error in this cell
		//verbose( cell.value );
		cell.value = "";
		return 0;
	}else{
		var box_and_cell; // Element '0' will carry the box and element '1' will carry the cell
		box_and_cell = particularsFromId( cell.id );
		result = isRepeated( box_and_cell[ 0 ],  box_and_cell[ 1 ] );
		if( false != result ){
			cell.style.background = "red"; //Repated number found here.
			htmlItem( result ).style.background = "red"; //and here.
			//result.focus();
			var box_and_cell = particularsFromId( result );
			verbose( "Number already exists at cell (" + box_and_cell[ 0 ] + ', ' + box_and_cell[ 1 ] + ')' );
			setTimeout( "removeCueColor( '" + result + "', '" + cell.id + "' )", 2000 );// Change the bg back to white after 2 secs
			cell.value = "";
			return 0;
		}
	}
	verbose( "" );
	return 1;
}

// A wrapper function for document.getElementById()
function htmlItem( id ){
	return document.getElementById( id );
}

// Adds an event listener
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;
	}
}

// Removes an event listener
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; // To defeat a Safari bug
	}
	return element;
}

// Check all the rows and columns for a repeated number
function checker() {
	if( "Computer Solved" != game_status ){	
		var err_found = 0;
		var repeated_at;
		verbose( "Checking game..." );	
		loop:
		for( var a = 0; a < 9; a++ ){
			for( var b = 0; b < 9; b++ ){
				var value = cells[ a ][ b ].value;
				if( isNaN( value ) || value < 1 || value > 9 ){
					if( value == "" ){
						verbose( "Empty cell found." );
					}else{
						verbose( "Invalid entry found." );
					}
					err_found = 1;
					cells[ a ][ b ].style.background = "red"; // Problem cell found here.
					cells[ a ][ b ].focus();
					setTimeout( "removeCueColor( '" + cells[ a ][ b ].id + "' )", 2000 ); // 2000 = 2 seconds
					break loop;
				}else if( value ){
					result = isRepeated( a + 1, b + 1 );
					if( false != result ){
						cells[ a ][ b ].style.background = "red"; // Repated number found here.
						htmlItem( result ).style.background = "red"; // and here.
						//result.focus();
						var box_and_cell = particularsFromId( result );
						verbose( "Number already exists at cell (" + box_and_cell[ 0 ] + ', ' + box_and_cell[ 1 ] + ')' );
						setTimeout( "removeCueColor( '" + result + "', '" + cells[ a ][ b ].id + "' )", 2000 ); // Cng bg to white after 2 secs
						err_found++;
						break loop;
					}
				}else{
					verbose( "Empty cells were found. Please complete the puzzle." );
					cells[ a ][ b ].style.background = "red"; // Empty
					cells[ a ][ b ].focus();
					setTimeout( "removeCueColor( '" + cells[ a ][ b ].id + "' )", 2000 ); // 2000 = 2 seconds
					found = 1;
					break loop;
				}  
			}
		}
		if( !err_found ){
			alert( "Congratulations - You have completed the puzzle." );
			verbose( "Game Over" );
		}
	}else{
		alert( "Sorry - the game has already been solved" );
		verbose( "Game Over" );
	}
}

// See if a and b are horizontal to each other.
function isHorizontal( a, b ){
	if( ( a == 1 || a == 2 || a == 3 ) && ( b == 1 || b == 2 || b == 3 ) ){
		return true;
	}
	if( ( a == 4 || a == 5 || a == 6 ) && ( b == 4 || b == 5 || b == 6 ) ){
		return true;
	}
	if( ( a == 7 || a == 8 || a == 9 ) && ( b == 7 || b == 8 || b == 9 ) ){
		return true;
	}
	return false;
}

// See if a and b are vertical to each other.
function isVertical( a, b ){
	if( ( a == 1 || a == 4 || a == 7 ) && ( b == 1 || b == 4 || b == 7 ) ){
		return true;
	}
	if( ( a == 2 || a == 5 || a == 8 ) && ( b == 2 || b == 5 || b == 8 ) ){
		return true;
	}
	if( ( a == 3 || a == 6 || a == 9 ) && ( b == 3 || b == 6 || b == 9 ) ){
		return true;
	}
	return false;
}

// This function will identify and return all boxes or cells that must be checked in contrast to a particular box or cell
function whoToCheck( for_this_item, flag ){
	var check_these = new Array();
	switch( Number( for_this_item ) ){
		case 1 :
				check_these.push( 2, 3, 4, 7 );
				break;
		case 2 :
				check_these.push( 1, 3, 5, 8 );
			   	break;
		case 3 :
				check_these.push( 1, 2, 6, 9 );
				break;
		case 4 :
				check_these.push( 1, 5, 6, 7 );
				break;
		case 5 :
				check_these.push( 2, 4, 6, 8 );
				break;
		case 6 :
				check_these.push( 3, 4, 5, 9 );
				break;
		case 7 :
				check_these.push( 1, 4, 8, 9 );
				break;
		case 8 :
				check_these.push( 2, 5, 7, 9 );
				break;
		case 9 :
				check_these.push( 3, 6, 7, 8 );
				break;
	}
	if( "cell" == flag ){
		check_these.push( for_this_item );
	}
	return check_these;
}

// Returns false if the 'number' given as the argument appears anywhere in that row, column or sub-grid.
function isRepeated( box, cell ){
	var repeated_at;
	var number = Number( htmlItem( "c_" + box + '_' + cell ).value );
	var boxes_to_check = whoToCheck( box, "box" );
	var cells_to_check = whoToCheck( cell, "cell" );
	// Check within the sub-grid
	//verbose( "--------------------------------------------------------------------------------" );
	//verbose( "Here to check for BOX " + box + " CELL " + cell );
	//verbose( "Will be checking " + boxes_to_check.length + " boxes. They are " + boxes_to_check[ 0 ] + ', ' + boxes_to_check[ 1 ] + ', ' + boxes_to_check[ 2 ] + ', ' + boxes_to_check[ 3 ] );
	for( var k = 0; k < 9; k++ ){
		if( ( number == Number( cells[ box - 1 ][ k ].value ) ) && ( k != ( cell - 1 ) ) ){ // Number found with in this sub-grid
			repeated_at = "c_" + box + '_' + ( k + 1 );
			return repeated_at;
		}
	}	
	for( var i = 0; i < boxes_to_check.length; i++ ){
		if( isHorizontal( box, boxes_to_check[ i ] ) ){
			//verbose( "Checking for horizontal box " + box + " Vs. " + boxes_to_check[ i ] );
			//verbose( "Will be checking " + cells_to_check.length + " cells. They are " + cells_to_check[ 0 ] + ', ' + cells_to_check[ 1 ] + ', ' + cells_to_check[ 2 ] + ', ' + cells_to_check[ 3 ] + ', ' + cells_to_check[ 4 ] );
			for( var j = 0; j < cells_to_check.length; j++ ){
				if( isHorizontal( cell, cells_to_check[ j ] ) ){
					//verbose( "Now H checking cell ... (" + boxes_to_check[ i ] + ',' + cells_to_check[ j ] + ') = ' + cells[ boxes_to_check[ i ] - 1 ][ cells_to_check[ j ] - 1 ].value );
					if( number == cells[ boxes_to_check[ i ] - 1 ][ cells_to_check[ j ] - 1 ].value ){
						repeated_at = "c_" + boxes_to_check[ i ] + '_' + cells_to_check[ j ]; // Get the id of the repeated cell
						return repeated_at; // There is number repeatation - return with error.
					}
				}
			}
		}else{ // Vertical
			//verbose( "Checking for vertical box " + box + " Vs. " + boxes_to_check[ i ] );
			//verbose( "Will be checking " + cells_to_check.length + " cells. They are " + cells_to_check[ 0 ] + ', ' + cells_to_check[ 1 ] + ', ' + cells_to_check[ 2 ] + ', ' + cells_to_check[ 3 ] + ', ' + cells_to_check[ 4 ] );
			for( var j = 0; j < cells_to_check.length; j++ ){
				if( isVertical( cell, cells_to_check[ j ] ) ){
					//verbose( "Now V checking cell ... (" + boxes_to_check[ i ] + ',' + cells_to_check[ j ] + ') = ' + cells[ boxes_to_check[ i ] - 1 ][ cells_to_check[ j ] - 1 ].value );
					if( number == cells[ boxes_to_check[ i ] - 1 ][ cells_to_check[ j ] - 1 ].value ){
						repeated_at = "c_" + boxes_to_check[ i ] + '_' + cells_to_check[ j ]; // Get the id of the repeated cell
						return repeated_at; // There is number repeatation - return with error.
					}
				}
			}	
		}
	}
	return false;
}

// Remove the red error cue color of cells
function removeCueColor( cell1, cell2 ){
	document.getElementById( cell1 ).style.background = "#FFFFFF";
	if( cell2 ){
		document.getElementById( cell2 ).style.background = "#FFFFFF";
	}
}

// Browser detection!
function isIe(){
	var uAgent = navigator.userAgent;
	return ( ( uAgent.indexOf( "compatible" ) > -1 ) && ( uAgent.indexOf( "MSIE" ) > -1 ) );
}

function isKonqueror(){
	var uAgent = navigator.userAgent;
	return ( ( uAgent.indexOf( "compatible" ) > -1 ) && ( uAgent.indexOf( "Konqueror" ) > -1 ) );
}

function isOpera(){
	var uAgent = navigator.userAgent;
	return ( uAgent.indexOf( "Opera" ) > -1 );
}

// Validates the entered input. Accepts only the numbers 1 to 9
function validateInput( theevent ){
	var pressedKey;
	if( ( true == isIe() ) || ( true == isOpera() ) ){
		pressedKey = Number( theevent.keyCode );
	}else{
		pressedKey = Number( theevent.charCode );
	}
	
	if( ( pressedKey >= 49 ) && ( pressedKey <= 57 ) ){
		return true;
	}else{
		return false;
	}
}

function navigate( theevent ){
	var targetElement;
	pressedKey = Number( theevent.keyCode );
	if( true == isIe() ){
		targetElement = theevent.srcElement;
	}else{
		targetElement = theevent.target;
	}
	if( ( LEFT == pressedKey ) || ( UP == pressedKey ) || ( RIGHT == pressedKey ) || ( DOWN == pressedKey ) ){
	// 37 = Left Key, 38 = Up Key, 39 = Right Key, 40 = Down Key - See defined variables up above!
		//targetElement.focus();
		moveCursor( targetElement, pressedKey );
	}
	
	return true;
}

function moveCursor( source, direction ){
	var box_and_cell = particularsFromId( source.id );
	var x = Number( box_and_cell[ 0 ] );
	var y = Number( box_and_cell[ 1 ] );
	var destination = findDestination( x, y, direction );
	//verbose( "Destination: " + destination + " Status: " + document.forms[ 0 ].elements[ destination ].disabled );
	document.forms[ 0 ].elements[ destination ].focus();
}

function findDestination( x, y, direction ){
	var newY, newX;
	switch( direction ){
		case LEFT:
			if( 0 == ( y + 2 ) % 3 ){
				newY = y + 2;
				newX = x - 1;
			}else{
				newY = y - 1;
				newX = x;
			}
			break;
		case RIGHT:
			if( 1 == ( y + 1 ) % 3 ){
				newY = 3 * ( Math.floor( ( y + 1 ) / 3 ) - 1 ) + ( ( y + 1 ) % 3 );
				newX = x + 1;
			}else{
				newY = y + 1;
				newX = x;
			}
			break;
		case UP:
			if( ( y - 3 ) <= 0 ){
				newY = y + 6;
				newX = x - 3;
			}else{
				newY = y - 3;
				newX = x;
			}
			break;
		case DOWN:
			if( ( y + 3 ) > 9 ){
				newY = y - 6;
				newX = x + 3;
			}else{
				newY = y + 3;
				newX = x;
			}
	}
	var destination = "c_" + newX + '_' + newY;
	if( ( newX < 1 ) || ( newX > 9 ) || ( true == document.forms[ 0 ].elements[ destination ].disabled ) ){
		newY = y;
		newX = x;
		destination = "c_" + newX + '_' + newY;
	}
	return destination;
}

window.onload = initialize;

