/*
 * file:	free_games.js
 * authors:	Ivan Wills

 * API:

 *	events:
 *				over_number		-
 *				out_number		-
 *				click_board		-
 *				enter_entry		-
 *				exit_entry		-
 *				type_entry		-
 *				select_game		-
 *				quick_pick		-
 *				clear_picks		-
 *				signup_now		-
 *	other functions:
 *				set_current_entry	- sets the number into the current entry position
 *				toggle_board_number	- toggles a number on the board
 *				set_board_number	- sets a number on the board
 *				clear_board_number	- clears the number off the board
 *				clear_board			- clears the board of all set numbers
 *				setup_board			- sets up the board baised on the current game
 *				get_position		- gets the position of a number
 *				has_position		- checks if a number already has a position
 *				set_next_position	- moves the current position to the next available position
 *				is_board_set		- checks if the board number is set
 *				update_position		- focuses and selects the current position
 *				validate_game		- returns true if the game is valid
 *				validate_nums		- returns true if all ordinary numbers are valid
 *				validate_sups		- returns true if all supplementry nunbers are valid
 *				validate_games		- returns ture if all games are completed correctly
 *				is_valid_number		- throws an error if the number passed is invalid
 *				is_valid_type		- throws an error if the type is unknown
 *				is_valid_game		- throw an error if the game passed is not valid
 *				debug				- displays messages if DEBUG is true
 *
 *	globals:
 *		CONSTANTS:
 *				MAX['num']		* contains the maximum number of selectable numbers
 *				MAX['sup']		* the number of supplementry numbers that can be chosen
 *				MAX.NUMBERS		* contains the largest selectable number
 *				MAX.GAMES		* the number of games that can be plaied
 *				SUP_STYLE		-
 *				NUM_STYLE		-
 *				SUP_TYPE		-
 *				NUM_TYPE		-
 *
 *
 *		Variables:
 *				current_game	- the current game being palied
 *				entry_typed		- flags that the user has typed into the current entry
 *				board			- stores all the board <td>'s for quick access
 *				game			- stores all the game entry <input>s
 *				entry			- stores information about weather an entry has been altered and the original value
 *				hover			- stores information about which element currently has the cursor over it
 *				current			* Stores the current values of various elements (position,game)
 *
 *
 *	* These are should be defined before this file is called
 *
 */

// set up the global variables
var board = new Array();
var game  = new Array();
var entry = { 'typed': 0, 'original': '' };
var hover = { 'number': 0, 'type': false };
var point;

current.num      = { 'pos': 0 };
current.sup      = { 'pos': 0 };
current.game_num = current.game < 10 ? '0' + current.game : current.game;
current.type     = 'num';
board.num = new Array();
board.sup = new Array();
game.num  = new Array();
game.sup  = new Array();

/**
 *	Sets the global parameters board_numbers, board_supps and game_numbers
 */
function set_params() {
	if ( !current.game ) {
		current.game = 1;
		current.game_num     = '01';
	}
	for ( var i = 1; i <= MAX.NUMBERS; i++ ) {
		board.num[i] = document.getElementById('game'+i);
	}
	for ( var i = 1; i <= MAX.NUMBERS; i++ ) {
		board.sup[i] = document.getElementById('supp'+i);
	}
	for ( var i = 1; i <= MAX.GAMES; i++ ) {
		var game_num = i < 10 ? '0'+i : i;
		game.num[i] = new Array();
		for ( var j = 0; j < MAX['num']; j++ ) {
			game.num[i][j] = document.getElementById('game_'+game_num+'_'+j);
		}
	}
	for ( var i = 1; i <= MAX.GAMES; i++ ) {
		var game_num = i < 10 ? '0'+i : i;
		game.sup[i] = new Array();
		for ( var j = 0; j < MAX['sup']; j++ ) {
			game.sup[i][j] = document.getElementById('supp_'+game_num+'_'+j);
		}
	}
	update_position();
}

/******    USER EVENTS   *****/

/**
 *	@param	number:	The number the mouse is over
 *	@param	type:	The board type
 *	@return
 *	@todo
 *	@bug
 *
 *
 */
