/**
 * The class for a slot machine.
 *
 * @copyright	Daniel Lehmann, DreamMedia, Berlin, Germany (www.dreammedia-bny.com)
 * @author		Daniel Lehmann <code@dreammedia-bny.com>
 * @version		1.0
 * @date        07.06.2011
 */


 
/**
 * The constructor.
 *
 * @param	slots	an array containing the div areas of the slots. the div areas have to have a style position other than "static"
 **/
function SlotMachine(slots) {
	
	this.chance = 30;			// the chance that a slot image belongs to the same group than the image in the first slot. a value between 0 and 100
	this.min_rotations = 0.3;	// the minimum on rotations until the slot stops
	this.max_rotations = 0.5;		// the maximum on rotations until the slot stops
	this.interval = 50;			// the interval in which the next frame will be drawn in milli seconds
	this.start_interval = 350;	// the interval in which the slots start to rotate in milli seconds
	this.max_step = 15;			// the step an animation can maximal do between two frames in percent of the height of the next image
	this.speed_up = 0.2;		// how much the animation speeds up from frame to frame at the beginning
	this.slow_down = 6;			// a factor on how fast the animation slows down at the end. as higher as slower
	this.point_of_return = 10;	// how far will the final image go over the goal before it snaps back into its final position (in percent of the height of the image)
	
	this.slots = slots;
	this.groups = new Array();
	
}

/**
 * Initialises the slot machine.
 * All image groups have to be added at this point.
 *
 * @param	callback	a function which will be called when all images got loaded and the initialisation is finished (optional). gets a refernce to "this" as parameter
 **/
SlotMachine.prototype.init = function(callback) {
	
	var me = this;
	var load_counter = 0;
	var initialising = true;
	var slots = this.slots;
	
	var initSlots = function () {
		for (var i=0; i<slots.length; i++) {
			slots[i].setImage(0);
		}
	}
	
	for (var i=0; i<this.slots.length; i++) {
		
		var images = new Array();
		for (var j=0; j<this.groups.length; j++) {
			var image = this.groups[j][i];
			images.push(image);
			
			load_counter++;
			image.onload = function() {
				load_counter--;
				if (!initialising && load_counter == 0) {
					initSlots();
					if (callback != undefined) callback(me);
				}
			}
		}
		
		this.slots[i] = new Slot(this.slots[i], images, this);
	}	
	
	initialising = false;
	if (load_counter == 0) {
		initSlots();
		if (callback != undefined) callback(me);
	}
	
}

/**
 * Adds a group of images to the slot machine.
 *
 * @param	group	an array containing the images (Image-class). the length of the array has to be the amount of slots
 **/
SlotMachine.prototype.addGroup = function(group) {
	
	if (group.length != this.slots.length) return;
	
	this.groups.push(group);
	
}

/**
 * Starts a new rotation.
 *
 * @param	callback	a function which will be called when the slots stoped (optional). gets a refernce to "this" as parameter
 **/
SlotMachine.prototype.start = function(callback) {
	
	if (this.isRotating()) return;
	
	var positions = this.getPositions();
	
	var first_position = Math.floor(Math.random()*this.groups.length);
	if (this.groups.length > 1) while (first_position == positions[0]) first_position = Math.floor(Math.random()*this.groups.length);
	
	var new_positions = new Array();
	new_positions.push(first_position);
	for (var i=1; i<this.slots.length; i++) {
		if (Math.random() < this.chance/100) new_positions.push(first_position);
		else {
			var new_position = first_position + Math.floor(Math.random()*(this.groups.length-1)) + 1;
			while (new_position >= this.groups.length) new_position -= this.groups.length;
			new_positions.push(new_position);
		}
	}
	
	var slots = this.slots;
	var startSlot = function() {
		
		for (var i=0; i<slots.length; i++) {
			if (slots[i].interval == undefined) {
				slots[i].animate(new_positions[i], finished);	
				return;
			}
		}
		
		window.clearInterval(start_interval);
		
	}
	
	var me = this;
	var slots_finished = this.slots.length;
	var finished = function() {
		slots_finished--;
		if (slots_finished <= 0 && callback != undefined) callback(me); 
	}
	
	var start_interval = window.setInterval(function(){startSlot();}, this.start_interval);
	slots[0].animate(new_positions[0], finished);
	
}

/**
 * Returns an array with the indices of the currently shown images of the slots.
 *
 * @return	array
 **/
SlotMachine.prototype.getPositions = function() {
	
	var indices = new Array();
	for (var i=0; i<this.slots.length; i++) {
		indices.push(this.slots[i].index);
	}
	
	return indices;
	
}

