/**
*	Animation functionality library.
* 	@author		Pavel Pirogov - NetStudio.ws
*	@date		2011-04-07
*	@license	no license.
*/

var xna= {
	fps:0,
	ups:0,
	time:new Date().getTime(),
	targetUPS:60,
	targetFPS:60,
	updateDelay:1000/60,
	drawDelay:1000/60,
	frameDelay:0,
	upDelay:0,
	upsCor:1,
	updateFn:[],
	drawFn:[],
	timer:new Date().getTime(),
	timer2:new Date().getTime(),
	fpm:60*60,
	upm:60*60,
	opacityProp:'opacity',

	objects:[], //array with all objects
	events:[], //all colide events
	keyEvents:[], //key events
	// end vars
	getWindowSize:function(){
		  var myWidth = 0, myHeight = 0;
		  if( typeof( window.innerWidth ) == 'number' ) {
		    //Non-IE
		    myWidth = window.innerWidth;
		    myHeight = window.innerHeight;
		  } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
		    //IE 6+ in 'standards compliant mode'
		    myWidth = document.documentElement.clientWidth;
		    myHeight = document.documentElement.clientHeight;
		  } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
		    //IE 4 compatible
		    myWidth = document.body.clientWidth;
		    myHeight = document.body.clientHeight;
		  }
		  return {width:myWidth, height:myHeight};
	},
	getBodyScrollTop:function ()
	{
	  return self.pageYOffset || (document.documentElement && document.documentElement.scrollTop) || (document.body && document.body.scrollTop);
	},
	getBodyScrollLeft:function ()
	{
	  return self.pageXOffset || (document.documentElement && document.documentElement.scrollLeft) || (document.body && document.body.scrollLeft);
	},
	// objects
	addObject:function (obj){ //{type,x,y,w,h,o,hp,speed,image,tpl,...draw(),update()}
		index=this.objects.length;
		if(obj.id==null)
			obj.id=index+"_"+Math.round(Math.random()*999)+"_"+Math.round(Math.random()*999);
		obj.pState={};
		this.objects[index]=obj;
		if(typeof(this.objects[index].draw)=="function")
			this.objects[index].draw();
	},
	removeObject:function (obj){
		for(var i = 0; i < this.objects.length; i++){
			if(this.objects[i]==obj){
				var p=document.getElementById(this.objects[i].id).parentNode;
				p.removeChild(document.getElementById(this.objects[i].id));
				this.objects.splice(i, 1);
			}
		}
	},
	getObject:function(id){
		for(var i = 0; i < this.objects.length; i++){
			if(this.objects[i].id==id){
				return this.objects[i];
			}
		}
	},
	getObjects:function(type){
		var res=[];
		for(var i = 0; i < this.objects.length; i++){
			if(this.objects[i].type==type){
				res[res.length]=this.objects[i];
			}
		}
		return res;
	},
	// end objects

	// events
	addColideEvent:function (name,type1,type2,acc,bool,callback){ //type1, type2, accuracy, event
		eventx={type1:type1,type2:type2,acc:acc,bool:bool,callback:callback,name:name};
		this.events.push(eventx);
	},
	removeColideEvent:function (name){
		for(var i = 0; i < this.events.length; i++){
			if(name == this.events[i].name){
				this.events.splice(i, 1);
			}
		}
	},
	// end events

	// key events

	addKeyEvent:function (code,keyDown,keyUp){
		var index=this.keyEvents.length;
		var keyEvent={
			code:code,
			keyDown:keyDown,
			keyUp:keyUp
		};
		this.keyEvents[index]=keyEvent;
	},
	removeKeyEvent:function (code){
		for(var i = 0; i < this.keyEvents.length; i++){
			if(this.keyEvents[i].code==code){
				this.keyEvents.splice(i, 1);
			}
		}
	},
	
	// end key events

	// update
	update:function (){
		var t=new Date().getTime()-this.timer;
		this.ups=1000/(this.updateDelay-this.upDelay+this.timer-this.time);
		this.time=this.timer;
		
		this.upm=this.upm-this.upm/60;
		this.upm+=this.ups;
		this.upm = this.upm? this.upm : this.ups * 60;
		
		if(this.ups){
			this.upsCor=this.targetUPS/this.ups;
		}else
			this.upsCor=1;
		this.ups=Math.round(this.ups);
		
		
		if(this.ups){
			for(var i = 0; i < this.updateFn.length; i++){
				if(typeof(this.updateFn[i])=="function"){
					try{
						this.updateFn[i]();
					}catch(e){
						//out(e);
					}
				}
			}
		}
		
		this.upDelay=this.updateDelay-t;
		if(this.upDelay<0){
			this.upDelay=0;
		}
		this.timer=new Date().getTime();
		setTimeout("xna.update()",this.upDelay);
	},
	setUpdateFn:function (name,fun){
		fun.name = name;
		this.updateFn.push(fun);
	},
	// end update

	// draw
	draw:function (){
		var t=new Date().getTime()-this.timer2;
		this.fps=1000/(this.drawDelay-this.frameDelay+t);
		
		this.fpm=this.fpm-this.fpm/60;
		this.fpm+=this.fps;
		this.fpm = this.fpm? this.fpm : this.fps * 60;
		
		this.fps=Math.round(this.fps);
		
		if(this.ups){
			for(var i = 0; i < this.drawFn.length; i++){
				if(typeof(this.drawFn[i])=="function"){
					try{
						this.drawFn[i]();
					}catch(e){
						//out(e);
					}
				}
			}
		}
		
		this.frameDelay=this.drawDelay-t;
		if(this.frameDelay<0){
			this.frameDelay=0;
		}
		this.timer2=new Date().getTime();
		setTimeout("xna.draw()",this.frameDelay);
	},
	setDrawFn:function (name,fun){
		fun.name = name;
		this.drawFn.push(fun);
	},
	// end draw

	// colide
	testColide:function (object1,object2,accuracy){
		flag=true;
		if(object2.x+accuracy>=object1.x+object1.w){
			return false;
		}else if(object1.x+accuracy>=object2.x+object2.w){
			return false;
		}else if(object2.y+accuracy>=object1.y+object1.h){
			flag=false;
		}else if(object1.y+accuracy>=object2.y+object2.h){
			flag=false;
		}
		return flag;
	},
	testColideLeft:function (object1,object2,accuracy){
		if(object2.x+accuracy>=object1.x+object1.w){
			return false;
		}else
			return true;
	},
	testColideRight:function (object1,object2,accuracy){
		if(object1.x+accuracy>=object2.x+object2.w){
			return false;
		}else
			return true;
	},
	testColideUp:function (object1,object2,accuracy){
		if(object2.y+accuracy>=object1.y+object1.h){
			return false;
		}else
			return true;
	},
	testColideDown:function (object1,object2,accuracy){
		if(object1.y+accuracy>=object2.y+object2.h){
			return false;
		}else
			return true;
	},
	// end colide

	// game start
	gameStart:function (){
		this.opacityProp = this.getOpacityProperty();
		this.time=new Date().getTime();
		this.timer=new Date().getTime();
		this.timer2=new Date().getTime();
		setTimeout("xna.update()",this.updateDelay);
		setTimeout("xna.draw()",this.drawDelay);
		this.setUpdateFn("testColide",function(){
			for(var i = 0; i < xna.objects.length; i++){
				for(var j = 0; j < xna.objects.length; j++){
					if(i!=j){
						for(var k = 0; k < xna.events.length; k++){
							if(xna.events[k].type1==xna.objects[i].type && xna.events[k].type2==xna.objects[j].type){
								
								flag=xna.testColide(xna.objects[i],xna.objects[j],xna.events[k].acc);
								if(flag && xna.events[k].bool || !flag && !xna.events[k].bool){
									if(xna.events[k].callback(xna.objects[i],xna.objects[j]))
										break;
								}
							}
						}
					}
				}
			}
		});
		this.setUpdateFn("updateObjects",function(){
			for(var i = 0; i < xna.objects.length; i++){
				if(typeof(xna.objects[i].update)=="function"){
					try{
						xna.objects[i].update();
					}catch(e){
						//out(e);
					}
					try{
						xna.objects[i].changed=false;
					}catch(e){
						//out(e);
					}
					try{
						for(j in xna.objects[i]){
							if(typeof(xna.objects[i][j])!="function" && j!="pState" && j!="changed"){
								if(xna.objects[i].pState[j]!=xna.objects[i][j])
									xna.objects[i].changed=true;
							}
						}
					}catch(e){
						//out(e);
						//object removed
					}
				}
			}
		});
		this.setDrawFn("redrawObjects",function(){
			for(var i = 0; i < xna.objects.length; i++){
				if(typeof(xna.objects[i].redraw)=="function" && xna.objects[i].changed){
					xna.objects[i].redraw();
					for(j in xna.objects[i]){
						if(typeof(xna.objects[i][j])!="function" && j!="pState" && j!="changed"){
							xna.objects[i].pState[j]=xna.objects[i][j];
						}
					}
				}
			}
		});
	},
	// end game start

	// base functions
	baseDraw:function (elid,obj){
		var el=document.createElement(obj.tag? obj.tag : 'img');
		el.id=obj.id;
		el.style.position="absolute";
		document.getElementById(elid).appendChild(el);
		this.baseRedraw(obj);
	},

	baseRedraw:function (obj){
		try{
			var elem=document.getElementById(obj.id);
			if(obj.y!=null && obj.y!=obj.pState.y)
				elem.style.top=obj.y+"px";
			if(obj.x!=null && obj.x!=obj.pState.x)
				elem.style.left=obj.x+"px";
			if(obj.w!=null && obj.w!=obj.pState.w)
				elem.style.width=obj.w+"px";
			if(obj.h!=null && obj.h!=obj.pState.h)
				elem.style.height=obj.h+"px";
			if(obj.z!=null && obj.z!=obj.pState.z)
				elem.style.zIndex=obj.z;
			if(obj.o!=null && obj.o!=obj.pState.o){
				this.setElementOpacity(obj.id, obj.o);
			}
			if(obj.image!=null && obj.image!=obj.pState.image)
				elem.src=obj.image;
		}catch(e){
			
		}
	},

	setElementOpacity: function (sElemOrId, nOpacity)
	{
		if(typeof(sElemOrId) == "object") {
			var elem = sElemOrId;
		} else {
			var elem = document.getElementById(sElemOrId);
		}
		
	  if (!elem || !this.opacityProp) return false; // Anee ia nouanoaoao yeaiaio n oeacaiiui id eee a?aoca? ia iiaaa??eaaao ie iaei ec ecaanoiuo ooieoee niiniaia oi?aaeaiey i?ic?a?iinou?
	  
	  if (this.opacityProp=="filter")  // Internet Exploder 5.5+
	  {
	    nOpacity *= 100;
	    if(nOpacity < 100)
	    {
	    	var oAlpha = elem.filters['DXImageTransform.Microsoft.alpha'] || elem.filters.alpha;
	    	if (oAlpha) oAlpha.opacity = nOpacity;
	    	else elem.style.filter += "progid:DXImageTransform.Microsoft.Alpha(opacity="+nOpacity+")"; // Для того чтобы не затереть другие фильтры используем "+="
	    }
	  }
	  else
	    elem.style[this.opacityProp] = nOpacity;
	  return true;
	},

	getOpacityProperty:function ()
	{
	  if (typeof document.body.style.opacity == 'string') // CSS3 compliant (Moz 1.7+, Safari 1.2+, Opera 9)
	    return 'opacity';
	  else if (typeof document.body.style.MozOpacity == 'string') // Mozilla 1.6 , Firefox 0.8 
	    return 'MozOpacity';
	  else if (typeof document.body.style.KhtmlOpacity == 'string') // Konqueror 3.1, Safari 1.1
	    return 'KhtmlOpacity';
	  else if (document.body.filters && navigator.appVersion.match(/MSIE ([\d.]+);/)[1]>=5.5) // Internet Exploder 5.5+
	    return 'filter';

	  return false;
	},
	
	rotateElement: function(element, rotationDeg){
		while(rotationDeg >= 360){
			rotationDeg -= 360;
		}
		rotationDeg = rotationDeg == 90 ? 90.001 : rotationDeg == 180 ? 180.001 : rotationDeg == 270 ? 270.001 : rotationDeg == 0 ? 0.001 : rotationDeg;
		var rad = rotationDeg * Math.PI / 180;
		element = typeof(element) == "object"? element : document.getElementById(element);
		if (this.opacityProp == "filter"){
			var matrix = "sizingMethod='auto expand',M11="+Math.cos(rad)+", M12=-"+Math.sin(rad)+",M21="+Math.sin(rad)+", M22="+Math.cos(rad);
			var filter = element.filters['DXImageTransform.Microsoft.Matrix'] || element.filters.matrix;
	    	if (filter) filter.matrix = matrix;
	    	else element.style.filter += "progid:DXImageTransform.Microsoft.Matrix("+matrix+")";
	    }
	    else {
	    	var matrix = 'matrix('+Math.cos(rad)+', '+Math.sin(rad)+', '+(-Math.sin(rad))+', '+Math.cos(rad)+', 0, 0)';
			element.style.MozTransform = matrix;
			element.style.WebkitTransform = matrix;
			element.style.OTransform = matrix;
			element.style.transform = matrix;
		}
	},
	
	direction:function (vx,vy){ //vx is difference between mouseX and object.x
		var x=0,y=0,hx=0,hy=0;
		var angle=Math.atan2(vy, vx);
		vhx=Math.sin(angle);
		vhy=Math.cos(angle);
		if(vy>0 && vx>0){
	    	x=vx/(vy+vx);
	   		y=vy/(vy+vx);
	   		hx=vhx/(vhy+vhx);
	   		hy=-vhy/(vhy+vhx);
	   	}else if(vy<0 && vx<0){
	    	x=-vx/(vy+vx);
	   		y=-vy/(vy+vx);
	   		hx=-vhx/(vhy+vhx);
	   		hy=vhy/(vhy+vhx);
	   	}else if(vy>0 && vx<0){
	    	x=-vx/(vx-vy);
	    	y=-vy/(vx-vy);
	   		hx=vhx/(vhx-vhy);
	   		hy=-vhy/(vhx-vhy);
	   	}else if(vy<0 && vx>0){
	    	x=vx/(vx-vy);
	    	y=vy/(vx-vy);
	   		hx=-vhx/(vhx-vhy);
	   		hy=vhy/(vhx-vhy);
	   	}
		var res={
			angle:angle+Math.PI/2,
			x:x,
			y:y,
			hx:hx,
			hy:hy
		};
		return res;
	}

	// end base functions
};