function over_number( number, type ) {
	is_valid_number(number);
	is_valid_type(type);
	var check_lab = type == NUM_TYPE ? 'check' : 'check_' + type;
	var class_lab = type == NUM_TYPE ? NUM_STYLE  : SUP_STYLE;
	var square    = board[type][number];
	var hover     = is_board_set(number, type) ? ' selected hover ' : ' hover '

	square.className = class_lab + hover;
	hover.number = number;
	hover.type  = type;
	return true;
}

/**
 *	@param	number:	The number the mouse was over
 *	@param	type:	The board type
 *	@return
 *	@todo
 *	@bug
 *
 *
 */
function out_number( number, type ) {
	is_valid_number(number);
	is_valid_type(type);
	var check_lab = type == NUM_TYPE ? 'check' : 'check_' + type;
	var class_lab = type == NUM_TYPE ? NUM_STYLE  : SUP_STYLE;
	var square    = board[type][number];
	var unhover   = is_board_set(number, type) ? ' selected' : ''

	square.className = class_lab + unhover;
	hover.number = '';
	hover.type  = '';
	return true;
}

/**
 *	@param	number:	The number that has been clicked
 *	@param	type:	The board type
 *	@return
 *	@todo
 *	@bug
 *
 *
 */
function click_board( number, type ) {
	is_valid_number(number);
	is_valid_type(type);
	get_position( number, type );
	var square  = board[type][number];
	var numbers = game[type][current.game][current[type].pos];

	// check if the current number is set else where on the board and clear it if is it set and not the current number
	if ( numbers.value && numbers.value != number && is_board_set( numbers.value, type ) )
		clear_board_number( numbers.value, type );
	set_current_entry( toggle_board_number(number, type), type );
	if ( !validate_games() ) {
		if ( validate_game() )
			setTimeout('select_game( '+current.game+' + 1 );', 10 ); // IE scheduling issue
		else if ( validate_nums() )
			set_next_position(SUP_TYPE);
		else if ( validate_sups() )
			set_next_position(NUM_TYPE);
	}
}

/**
 *	@param	game_no:	The game the entry is part of
 *	@param	position:	The game entry position
 *	@param	type:	The poistion type
 *	@return
 *	@todo
 *	@bug
 *
 *
 */
function enter_entry( game_no, position, type ) {
	entry.typed       = false;
	game_no          *= 1;
	current[type].pos = position;
	current.type      = type;

	if ( current.game != game_no )
		select_game(game_no);

	if ( game[type][game_no][position].value )
		entry.original = game[type][game_no][position].value * 1;
	entry.typed    = false;
	current.type = type;
}

/**
 *	@param	game_no:	The game the entry is part of
 *	@param	position:	The game entry position
 *	@param	type:	The poistion type
 *	@return
 *	@todo
 *	@bug
 *
 *	Checks if the game entry position we are exiting has been typed into
 *	by the user. If it has we clear the previous value from the board (if it
 *	existed) and enter
 */
function exit_entry( game_no, position, type ) {
	if ( entry.typed ) {
		var value  = game[type][game_no][position].value;
		var number = value * 1;
		if ( value+'' == number+'' && number != entry.original ) {
			// Check that the number is valid
			if ( number < 1 || MAX.NUMBERS < number ) {
				game[type][game_no][position].value = value = '';
				number = 0;
			}
			if ( entry.original )
				clear_board_number( entry.original, type );
			if ( number ) {
				set_board_number( number, type );
				// check if the number is entered else where
				var found = has_position( number, type );
				debug('game = '+game_no+', pos = '+position+', original value = '+entry.original+', found = '+found);
				if ( found >= 0 && found != position )
					game[type][game_no][found].value = '';
			}
		}
		entry.typed = false;
	}
	entry.original = '';
}

/**
 *	@param	game_no:	The game the entry is part of
 *	@param	position:	The game entry position
 *	@param	type:	The poistion type
 *	@return
 *	@todo
 *	@bug
 *
 *	Flags that the current position is being typed into
 */
function type_entry( game_no, position, type ) {
	entry.typed = 1;
}

/**
 *	@param	game_no:	the game to select
 *	@return
 *	@todo
 *	@bug
 *
 *	Changes the current game to game_no
 */
function select_game( game_no ) {
	current.game = game_no;

	if ( current.game < 1 || current.game > MAX.GAMES ) {
		current.game = 1;
	}
	current.game_num = current.game < 10 ? '0'+current.game : current.game;

	document.getElementById('radgame'+current.game_num).checked = true;
	clear_board();
	setup_board();
	update_position();
}

