/*
 * gameQuery 0.1
 *
 * Copyright (c) 2008 Selim Arsever (www.onaluf.org)
 * licensed under the MIT (MIT-LICENSE.txt)
 *
 */

// This loop is used for  animation and event generation:
function mainLoop() {
	//do stuff here !
	var start_msec = (new Date()).getMilliseconds();
	jQuery.gameQuery.resourceManager.refresh();
	var stop_msec = (new Date()).getMilliseconds();
	var diff_msec = stop_msec - start_msec;
	if(diff_msec > jQuery.gameQuery.refreshRate){
		setTimeout("mainLoop()",1);
	} else if(diff_msec < 0) {
		//this is a fix for a weird situation where the diff is a large
		// negatif value --> TO INVESTIGATE LATER
		setTimeout("mainLoop()",(jQuery.gameQuery.refreshRate));
	} else {
		setTimeout("mainLoop()",(jQuery.gameQuery.refreshRate-diff_msec));
	}
}

function waitForResources(){
	var loadbarEnabled = (jQuery.gameQuery.loadbar != undefined);
	if(loadbarEnabled){
		$(jQuery.gameQuery.loadbar.id).width(0); 
		var loadBarIncremant = jQuery.gameQuery.loadbar.width / (jQuery.gameQuery.resourceManager.animations.length + jQuery.gameQuery.resourceManager.sounds.length);
	}
	//number of loaded resources
	var count = 0; 
	//check the images
	for(var i=0; i < jQuery.gameQuery.resourceManager.animations.length; i++){
		if(jQuery.gameQuery.resourceManager.animations[i].domO.complete){
			count++;
		}
	}
	//check the sounds 
	for(var i=0; i < jQuery.gameQuery.resourceManager.sounds.length; i++){
		if(jQuery.gameQuery.resourceManager.sounds[i].domO.complete){
			count++;
		}
	}
	//uptdate the loding bar
	if(loadbarEnabled){
		$("#"+jQuery.gameQuery.loadbar.id).width(count*loadBarIncremant); 
	}
	
	if(count < (jQuery.gameQuery.resourceManager.animations.length + jQuery.gameQuery.resourceManager.sounds.length)){
		imgWait=setTimeout('waitForResources()', 100);
	}
	else{
		//if the element is the playground we start the game:
		jQuery.gameQuery.resourceManager.running = true;
		mainLoop();
		if(jQuery.gameQuery.startCallback){
			jQuery.gameQuery.startCallback();
		}
		//make the sceengraph visible
		jQuery.gameQuery.playground.children(".sceengraph").css("visibility","visible");
	}
}

