/**
 *
 * A javascript class for picking random lotto numbers
 *
 * Example usage:
 *   var picker = new OzLotteries.QuickPick(45);
 *   var picked = picker.quickpick(6);
 *   alert(picked.join(' '));
 *
 * TODO: update to include QuickPickSets info...
 *
 * (Note: this requires jQuery but only for $.extend)
 *
 */

(function(){

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

var OzLotteries = window.OzLotteries;

/*
 * Create OzLotteries.QuickPick and OzLotteries.QuickPickSets classes
 */
$.extend(OzLotteries,{
	QuickPick: function(total_numbers,how_many_default,offset_above_one) {
		this.init(total_numbers,how_many_default,offset_above_one);
	},
	QuickPickSets: function(sets) {
		this.init(sets);
	}
});

/*
 * Add OzLotteries.QuickPick methods
 */
$.extend(OzLotteries.QuickPick,{

	DEFAULT_TOTAL_NUMBERS: 45,
	DEFAULT_HOW_MANY:      6,
	DEFAULT_OFFSET:        0,

	/*
	 * Sort randomly. For use with Array::sort
	 */
	sorter_random: function() {
		return 0.5 - Math.random();
	},

	/*
	 * Sort numerically. For use with Array::sort
	 */
	sorter_numeric: function(a,b) {
		return a - b;
	},

	/*
	 * A random int between min and max
	 */
	random_int: function(min, max) {
		return Math.floor(Math.random() * (max - min + 1)) + min;
	},

	prototype: {

		/*
		 * Constructor for OzLotteries.QuickPick objects
		 *
		 * @param int total_numbers how many ping pong balls are in the barrel
		 *
		 * @param int how_many_default how many are we going to be pulling out
		 *    (used in get_numbers if you don't specify how many balls you want)
		 *
		 * @param int offset_above_one optional defaults to zero
		 *    Eg, if total_numbers is 45 then if offset_above_one is 10 you will
		 *    be choosing from numbers from 11 to 55.
		 */
		init: function(total_numbers,how_many_default,offset_above_one) {
			this.total_numbers = total_numbers       || OzLotteries.QuickPick.DEFAULT_TOTAL_NUMBERS;
			this.how_many_default = how_many_default || OzLotteries.QuickPick.DEFAULT_HOW_MANY;
			this.offset_above_one = offset_above_one || OzLotteries.QuickPick.DEFAULT_OFFSET;
			this.setup_numbers(); // creates this.numbers
			this.shuffle(); // initial spin for good luck
		},

		/*
		 * Put the ping pong balls in the barrel
		 */
		setup_numbers: function() {
			this.numbers = new Array(this.total_numbers);
			for (var i=0;i<this.total_numbers;i++) {
				this.numbers[i] = (i + 1) + this.offset_above_one; // i + 1 so there's no ball zero
			}
		},

		/*
		 * Spin the barrel
		 */
		shuffle: function() {
			// Make sure it's good and shuffled
			var num_spins = OzLotteries.QuickPick.random_int(5,10);
			for (var i=0;i<num_spins;i++) {
				this.numbers.sort(OzLotteries.QuickPick.sorter_random);
			}
		},

		/*
		 * Pull out some balls
		 */
		get_numbers: function(how_many) {
			how_many = typeof how_many != 'undefined' ? how_many : this.how_many_default; // how_many might be zero
			// A little bit of sanity checking
			if (how_many > this.total_numbers) throw "Can't pick " + how_many + " from " + this.total_numbers + " (ozl-quickpick.js)";
			// Instead of grabbing the first N we will randomly choose where to start grabbing balls (teh extra randomz)
			var start_at = OzLotteries.QuickPick.random_int(0,this.total_numbers - how_many);
			return this.numbers.slice(start_at,start_at + how_many);
		},

		/*
		 * Spin the barrel, pull out some balls
		 */
		quickpick: function(num) {
			this.shuffle();
			return this.get_numbers(num);
		},

		/*
		 * A quickpick with the result sorted
		 */
		quickpick_sorted: function(num) {
			var result = this.quickpick(num);
			result.sort(OzLotteries.QuickPick.sorter_numeric);
			return result;
		}
	}
});

/*
 * Add OzLotteries.QuickPickSets methods
 */
$.extend(OzLotteries.QuickPickSets,{

	/*
	 * Adds up the 'key' fields in an array of hashes 
	 */
	key_sum: function(arr,key){
		var sum = 0;
		for (var i=0;i<arr.length;i++) {
			sum += arr[i][key]; // Presume ints here
		}
		return sum;
	},

	/*
	 * When quick pick is used outside the play.php page we don't have access
	 * to a collated sets array. In this case the collate_set_details function
	 * can be used. Eg:
	 *    var qp = new OzLotteries.QuickPickSets(
	 *       OzLotteries.QuickPickSets.collate_set_details(firsts,lasts,counts,pool_ids)
	 *    );
	 * to the following the set details aren't collated into an array. 
	 */
	collate_set_details: function(firsts,lasts,counts,pool_ids) {
		var result = [];
		for (var i=0;i<counts.length;i++) {
			result.push({
				count:   counts[i],
				first:   firsts[i],
				last:    lasts[i],
				pool_id: pool_ids[i]
			});
		}
		return result;
	},

	prototype: {

		/*
		 * Constructor for OzLotteries.QuickPickSets object.
		 * Setup everything so we're ready to pick some numbers.
		 * @param array sets an array of hashes with the following keys: count, first, last, pool_id.
		 */
		init: function(sets) {
			this.sets = sets;
			this.setup_pool_groups();
			this.setup_pickers();
		},

		/*
		 * Group the sets into pool groups, ie groups with the same pool_id
		 * We deal with the generic case of N sets in case some day we have more than two sets.
		 */
		setup_pool_groups: function() {
			this.pools = {};
			for (var i=0;i<this.sets.length;i++) {
				var set = this.sets[i];
				var pool_id = set.pool_id;
				set.order = i; // Stash this here to remember the original set order
				this.pools[pool_id] = this.pools[pool_id] || [];
				this.pools[pool_id].push(set);
			}
		},

		/*
		 * Setup an OzLotteries.QuickPick for each pool
		 */
		setup_pickers: function() {
			this.pickers = {};
			for (var pool_id in this.pools) {
				// 'first' and 'last' should be the same for every set in a pool group.
				// So we just grab value of last from the 0th set in this pool.
				var first = this.pools[pool_id][0].first;
				var last = this.pools[pool_id][0].last;
				var balls_in_pool = last - first + 1; // eg, 1 to 10 is 10 balls
				var offset_above_one = first - 1; // eg, if first is 1, offset_above_one is zero
				// This is how many balls need to be drawn from this pool altogether
				var count_in_pool = OzLotteries.QuickPickSets.key_sum(this.pools[pool_id],'count');
				// Get a picker ready
				this.pickers[pool_id] = new OzLotteries.QuickPick(balls_in_pool,count_in_pool,offset_above_one);
			}
		},

		/*
		 * Return quick pick numbers for each set based on sets given in constuctor
		 */
		quickpick_sets: function() {
			var result = Array(this.sets.length);
			// Loop over the pools
			for (var pool_id in this.pools) {
				// Get the numbers for this pool
				var picked_numbers = this.pickers[pool_id].quickpick();
				var number_pointer = 0;
				// Slice them up per set as needed
				// (It's important that picked_numbers is not sorted here)
				for (var i=0;i<this.pools[pool_id].length;i++) {
					var set = this.pools[pool_id][i];
					var set_numbers = picked_numbers.slice(number_pointer, number_pointer += set.count);
					// Sort because it looks nicer on screen (only when required)
					if (quickpick_sort_numbers) set_numbers.sort(OzLotteries.QuickPick.sorter_numeric);
					// Need to give back the results for the sets in the same order as we got them originally
					result[set.order] = set_numbers;
				}
			}
			return result;
		}
	}
});

})();