/**
 *	@todo
 *	@bug
 *
 *	Makes random entry selections for the current game then moves onto the next game
 */
function quick_pick() {
	// Note: We don't know anything about pool id here so just do simple quickpick
	var picker = new OzLotteries.QuickPick(MAX.NUMBERS);
	var list = picker.quickpick_sorted(MAX['num']);
	clear_picks();
	for ( var i = 0; i < list.length; i++ ) {
		get_position( list[i], NUM_TYPE );
		set_current_entry( list[i], NUM_TYPE );
	}
	list = picker.quickpick_sorted(MAX['sup']);
	for ( var i = 0; i < list.length; i++ ) {
		get_position( list[i], NUM_TYPE );
		set_current_entry( list[i], SUP_TYPE );
	}
	setTimeout('select_game( '+current.game+' + 1 );', 10 ); // IE scheduling issue
}

/**
 *	@todo
 *	@bug
 *
 *	Clears the board and the entry positions for the current game
 */
function clear_picks() {
	clear_board();
	for ( var i = 0; i < game.num[current.game].length; i++ )
		game.num[current.game][i].value = '';
	if (game.sup.length > 0) {
		for ( var i = 0; i < game.sup[current.game].length; i++ )
			game.sup[current.game][i].value = '';
	}

	current.num.pos  = 0;
	if (game.sup.length > 0) {
		current.sup.pos = 0;
	}
}

/**
 *	@return	bool	true if all games validate false other wise
 *	@todo
 *	@bug
 *
 *	Checks that all games are valid other wise allerts the user if there are
 *	invalid/unfinished games and cancels the submit.
 */
function signup_now() {
	if ( validate_games() )
		return true;
	alert('You still have incomplete games!');
	return false;
}


/* *** Helper Functions *** */

/**
 *	@param	number:	The number the mouse was over
 *	@param	type:	The board type
 *	@return
 *	@todo
 *	@bug
 *
 *	Enters number into the current.game type position
 */
function set_current_entry( number, type ) {
	try {
		is_valid_number(number);
		is_valid_type(type);
	}
	catch (e) { throw 'set_current_entry ' + e; }
	var numbers =  game[type][current.game][current[type].pos];
	if (numbers.value != number)
		numbers.value = number;
	else
		numbers.value = '';
	set_next_position(type);
	validate_game(current.game);
}

/**
 *	@param	number:	The number the mouse was over
 *	@param	type:	The board type
 *	@return
 *	@todo
 *	@bug
 *
 *	Toggles on/off a number on the board
 */
function toggle_board_number( number, type ) {
	try {
		is_valid_number(number);
		is_valid_type(type);
	}
	catch (e) { throw 'toggle_board_number ' + e; }
	if ( is_board_set(number, type) )
		return clear_board_number( number, type );
	else
		return set_board_number( number, type );
}

/**
 *	@param	number:	The number the mouse was over
 *	@param	type:	The board type
 *	@return
 *	@todo
 *	@bug
 *
 *	Turns on a number of the type board.
 */
function set_board_number( number, type ) {
	try {
		is_valid_number(number);
		is_valid_type(type);
	}
	catch (e) { throw 'set_board_number ' + e; }
	var square       = board[type][number];
	var base         = type == NUM_TYPE ? NUM_STYLE : SUP_STYLE;
	var hover_class  = ( number == hover.number && type == hover.type ) ? 'hover' : '';
	square.className = base + ' selected ' + hover_class;
	return number;
}

/**
 *	@param	number:	The number the mouse was over
 *	@param	type:	The board type
 *	@return
 *	@todo
 *	@bug
 *
 *	Clears number of the board type
 */
function clear_board_number( number, type ) {
	try {
		is_valid_number(number);
		is_valid_type(type);
	}
	catch (e) { throw 'clear_board_number ' + e; }
	if (!(board[type][number] == null)) {
		var square       = board[type][number];
		var base         = type == NUM_TYPE ? NUM_STYLE : SUP_STYLE;
		var hover_class  = ( number == hover.number && type == hover.type ) ? ' hover' : '';
		square.className = base + hover_class;
	}
	return '';
}

/**
 *	@todo
 *	@bug
 *
 *	Clears all numbers of both boards
 */