/**
 * Returns if the slot machine is rotating.
 *
 * @return	boolean
 **/
SlotMachine.prototype.isRotating = function() {
	
	for (var i=0; i<this.slots.length; i++) {
		if (this.slots[i].interval != undefined) return true;
	}
	
	return false;
	
}


/**
 * The constructor for a slot.
 *
 * @param	window		the div area of the slot
 * @param	images 		an array of the images of the slot
 * @param	machine		the slot machine to which the slot belongs
 **/
function Slot(window, images, machine) {
	
	this.window = window;
	this.images = images;
	this.machine = machine;
	
	this.index = 0;
	this.offset = 0;
	
	this.interval = undefined;
	
}

/**
 * Sets an image and its position.
 *
 * @param	index	the index of the image
 * @param	offset	the offset of the image position. a value between 0 and 100. an offset greater or equal 100 results in a new index (optional)
 * @return	the new index
 **/
Slot.prototype.setImage = function(index, offset) {
	
	if (!this.images.length) return 0;
	
	if (offset == undefined) offset = 0;
	if (offset < 0) offset = 0;
	while (offset >= 100) {
		index++;
		offset -= 100;
	}
	
	while (index >= this.images.length) index -= this.images.length;
	while (index < 0) index += this.images.length;
	
	var next_index = index+1;
	if (next_index == this.images.length) next_index = 0;
	
	var image = new Image();
	image.src = this.images[index].src;
	var next_image = new Image();
	next_image.src = this.images[next_index].src;
	
	var top_offset = this.images[next_index].height * (offset/100);
	
	image.style.position = next_image.style.position = "absolute";
	image.style.top = top_offset+"px";
	next_image.style.bottom = "-"+top_offset+"px";
	
	while(this.window.childNodes.length) this.window.removeChild(this.window.firstChild);
	this.window.appendChild(next_image);
	this.window.appendChild(image);
	
	this.index = index;
	this.offset = offset;
	
	return index;
	
}

/**
 * Starts an animation.
 *
 * @param	to			the index of the image to on which the animation should stop.
 * @param	callback	a function which will be called when the slot stoped (optional)
 **/
Slot.prototype.animate = function(to, callback) {
	
	if (this.interval != undefined) window.clearInterval(this.interval);
	
	var rounds = Math.floor(Math.random()*(this.machine.max_rotations - this.machine.min_rotations)) + this.machine.min_rotations;
	if (rounds < 0) rounds = 0;
	
	var me = this;
	var previous = (to != 0) ? to-1 : this.images.length-1;
	var step = 0;
	var max_step = this.machine.max_step;
	var speed_up = this.machine.speed_up;
	var round_done = false;
	
	// starts and keeps the rotation going
	var rotate = function() {
		
		step += speed_up;
		if (step > max_step) step = max_step;
		var offset = me.offset + step;
		if (offset >= 100) round_done = true;
		
		me.setImage(me.index, me.offset+step);
		
		if (me.index == previous && round_done) {
			rounds--;
			round_done = false;
		}
		
		if (rounds <= 0 && me.index == previous) {
			window.clearInterval(me.interval);
			me.interval = window.setInterval(function(){slowDown();}, me.machine.interval);
		}
		
	}
	
	// slows the rotation down
	var slowDown = function() {
	
		var pixel_of_return = me.images[to].height * (me.machine.point_of_return/100);
		var pixel_to_go = 0;
		if (me.index == previous) pixel_to_go = pixel_of_return + me.images[previous].height * ((100-me.offset)/100);
		if (me.index == to) pixel_to_go = pixel_of_return - me.images[previous].height * (me.offset/100);
		if (pixel_to_go < 1) pixel_to_go = 0;
		
		var pixel_step = pixel_to_go / me.machine.slow_down;
		var next_step = pixel_step / me.images[previous].height * 100;
		
		step += speed_up;
		step = (next_step > step) ? step : next_step;
		if (step > max_step) step = max_step;
		
		me.setImage(me.index, me.offset+step);
		
		if (pixel_to_go == 0) {
			window.clearInterval(me.interval);
			me.interval = window.setInterval(function(){snapBack();}, me.machine.interval);
		}
		
	}
	
	// snaps the image back in its final position
	var snapBack = function() {
		
		step += speed_up;
		if (step > max_step) step = max_step;
		
		me.setImage(me.index, me.offset - step);
		
		if (me.offset == 0) {
			window.clearInterval(me.interval);
			me.interval = undefined;
			if (callback != undefined) callback();
		}
		
	}
	
	this.interval = window.setInterval(function(){rotate();}, this.machine.interval);
	
}