// "trim" function for IE
if(typeof String.prototype.trim !== 'function') {
  String.prototype.trim = function() {
    return this.replace(/^\s+|\s+$/g, ''); 
  }
}

// "getElementsByClassName" for IE, 
function addGetElementsByClassNameForIE(element){
	if(element){
		if(element.getElementsByClassName == undefined) { 
		   element.getElementsByClassName = function(cl) { 
			  var retnode = []; 
			  var myclass = new RegExp('\\b'+cl+'\\b'); 
			  var elem = this.getElementsByTagName('*'); 
			  for (var i = 0; i < elem.length; i++) { 
				 var classes = elem[i].className; 
				 if (myclass.test(classes)) { 
					retnode.push(elem[i]); 
				 } 
			  } 
			  return retnode; 
		   };
		}
	}
}
addGetElementsByClassNameForIE(document);


// crosbrowser addStartupEvent function
function addStartupEvent(fn){
	if (window.addEventListener)
		window.addEventListener('load', fn, false);
	else
		window.attachEvent('onload', fn);
}

addStartupEvent( function() {
	xna.gameStart();
	document.onkeydown=function(e){
		e=e?e:event;
		for(var i = 0; i < xna.keyEvents.length; i++){
			if(e.keyCode==xna.keyEvents[i].code)
				xna.keyEvents[i].keyDown? xna.keyEvents[i].keyDown() : null;
		}
	}
	document.onkeyup=function(e) {
		e=e?e:event;
		for(var i = 0; i < xna.keyEvents.length; i++){
			if(e.keyCode==xna.keyEvents[i].code)
				xna.keyEvents[i].keyUp? xna.keyEvents[i].keyUp() : null;
		}
	}
});