function clear_board() {
	for ( var i = 1; i <= MAX.NUMBERS; i++ ) {
		clear_board_number( i, NUM_TYPE );
		clear_board_number( i, SUP_TYPE );
	}
}

/**
 *	@todo
 *	@bug
 *
 *	Sets all the numbers from the current game onto the board
 */
function setup_board() {
	for ( var i = 0; i < game.num[current.game].length; i++ ) {
		if ( game.num[current.game][i].value )
			set_board_number( game.num[current.game][i].value, NUM_TYPE );
	}
	for ( var i = 0; i < game.sup[current.game].length; i++ ) {
		if ( game.sup[current.game][i].value )
			set_board_number( game.sup[current.game][i].value, SUP_TYPE );
	}
}

/**
 *	@param	number:	The number the mouse was over
 *	@param	type:	The board type
 *	@return
 *	@todo
 *	@bug
 *
 *	Gets the entry position to enter a number to (if number is found that position is returned) other wise the current position is returned
 */
function get_position( number, type ) {
	is_valid_number(number);
	is_valid_type(type);
	var found = has_position( number, type );
	if ( found >= 0 ) {
		current[type].pos = found;
		return found;
	}
	return current[type].pos;
}

/**
 *	@param	number:	The number the mouse was over
 *	@param	type:	The board type
 *	@return
 *	@todo
 *	@bug
 *
 *	Returns the position of number if it has been entered some where (returns -1 if number is not found)
 */
function has_position( number, type ) {
	is_valid_number(number);
	is_valid_type(type);
	var numbers = game[type][current.game];
	for ( var i = 1; i <= numbers.length; i++ ) {
		var pos = 1*current[type].pos + i < MAX[type] ? 1*current[type].pos + i : 1*current[type].pos + i - MAX[type];
		if ( numbers[pos].value == number )
			return pos;
	}
	return -1;
}

/**
 *	@param	number:	The number the mouse was over
 *	@param	type:	The board type
 *	@return
 *	@todo
 *	@bug
 *
 *	Moves the current entry position to the next logical position
 */
function set_next_position(type) {
	is_valid_type(type);
	var numbers     = game[type][current.game];
	var current_pos = current[type].pos;
	current_pos    *= 1;
	for ( var i = 0; i < numbers.length; i++ ) {
		var pos = current_pos + i < numbers.length - 1 ? current_pos + i : current_pos + i - numbers.length + 1;
		if ( !numbers[pos].value ) {
			current[type].pos = pos;
			if ( numbers[pos] ) {
				try {
					numbers[pos].focus();
					numbers[pos].select();
				}
				catch (e) {}
			}
			else debug( pos + ' has no data!');
			return pos;
		}
	}
	var pos = current_pos + 1 < numbers.length ? current_pos + 1 : current_pos + 1 - numbers.length;
	current[type].pos = pos;
	if ( numbers[pos] ) {
		try {
			numbers[pos].focus();
			numbers[pos].select();
		}
		catch (e) {}
	}
	else debug( pos + ' has no data!');
	return pos;
}

/**
 *	@param	number:	The number the mouse was over
 *	@param	type:	The board type
 *	@return
 *	@todo
 *	@bug
 *
 *	Determines if the number is set on the board
 */
function is_board_set(number, type) {
	is_valid_number(number);
	is_valid_type(type);
	var square = board[type][number];
	if ( square.className.match( 'selected' ) )
		return true;
	return false;
}

/**
 *	@todo
 *	@bug
 *
 *	Sets the current entry position
 */
function update_position() {
	try {
		game[current.type][current.game][current[current.type].pos].focus();
		game[current.type][current.game][current[current.type].pos].select();
	}
	catch (e) {}
}

/**
 *	@param	game_no:	The game to validate
 *	@return
 *	@todo
 *	@bug
 *
 *	Checks that all numbers are entered and not repeated
 */
function validate_game(game_no) {
	if ( !game_no )
		game_no = current.game;
	if ( !validate_nums(game_no) )
		return false;
	return validate_sups(game_no);
}

/**
 *	@param	game_no:	The game to validate
 *	@return
 *	@todo
 *	@bug
 *
 *	Checks that all ordinary numbers have been entered and not repeated
 */
