/**
 * ozl-play-picker.js
 * Replacement for play_picker.js
 *
 * Trying to support the current layout as well as the new v3 layout.
 * This way we are close to a drop in replacement for play_picker.js
 * and we are limiting the scope of change.
 *
 * (Note: OzLotteries.PlayPicker is not a class, ie we don't use new
 * OzLotteries.PlayPicker() anywhere and we couldn't have two PlayPicker
 * instances in a single page. It's just a scope to hold some
 * vars and methods used on the play game page.
 *
 * @author simonb
 * @created Dec-2009
 */

(function(){

/*
 * Initialise OzLotteries in the global scope if it's not already there
 */
window.OzLotteries = window.OzLotteries || {};
var OzLotteries = window.OzLotteries;

/*
 * Our code will live in OzLotteries.PlayPicker
 */
$.extend(OzLotteries,{
	PlayPicker: {

		/* ----------------------------------------------------------------------
		 * This holds the index of the currently selected game.
		 * A bit like the cursor 'y' position.
		 * Set setter needs to do a few things like refresh the board etc.
		 */
		_current_game_index: 0,

		_current_set_index: 0,

		_current_number_of_games: 0,
		_play_form_rebuild_counter: 0,
		_current_game_number_picks: Array(),

		get_current_game_index: function() {
			return this._current_game_index;
		},

		get_current_set_index: function() {
			return this._current_set_index;
		},

		set_current_game_index: function(new_game_index,fast_update) {
			this._current_game_index = new_game_index;
			this._current_field_indexes = []; // effectively zeros the current field indexes
			this.get_game_radio_button().attr('checked',true);
			if (!fast_update) {
				this.position_board();
				this.refresh_board();
				this.highlight_current();
			}
		},

		/* ----------------------------------------------------------------------
		 * This holds the currently selected number fields (one per each set).
		 * (We usually have either one or two sets. Eg, in powerball, the main numbers are
		 * the first set and the powerball is the second set).
		 * A bit like the cursor 'x' position per set.
		 */
		_current_field_indexes: [],

		get_current_field_index: function(set_index) {
			return this._current_field_indexes[set_index] || 0;
		},

		set_current_field_index: function(set_index,new_field_index) {
			this._current_field_indexes[set_index] = new_field_index;
		},

		/* ----------------------------------------------------------------------
		 * The number of games. We are deriving it. Could probably get it elsewhere?
		 */
		get_game_count: function() {
			if (!this._game_count) this._game_count = this.get_games().length;
			return this._game_count;
		},

		/* ----------------------------------------------------------------------
		 * pool_first, pool_last, set_counts and pool_id are globals defined
		 * somewhere upstream. I don't like this much. TODO: refactor maybe..?
		 */
		get_length_of_set: function(set_index) {
			return set_counts[set_index];
		},

		get_maximum_in_set: function(set_index) {
			return pool_last[set_index];
		},

		/* ----------------------------------------------------------------------
		 * The quickpicker is setup once and used many times. Notice use of globals here also.
		 */
		_quickpicker: null,

		get_quickpicker: function() {
			if (!this._quickpicker) {
				this._quickpicker = new OzLotteries.QuickPickSets(
					OzLotteries.QuickPickSets.collate_set_details(pool_first,pool_last,set_counts,pool_id)
				);
			}
			return this._quickpicker;
		},

		/* ----------------------------------------------------------------------
		 * Define basic methods for accessing games/sets and fields.
		 * Each of these functions returns a jQuery object.
		 * If you don't specify a game_index/field_index then it uses the current game/field
		 * You must always specify a set_index since there is no currently selected set
		 */
		get_games: function() {
			return $('#number_entries').find('tr.game_row');
		},

		get_game: function(game_index) {
			if (typeof game_index == 'undefined') game_index = this.get_current_game_index();
			return this.get_games().eq(game_index);
		},

		get_sets_in_game: function(game_index) {
			return this.get_game(game_index).find('td.game_set');
		},

		get_set: function(set_index,game_index) {
			return this.get_sets_in_game(game_index).eq(set_index);
		},

		get_fields_in_set: function(set_index,game_index) {
			return this.get_set(set_index,game_index).find('input.numb_field');
		},

		get_field: function(set_index,field_index,game_index) {
			if (typeof field_index == 'undefined') field_index = this.get_current_field_index(set_index);
			return this.get_fields_in_set(set_index,game_index).eq(field_index);
		},

		/* ----------------------------------------------------------------------
		 * Recreate the Play Form
		 */
		create_play_form: function(num_games_input_id) {
			// get the number of games and the row template
			var new_num_games = $('#' + num_games_input_id).attr('value');
			var row_template = $('#game_template_row').clone();

			// save the game data
			//this._current_game_number_picks = Array();
			for (var game_number=0; game_number<this._current_number_of_games; game_number++) {
				this._current_game_number_picks[game_number] = Array();

				for (var set_number=0; set_number<set_counts.length; set_number++) {
					this._current_game_number_picks[game_number][set_number] = Array();

					for (set_game_number=0; set_game_number<set_counts[set_number]; set_game_number++) {
						var input_id = 'game' + game_number  + '_set' + set_number + '_' + set_game_number;
						this._current_game_number_picks[game_number][set_number][set_game_number] = $('#' + input_id).attr('value');
					}
				}
			}

			// remove all the games
			$('#play_game_form tr').remove();

			// add all the games
			for (var game_number=0; game_number<new_num_games; game_number++) {
				// get the rows class
				var className = 'even';
				if ((game_number % 2) == 1) className = 'odd';

				// make required changes to the row
				var new_row = row_template.clone();
				new_row.attr('id', 'game' + game_number + '_row');
				new_row.addClass('game_row ' + className);
				new_row.find('#game_name_template').attr('innerHTML', (game_number+1));
				new_row.find('#radio_game').attr('id', 'radio_game' + game_number);
				new_row.find('#radio_game').attr('onmouseup', 'select_game(' + game_number + ')');

				// make required changes to the inputs
				for (var set_number=0; set_number<set_counts.length; set_number++) {
					new_row.find('#game_error_template_' + set_number).attr('innerHTML', '');
					new_row.find('#game_error_template_' + set_number).attr('id', 'game_error' + game_number + '_' + set_number);

					// go though all the numbers in the set
					for (set_game_number=0; set_game_number<set_counts[set_number]; set_game_number++) {
						var input_id = 'game_template_set' + set_number + '_' + set_game_number;
						var new_input_id = 'game' + game_number  + '_set' + set_number + '_' + set_game_number;
						new_row.find('#' + input_id).attr('name', new_input_id);
						new_row.find('#' + input_id).attr('id', new_input_id);
					}
				}

				new_row.appendTo('#play_game_form');
			}

			// restore the game data
			for (var game_number=0; game_number<Math.min(this._current_game_number_picks.length, new_num_games); game_number++) {
				for (var set_number=0; set_number<set_counts.length; set_number++) {
					for (set_game_number=0; set_game_number<set_counts[set_number]; set_game_number++) {
						var input_id = 'game' + game_number  + '_set' + set_number + '_' + set_game_number;
						if (typeof(PP._current_game_number_picks[game_number][set_number]) != "undefined") {
						   $('#' + input_id).attr('value', this._current_game_number_picks[game_number][set_number][set_game_number]);
						}
					}
				}
			}
			this._current_number_of_games = new_num_games;
			this._game_count = this.get_games().length;
			this.initialise();

			// check all the games for errors
			if (this._play_form_rebuild_counter == 0 && stored_numbers_array.length > 0) {
				for (var game_number=0; game_number<Math.min(this._current_game_number_picks.length, new_num_games); game_number++) {
					for (set_number=0; set_number<set_counts.length; set_number++) {
						$('#game_error' + game_number + '_' + set_number).attr('innerHTML', 'check');
					}
					check_single_game(document.game_offer, game_number, false);
				}
			}

			// show new number of games in the old_count variable
			$('#old_count').attr('value', new_num_games);

			// re-init the play picker
			this._play_form_rebuild_counter++;
			$(".numb_field").focus(PP.number_field_focus);
			$(".numb_field").blur(PP.number_field_blur);
			$("input[name=radio_game]").click(PP.game_radio_click);
		},
		 

		/* ----------------------------------------------------------------------
		 * Couple of extras...
		 */
		get_all_fields: function() {
			return $('input.numb_field');
		},

		get_game_radio_button: function(game_index) {
			return this.get_game(game_index).find('input[name=radio_game]');
		},

		/* ----------------------------------------------------------------------
		 * Define methods for accessing/dealing with field values
		 */
		get_field_val: function(set_index,field_index,game_index) {
			return this.get_field(set_index,field_index,game_index).val();
		},

		get_field_val_as_int: function(set_index,field_index,game_index) {
			return parseInt(this.get_field_val(set_index,field_index,game_index),10);
		},

		field_is_blank: function(set_index,field_index,game_index) {
			return $.trim(this.get_field_val(set_index,field_index,game_index)) == '';
		},

		string_is_valid_int: function(val,max) {
			var int_val = parseInt(val,10);
			return (!isNaN(int_val) && (""+int_val) == (""+val) && int_val >= 1 && int_val <= max);
		},

		/*
		 * If the value of a field isn't a valid integer then just clear it
		 * TODO: could be more polite about it, eg let the user know what just happened somehow
		 */
		clear_invalid_field: function(set_index,field_index,game_index) {
			var field = this.get_field(set_index,field_index,game_index);
			var val = field.val();
			field.val(this.string_is_valid_int(val,this.get_maximum_in_set(set_index)) ? val : '');
		},

		/* ----------------------------------------------------------------------
		 * Define methods for accessing the board picker numbers
		 */
		get_board_number: function(set_index,picker_number) {
			return $('#picker').find('#set'+set_index+'_number'+picker_number);
		},

		get_board_numbers_in_set: function(set_index) {
			return $('#picker').find('.set'+set_index);
		},

		get_set_board: function(set_index) {
			return $('#picker').find('.gamenumber_cells').eq(set_index);
		},

		all_board_numbers: function() {
			return $('#picker').find('.gamenumber_cells td');
		},

		mark_board_number: function(set_index,picker_number,marked) {
			this.get_board_number(set_index,picker_number).toggleClass('selected',marked);
		},

		/* ----------------------------------------------------------------------
		 * Functions dealing with the currently selected field including
		 * placing a number into a set.
		 */

		/*
		 * Check if a game has had all its numbers filled
		 */
		check_game_complete: function(game_number) {
			var all_numbers_filled = true;
			for( var set_number = 0; set_number < set_counts.length; set_number++ ) {
				for( var number_index = 0; number_index < set_counts[set_number]; number_index++ )
				{
					if ($('#game' + game_number + '_set' + set_number + '_' + number_index).attr('value').length == 0) {
						all_numbers_filled = false;
					}
				}
			}
			return all_numbers_filled;
		},

		/*
		 * Check if a game has had all its numbers filled
		 */
		check_all_games_complete: function() {
			var last_game = this.get_game_count() - 1;
			var is_game_board_full = true;
			for (var i=0; i<=last_game; i++) {
				if (!this.check_game_complete(i)) {
					is_game_board_full = false;
				}
			}
			return is_game_board_full;
		},

		/*
		 * Utility for incrementing with a wrap-around
		 */
		inc_wrap: function(cur,last) {
			return (cur < last ? cur + 1 : 0);
		},

		/*
		 * Bump the selected game up one. Wrap back to the first game if we hit the end.
		 */
		inc_current_game: function() {
			var cur_game = this.get_current_game_index();
			var last_game = this.get_game_count() - 1;
			this.set_current_game_index(this.inc_wrap(cur_game,last_game));
		},

		/*
		 * This will move the selected number to the first blank number in the game
		 */
		goto_first_blank_field: function(game_number) {
			for( set_number = 0; set_number < set_counts.length; set_number++ ) {
				var found_blank_number = false;
				var new_field_index = 0;
				for( number_index = 0; (number_index < set_counts[set_number] && !found_blank_number); number_index++ )
				{
					if (document.getElementById('game' + game_number  + '_set' + set_number + '_' + number_index).value.length == 0) {
						this.set_current_field_index(set_number, number_index);
						found_blank_number = true;
					}
				}
			}

			this._current_game_index = game_number;
			this.get_game_radio_button().attr('checked',true);
			this.position_board();
			this.refresh_board();
			this.highlight_current();
		},

		goto_next_uncompleted_game: function() {
			var last_game = PP.get_game_count() - 1;
			var cur_game = PP.get_current_game_index();

			// if this is the last game, search remaing games for a non-complete game
			var found_game = false;
			for (var i=cur_game+1; (i<=last_game && !found_game); i++) {
				if (!PP.check_game_complete(i)) {
					PP.set_current_game_index(i);
					found_game = true;
				}
			}

			// if we haven't found a uncompleted game yet
			if (!found_game) {
				for (i=0; (i<cur_game && !found_game); i++) {
					if (!PP.check_game_complete(i)) {
						PP.set_current_game_index(i);
						found_game = true;
					}
				}
			}
		},

		/*
		 * Bump the selected field up one. Wrap around to the first field if we hit the end.
		 */
		inc_current_field: function(set_index) {
			var cur_pos = this.get_current_field_index(set_index);
			var last_pos = this.get_length_of_set(set_index) - 1;
			this.set_current_field_index(set_index,this.inc_wrap(cur_pos,last_pos));
		},

		/*
		 * Similar to inc_current_field except if there is a blank field
		 * then the first blank field found becomes the new selected field.
		 * Otherwise just use the next field sequentially as per inc_current_field.
		 */
		inc_current_field_favour_blank: function(set_index) {
			// Do an initial increment of the selected field (will wrap around if needed)
			this.inc_current_field(set_index);
			// Remember our initial position
			var marker = this.get_current_field_index(set_index);
			// Use this to indicate we should stop looking for an empty field
			var looped = false;
			// While the current field isn't empty and we haven't looped right around yet...
			while (!this.field_is_blank(set_index) && !looped) {
				// Move to the next position
				this.inc_current_field(set_index);
				// Check if we looped yet
				looped = (marker == this.get_current_field_index(set_index));
			}
			// Highlight the currently selected fields
			this.highlight_current();
		},

		/*
		 * Higlight the currently selected fields
		 */
		highlight_current: function() {
			// Unhighlight all fields
			this.get_all_fields().removeClass('highlighted');
			// Highlight the current fields (ie show the current cursor position)
			this.get_sets_in_game().each(function(set_index){
				PP.get_field(set_index).addClass('highlighted');
			});
		},

		/*
		 * Clear a number from a set in the currently selected game
		 */
		clear_number_from_set: function(set_index,number_to_clear) {
			var index = 0;
			this.get_fields_in_set(set_index).each(function(){
				if ($(this).val() == number_to_clear) {
					$(this).val('');
					temp_arr = this.name.split('_');
					PP.set_current_field_index(set_index, parseInt(temp_arr[2]));
				}
			});
		},

		/*
		 * Add a number to a set in the currently selected game
		 */
		add_number_to_set: function(set_index,number_to_add) {
			// Can't have the same number twice
			this.clear_number_from_set(set_index,number_to_add);
			// Put the number into the current selected field
			this.get_field(set_index).val(number_to_add);
			// Move the 'cursor' ahead one
			this.inc_current_field_favour_blank(set_index);
		},

		/*
		 * A utility for parsing out the game, set and field indexes
		 * from the id of a number input field. For example:
		 * foo10_bar8_10 will return { foo: 10, bar: 8, field:10 };
		 */
		parse_details_from_id: function(id_string) {
			var result = {};
			var split_id = id_string.split('_');
			for (var i=0;i<split_id.length;i++) {
				if (match = /^([a-z]+)(\d+)$/.exec(split_id[i])) {
					result[match[1]] = parseInt(match[2],10);
				}
				else if (match = /^(\d+)$/.exec(split_id[i])) {
					result['field'] = parseInt(match[1],10);
				}
			}
			return result;
		},

		/*
		 * Update the number picker board so the X marks are in the right place
		 * for the currently selected game
		 */
		refresh_board: function() {
			this.all_board_numbers().removeClass('selected');
			this.get_sets_in_game().each(function(set_index){
				PP.get_fields_in_set(set_index).each(function(field_index){
					PP.clear_invalid_field(set_index,field_index); // just in case
					PP.mark_board_number(set_index,PP.get_field_val_as_int(set_index,field_index),true);
				});
			});
		},

		/*
		 * Adjust the position of the board so that it's near the current selected game
		 * TODO: put some more comments here
		 */
		position_board: function() {
			// Positioning stuff
			var entries_box_top   = $('#number_entries').offset().top;
			var first_row_top     = this.get_game(0).offset().top;
			var last_row_top      = this.get_game(this.get_game_count() - 1).offset().top;
			var selected_row_top  = this.get_game().offset().top;

			var max_height = $('#number_entries').innerHeight();
			var arrow_offset = first_row_top - entries_box_top

			var offset = selected_row_top - first_row_top;
			var box_height = $('#picker').innerHeight();
			var max_offset = Math.max(0,max_height - box_height);

			var scale;
			var scaled_board_offset;

			// Check to see if there is only one game being played.
			if(last_row_top == first_row_top) {
				// If this case is true, then we are picking numbers for one game ONLY.
				// since these to vars are equal, performing the equation on them below
				// will cause a divide-by-zero error.
				scale = 0;
				scaled_board_offset = 0;
			}
			else {
				// scale the offset from 0 to max_offset
				scale = max_offset / (last_row_top - first_row_top);
				scaled_board_offset = scale * offset;
			}

			$('#picker').css('margin-top',scaled_board_offset);
			$('#picker_arrow').css('margin-top',arrow_offset + offset - scaled_board_offset); // adjust the arrow as required. comment this plz
		},

		/*
		 * Do initialisations
		 */
		initialise: function() {
			// find first non completed game
			var last_game = this.get_game_count() - 1;
			var starting_game = -1;
			for (var i=0; (i<=last_game && starting_game == -1); i++) {
				if (!this.check_game_complete(i)) {
					starting_game = i;
				}
			}

			if (starting_game != -1) {
				this.goto_first_blank_field(starting_game);
			}
			else {
				this.set_current_game_index(0);
			}
		},

		/* ------------------------------------------------------------------
		 * Event handlers.
		 * Note: Remember that 'this' is the clicked element
		 */

		/*
		 * Handles clicking a number in the floating number picker panel
		 */
		board_number_click: function() {
			var details = PP.parse_details_from_id(this.parentNode.id);
			PP._current_set_index = details.set;
			if (!$(this.parentNode).hasClass('selected')) {
				// Adding the number
				PP.add_number_to_set(details.set,details.number);
				// Mark the number with a cross
				$(this.parentNode).addClass('selected');
			}
			else {
				// Removing the number
				PP.clear_number_from_set(details.set,details.number);
				
				// Unmark the number
				$(this.parentNode).removeClass('selected');
			}
			check_single_game(document.game_offer, PP.get_current_game_index(), true);
			PP.refresh_board();
			PP.highlight_current();

			return false;
		},

		/*
		 * 
		 */
		number_field_focus: function() {
			var details = PP.parse_details_from_id(this.id);
			PP.set_current_game_index(details.game);
			PP.set_current_field_index(details.set,details.field);
			PP.highlight_current();
		},

		/*
		 * 
		 */
		game_radio_click: function() {
			var details = PP.parse_details_from_id(this.id);
			PP.set_current_game_index(details.game);
			PP.highlight_current();
		},

		/*
		 * 
		 */
		number_field_blur: function() {
			var details = PP.parse_details_from_id(this.id);
			PP.clear_invalid_field(details.set,details.field,details.game);
			check_single_game(document.game_offer, details.game, false);
			PP.refresh_board();
		},

		/* ----------------------------------------------------------------
		 * Quickpick and clear
		 */

		// fast version doesn't refresh board
		do_clear: function() {
			PP.get_sets_in_game().each(function(set_index){
				PP.get_fields_in_set(set_index).each(function(){
					$(this).val('');
				});
			});
		},

		// fast version, doesn't refresh board
		do_quickpick: function() {
			var foo = PP.get_quickpicker();
			var quickpicked_numbers = foo.quickpick_sets();
			PP.get_sets_in_game().each(function(set_index){
				PP.get_fields_in_set(set_index).each(function(field_index){
					if (typeof(quickpicked_numbers[set_index]) != "undefined") {
						$(this).val(quickpicked_numbers[set_index][field_index]);
					}
				});
			});
		},

		clear: function() {
			PP.do_clear();
			check_single_game(document.game_offer, PP.get_current_game_index(), false);
			PP.refresh_board();
		},

		quickpick: function() {
			var cur_game = PP.get_current_game_index();
			var last_game = PP.get_game_count() - 1;
			PP.do_quickpick();
			check_single_game(document.game_offer, PP.get_current_game_index(), false);
			/*if (!PP.check_all_games_complete() || (PP.check_all_games_complete() && cur_game != last_game)) {
			   PP.inc_current_game();
			}*/
			PP.goto_next_uncompleted_game();
			PP.refresh_board();
		},

		clear_all: function() {
			PP.get_games().each(function(game_index){
				PP.set_current_game_index(game_index,/*fast=*/true);
				PP.do_clear();
				check_single_game(document.game_offer, game_index, false);
			});
			PP.set_current_game_index(0);
		},

		quickpick_all: function() {
			var $button_text = $('#quickpick_all_text');
			var wait_message = 'Please wait...';
			// Save button text and change it to please wait..
			var orig_text = $button_text.text();
			$button_text.text(wait_message);
			$(this.parentNode).css('background-color','silver'); // TODO use a class
			PP.get_games().each(function(game_index){
				//PP.set_current_game_index(game_index,/*fast=*/false);
				PP.set_current_game_index(game_index,true);
				PP.do_quickpick();
				check_single_game(document.game_offer, game_index, false);
			});
			PP.set_current_game_index(0);
			// Restore button
			$(this.parentNode).css('background-color',null);
			$button_text.text(orig_text);
		}
	}
});

/*
 * Save some keystrokes...
 */
var PP = OzLotteries.PlayPicker;

// Disable all the legacy stuff.
// TODO: remove the calls from templates/play/games.php
window.set_params  = function(){};
window.select_game = function(){};
window.type_entry  = function(){};
window.exit_entry  = function(){};
window.enter_entry = function(){};
window.click_board = function(){};

window.PP              = PP;
window.clear_picks     = PP.clear;
window.clear_all       = PP.clear_all;
window.quick_pick      = PP.quickpick;
window.quick_pick_all  = PP.quickpick_all;

$(document).ready(function(){
	$('#play_game_form_div').attr('style', 'display: ;');
	$('#picker .board_number').click(PP.board_number_click);
	PP.initialise();
	PP._current_game_number_picks = stored_numbers_array;
	PP.create_play_form('new_count');
});

})();