// this allow to used the convenient $ notation in  a plugins 
(function($) {
	
	/**
	* A class to manages the resources like animations
	**/
	function ResourceManager() {
		this.animations = new Array(); // List of animation / images used in the game
		this.sounds		= new Array(); // List of sounds used in the game
		this.sprites = new Array();    // List of sprites on the game at a the time
		this.callbacks = new Array();  // List of the functions called at each refresh
		this.running = false;		   // State of the game,
		
		this.preload = function(){
			
			for (var i in this.animations){
				this.animations[i].domO = new Image();
				this.animations[i].domO.src = this.animations[i].image;
			}
			
			jQuery.gameQuery.playground.append("<div id='sounds' style='visibility: hidden' width=0 height=0 />");
			
			for (var i in this.sounds){
				$("#sounds").append("<embed src='"+this.sounds[i].url+"' autostart=false id='"+this.sounds[i].name+"'enablejavascript='true' />");
				this.sounds[i].domO = $("#"+this.sounds[i].name)[0]; 
			}
			
			for (i in this.sprites){
				this.sprites[i].height(this.sprites[i].options.height);
				this.sprites[i].width(this.sprites[i].options.width);
				this.sprites[i].css("left", this.sprites[i].options.posx);
				this.sprites[i].css("top", this.sprites[i].options.posy);
				this.sprites[i].css("background-image", "url("+this.sprites[i].options.animation.image+")");
				if(this.sprites[i].options.animation.type=="v") {
					this.sprites[i].css("background-repeat", "repeat-x");
				} else if(this.sprites[i].options.animation.type=="h") {
					this.sprites[i].css("background-repeat", "repeat-y");
				} else {
					this.sprites[i].css("background-repeat", "no-repeat");
				}
				this.sprites[i].css("background-position","0px 0px");
			}
			
			waitForResources();
		}
		
		this.refresh = function(){
			for (var i in this.animations){
				if(this.animations[i].idleCounter == this.animations[i].rate-1){
					this.animations[i].currentFrame = (this.animations[i].currentFrame+1)%this.animations[i].numberOfFrame;
				}
				this.animations[i].idleCounter = (this.animations[i].idleCounter+1)%this.animations[i].rate;
			}
			for (var i in this.sprites){
				if(this.sprites[i].options.animation.numberOfFrame > 1){
					if(this.sprites[i].options.animation.type=="h"){
						this.sprites[i].css("background-position",""+(-this.sprites[i].options.animation.delta*this.sprites[i].options.animation.currentFrame)+"px 0px");
					} else if(this.sprites[i].options.animation.type=="v") {
						this.sprites[i].css("background-position","0px "+(-this.sprites[i].options.animation.delta*this.sprites[i].options.animation.currentFrame)+"px");
					}
				}
			}
			var deadCallback= new Array();
			for (var i in this.callbacks){
				if(this.callbacks[i].idleCounter == this.callbacks[i].rate-1){
					if(this.callbacks[i].fn()){
						deadCallback.push(parseInt(i));
					}
				}
				this.callbacks[i].idleCounter = (this.callbacks[i].idleCounter+1)%this.callbacks[i].rate;
			}
			for (i in deadCallback){
				this.callbacks.splice(deadCallback[i],1);
			}
		}
		
		this.addAnimation = function(animation){
			//normalize the animationRate:
			animation.rate = parseInt(animation.rate/jQuery.gameQuery.refreshRate);
			if(animation.rate==0){
				animation.rate = 1;
			}
			animation.idleCounter = 0;
			this.animations.push(animation);
		}
		
		this.addSprite = function(node, options){
			this.sprites.push(node);
			this.sprites[this.sprites.length-1].options = options;
			if(this.running){
				node.height(options.height);
				node.width(options.width);
				node.css("left", options.posx);
				node.css("top", options.posy);
				node.css("background-image", "url("+options.animation.image+")");
				if(options.animation.type=="v") {
					node.css("background-repeat", "repeat-x");
				} else if(options.animation.type=="h") {
					node.css("background-repeat", "repeat-y");
				} else {
					node.css("background-repeat", "no-repeat");
				}
				node.css("background-position","0px 0px");
			}
		}
		
		this.addSound = function(name, soundUrl){
			this.sounds.push({name: name, url: soundUrl});
		}
		
		this.registerCallback = function(fn, rate){
			rate  = parseInt(rate/jQuery.gameQuery.refreshRate);
			if(rate==0){
				rate = 1;
			}
			this.callbacks.push({fn: fn, rate: rate, idleCounter: 0});
		}
		
		this.setAnimation = function(sprite, animation){
			for(var i in this.sprites){
				if(this.sprites[i][0] == sprite[0]){
					var animationIndex = jQuery.inArray(animation,jQuery.gameQuery.resourceManager.animations);
					if(animationIndex<0){ 	// if the animation is not on the list we add it:
						animationIndex = jQuery.gameQuery.resourceManager.animations.length;
						jQuery.gameQuery.resourceManager.addAnimation(animation);
					}
					this.sprites[i].options.animation = jQuery.gameQuery.resourceManager.animations[animationIndex];
					this.sprites[i].css("background-image", "url("+this.sprites[i].options.animation.image+")");
					this.sprites[i].css("background-position","0px 0px");
				}
			}
		}
		
		return true;
	}
	
	/**
	* Define the div to use for the display the game and initailize it.
	* This could be called on any node it doesn't matter.
	* The returned node is the playground node.
	**/
	jQuery.fn.playground = function(div, options) {
		if(div == undefined){
			return jQuery.gameQuery.playground;
		} else {
			options = jQuery.extend({
				height:		320,
				width:		480,
				refreshRate: 30
			}, options);
			//We save the playground node and set some variable for this node:
			var gameQuery =	{
							playground:			$(div), 
							refreshRate: 		options.refreshRate,
							resourceManager:	new ResourceManager()
							};
			jQuery.extend({	gameQuery: gameQuery});
			
			// We initialize the apearance of the div
			jQuery.gameQuery.playground.css("position","absolute");
			jQuery.gameQuery.playground.css("display", "block");
			jQuery.gameQuery.playground.css("overflow","hidden");
			jQuery.gameQuery.playground.css("border","1px solid black");
			jQuery.gameQuery.playground.height(options.height);
			jQuery.gameQuery.playground.width(options.width);
			
			// We create the sceen graph:
			jQuery.gameQuery.playground.append("<div class='sceengraph' style='visibility: hidden'/>");
			//and we are done!
			return jQuery.gameQuery.playground;
		}
	}
	
	/**
	* Starts the game. The resources from the resource manager are preloaded if necesary 
	* Works only for the playgroung node.
	**/
	jQuery.fn.startGame = function(callback) {
		//if the element is the playground we start the game:
		if(jQuery.gameQuery.playground==this){
			jQuery.gameQuery.startCallback = callback;
			jQuery.gameQuery.resourceManager.preload();
			return this;
		}
	}
	
	/**
	* Add a group to the sceen graph
	* works only on the sceengraph root or on another group
	**/
	jQuery.fn.addGroup = function(group, options) {
		options = jQuery.extend({
			width:		32,
			height:		32,
			posx:		0,
			posy:		0,
			overflow: 	"visible"
		}, options);
		if(this == jQuery.gameQuery.playground){
			this.children(".sceengraph").append("<div id='"+group+"' class='group' style='position: absolute; display: block;' />");
			$("#"+group).css("overflow",options.overflow);
			$("#"+group).css("top",options.posy);
			$("#"+group).css("left",options.posx);
			$("#"+group).height(options.height);
			$("#"+group).width(options.width);
		} else if (this == jQuery.gameQuery.sceengraph){
			
		} else if (this.hasClass("group")){
			$(this).append("<div id='"+group+"' class='group' style='position: absolute; display: block;' />");
			$("#"+group).css("overflow",options.overflow);
			$("#"+group).css("top",options.posy);
			$("#"+group).css("left",options.posx);
			$("#"+group).height(options.height);
			$("#"+group).width(options.width);
		}
		return $("#"+group);
	}
	
	/**
	* Add a sprite to the current node.
	* Works only on the playground, the sceengraph root or a sceengraph group
	**/
	jQuery.fn.addSprite = function(sprite, options) {
		options = jQuery.extend({
			width:		32,
			height:		32,
			posx:		0,
			posy:		0
		}, options);
		if(jQuery.inArray(options.animation,jQuery.gameQuery.resourceManager.animations)<0){
			jQuery.gameQuery.resourceManager.addAnimation(options.animation);
		}
		var added = false;
		if(this == jQuery.gameQuery.playground){
			this.children(".sceengraph").append("<div id='"+sprite+"' style='position: absolute; display: block; overflow: hidden;' />");
			added  = true;
		} else {
			this.append("<div id='"+sprite+"' style='position: absolute; display: block; overflow: hidden;' />");
			added  = true;
		}
		jQuery.gameQuery.resourceManager.addSprite($("#"+sprite), options);
		return this;
	}
	
	/**
	* Changes the animation associated with a sprite.
	* WARNING: no check are made to ensure that the object is really a sprite
	**/
	jQuery.fn.setAnimation = function(animation) {
		jQuery.thisForsetAnimation = this;
		jQuery.gameQuery.resourceManager.setAnimation(this, animation);
		return this;
	}
	
	/**
	* This function add the animation to the resourceManger so that when the loading occures
	* the animation is also loaded. This way no lag will occures at the first use of the animation
	**/
	jQuery.fn.addAnimationToPreload = function(animation) {
		jQuery.gameQuery.resourceManager.addAnimation(animation);
		return this;
	}
	
	/**
	* This function add the sound to the resourceManger for later use.
	**/
	jQuery.fn.addSound = function(name, soundUrl) {
		jQuery.gameQuery.resourceManager.addSound(name, soundUrl);
		return this;
	}
	/**
	* This function plays the sound if it has been loaded before
	**/
	jQuery.fn.playSound = function(loop) {
		if($(this).parent("#sounds").length!=0){
			$(this)[0].Play();
		}
		return this;
	}
	
	/**
	* Register a callback to be trigered every "rate"
	**/
	jQuery.fn.registerCallback = function(fn, rate) {
		jQuery.gameQuery.resourceManager.registerCallback(fn, rate);
		return this;
	}
	
	/**
	* Set the id of the div to use as a loading bar while the games media are loaded during the preload
	**/
	jQuery.fn.setLoadBar = function(elementId, finalwidth) {
		jQuery.gameQuery.loadbar = {id: elementId, width: finalwidth};
		return this;
	}
	
})(jQuery);