function validate_nums(game_no) {
	if ( !game_no )
		game_no = current.game;
	var numbers = new Array();
	for ( var i = 0; i < game.num[game_no].length; i++ ) {
		var number = game.num[game_no][i].value;
		if ( !number || numbers[number] )
			return false;
		if ( number < 1 || MAX.NUMBERS < number )
			return false;
		numbers[number] = 1;
	}
	return true;
}

/**
 *	@param	game_no:	The game to validate
 *	@return
 *	@todo
 *	@bug
 *
 *	Checks that all supplementry numbers have been entered and not repeated
 */
function validate_sups(game_no) {
	if ( !game_no )
		game_no = current.game;
	var numbers = new Array();
	for ( var i = 0; i < game.sup[game_no].length; i++ ) {
		var number = game.sup[game_no][i].value;
		if ( !number || numbers[number] )
			return false;
		if ( number < 1 || MAX.NUMBERS < number )
			return false;
		numbers[number] = 1;
	}
	return true;
}

/**
 *	@return
 *	@todo
 *	@bug
 *
 *	Checks that all games have been entered and are valid
 */
function validate_games() {
	for ( var i = 1; i <= MAX.GAMES; i++ ) {
		if ( !validate_game(i) )
			return false;
	}
	return true;
}

/**
 *	@param	number:	The number the mouse was over
 *	@return
 *	@todo
 *	@bug
 *
 *	Checks that the passed number is a valid number (throws and error if it is not)
 */
function is_valid_number( number ) {
	if ( number == '' )
		throw 'No number passed!';
	if ( number < 1 || MAX.NUMBERS < number )
		throw 'The number '+number+' is out of bounds!';
}

/**
 *	@param	type:	The board type
 *	@return
 *	@todo
 *	@bug
 *
 *	Checks that the passed type is a valid number (throws and error if it is not)
 */
function is_valid_type( type ) {
	if ( !(type == 'num' || type == 'sup' ) )
		throw 'Unknown type "'+type+'"!';
}

/**
 *	@param	game_no:	The game to validate
 *	@return
 *	@todo
 *	@bug
 *
 *	Checks that the passed game is a valid number (throws and error if it is not)
 */
function is_valid_game( game_no ) {
	if ( game_no == '' )
		throw 'No game number passed!';
	if ( game_no < 1 || MAX.GAMES < game_no )
		throw 'The game number "'+game_no+'" is out of bounds!';
}

/**
 *	@param	string:	Debug message to display
 *
 *	Prints descriptive messages onto the game to testing pourposes. This is turned on/off with the DEBUG variable.
 */
function debug( string ) {
	if (!DEBUG)
		return;
	if (!point) {
		point = document.getElementById('games_form').parentNode.parentNode.appendChild( document.createElement('td') );
		point.style.fontSize = '0.7em';
	}
	var p = document.createElement('div');
	p.appendChild( document.createTextNode(string) );
	point.appendChild(p);
}


/* *** Array prototype changes *** */

/**
 *	@param	search_term:	the item to check if it is in the array
 *	@return	bool:	true if search_term is found false other wise
 *	@todo
 *	@bug
 *
 *	Adds new method to Arrays()s to checks to see if 'search_term' is in the array
 */
/*
Array.prototype.in_array = function(search_term) {
	var i = this.length;
	do {
		if(this[i] === search_term) return true;
		if(i < 0) break;
	} while(--i);
	return false;
}
*/

/**
 *	@return	Array:	New unique array made of the of the original elements
 *	@todo
 *	@bug
 *
 *	Adding new method to Array()s to create a new list with out duplicates
 */
/*
Array.prototype.uniq = function() {
	// zero or one element arrays are always unique
	if (this.length < 2) {
		return this;
	}

	var list = [this[0]];
	for ( var i = 0; i < this.length; i++ ) {
		var check = true;
		for ( var j = 0; j < list.length; j++ ) {
			if ( list[j] == this[i] ) {
				check = false;
			}
		}
		if (check) {
			list.push( this[i] );
		}
	}
	return list;
}
*/
function uniq(orig) {
	// zero or one element arrays are always unique
	if (orig.length < 2) {
		return orig;
	}

	var list = [orig[0]];
	for ( var i = 0; i < orig.length; i++ ) {
		var check = true;
		for ( var j = 0; j < list.length; j++ ) {
			if ( list[j] == orig[i] ) {
				check = false;
			}
		}
		if (check) {
			list.push( orig[i] );
		}
	}
	return list;
}
