var Canvas = {
	canvas:null,
	aiInputHandlerRegistry:[],
	tankRegistry:[],
	rocketRegistry:[],
	time:0,
	right:false,
	left: false,
	init:function()
	{
		this.canvas = $('testCanvas');
		if (this.canvas.getContext)
		{
			this.width = 900;
			this.height = 700;
			this.ctx = this.canvas.getContext("2d");
			this.rate = 1/30;
			
			
			this.createUserTank();
			this.createAITank(600,600,Math.PI,'#cc0000');
			this.createAITank(50,600,1.5*Math.PI,'#0000cc');
			
			
			
			new PeriodicalExecuter(this.refresh,this.rate);
			Event.observe(window,'keydown',this.keydownHandler.bind(this));
			Event.observe(window,'keyup',this.keyupHandler.bind(this));
			
		}
		
	},
	createAITank:function(x,y,rot,col)
	{
		var aiInputHandler = new InputHandler(true);
		this.aiInputHandlerRegistry.push(aiInputHandler);
		var aitank = new Tank(this.ctx,aiInputHandler);
		aitank.setPos(x,y);
		aitank.setRot(rot);
		aitank.setColor(col);
		this.tankRegistry.push(aitank);
	},
	createUserTank:function()
	{
		this.userInputHandler = new InputHandler(false);
		var tank = new Tank(this.ctx,this.userInputHandler);
		this.tankRegistry.push(tank);
		this.userInputHandler.setTank(tank);
			
	},
	updateScore:function()
	{
		$('score').innerHTML = '';
		for (var i=0,ilen=Canvas.tankRegistry.length;i<ilen;i++)
		{
			var tank = Canvas.tankRegistry[i];
			var score = ""+tank.score;
			var legend = Builder.node('span',{},'Tank ' + (i+1) + ': ');
			$(legend).setStyle({color:tank.color});
			$('score').appendChild(Builder.node('div',{},[
				legend,
				Builder.node('span',{},score)
			]));
			
		}
	},
	refresh:function()
	{
		Canvas.clear();
		Canvas.handleAI();
		Canvas.draw();
		Canvas.updateScore();
	},
	clear:function()
	{
		Canvas.ctx.clearRect(0,0,Canvas.width,Canvas.height);
	},
	draw:function()
	{
		for (var i=0,ilen=Canvas.tankRegistry.length;i<ilen;i++)
		{
			var tank = Canvas.tankRegistry[i];
			tank.firer();
			tank.motion();
			tank.draw();
			
		}
		for (var i=0,ilen=Canvas.rocketRegistry.length;i<ilen;i++)
		{
			var rocket = Canvas.rocketRegistry[i];
			if (rocket && rocket.visible)
			{
				rocket.motion();
				rocket.draw();
			}
			else
			{
				Canvas.rocketRegistry.splice(i,1);
			}
		}
		
	},
	handleAI:function()
	{
		for (var i=0,ilen=Canvas.aiInputHandlerRegistry.length;i<ilen;i++)
		{
			var ai = Canvas.aiInputHandlerRegistry[i];
			ai.control();
		}
	},
	keydownHandler:function(evt)
	{
		var key = evt.keyCode;
		switch (key)
		{
			case 39: //right
				this.userInputHandler.right = true;
			break;
			case 37: // left
				this.userInputHandler.left = true;
			break;
			case 38: // up
				this.userInputHandler.up = true;
			break;
			case 40: // down
				this.userInputHandler.down = true;
			break;
			case 32: //space
				this.userInputHandler.fire = true;
			break
		}
		this.killEvent(evt);
	},
	keyupHandler:function(evt)
	{
		var key = evt.keyCode;
		switch (key)
		{
			case 39: //right
				this.userInputHandler.right = false;
			break;
			case 37: // left
				this.userInputHandler.left = false;
			break;
			case 38: // up
				this.userInputHandler.up = false;
			break;
			case 40: // down
				this.userInputHandler.down = false;
			break;
			case 32: //space
				this.userInputHandler.fire = false;
			break;
		}
		this.killEvent(evt);
	},
	killEvent:function(evt)
	{
		var key = evt.keyCode;
		switch (key)
		{
			case 39: //right
			case 37: // left
			case 38: // up
			case 40: // down
			case 32: //space
				Event.stop(evt);
			default:
		}
	},
	getTanksInfo:function()
	{
		var pos = {}
		for(var i=0,ilen=Canvas.tankRegistry.length;i<ilen;i++)
		{
			var tank = Canvas.tankRegistry[i];
			pos[tank.id] = {
				x:tank.xPos,
				y:tank.yPos,
				xv:tank.xVel,
				yv:tank.yVel,
				rot:tank.rot
			};
		}
		return pos
	},
	getDistance:function(x1,y1,x2,y2)
	{
		var xd = x1-x2;
		var yd = y1-y2;
		return Math.sqrt(xd*xd+yd*yd);
	},
	getPhysicsType:function()
	{
		if (!$('physics').checked)
			return 'normal';
		else
			return 'asteroids';
	},
	allowFire:function()
	{
		return $('fire').checked;
	},
	addTank:function()
	{
		var x = Math.ceil(Math.random()*this.width);
		var y = Math.ceil(Math.random()*this.height);
		var rot = Math.ceil(Math.random()*2);
		var color = this.generateRandomColor();
		this.createAITank(x,y,rot*Math.PI,color);
	},
	generateRandomColor:function()
	{
		var s = '#';
		for (var i=0;i<6;i++)
		{
			var x = Math.ceil(Math.random()*16);
			s += x.toString(16);
		}
		return s;
	}

};

var GraphicLibrary = Class.create({
	initialize:function(ctx)
	{
		this.ctx = ctx;
	},
	drawRectangle:function(x,y,w,h,t,c)
	{
		var ctx = this.ctx;
		ctx.fillStyle = c;
		
		var w2 = w/2;
		var h2 = h/2;
		var c = Math.cos(t);
		var s = Math.sin(t);
		var m = {0:c,1:-s,2:s,3:c};
		var x1 = -w2;
		var y1 = -h2;
		var p1 = this.transform(x1,y1,m);
		var x2 = w2;
		var y2 = -h2;
		var p2 = this.transform(x2,y2,m);
		var x3 = w2;
		var y3 = h2;
		var p3 = this.transform(x3,y3,m);
		var x4 = -w2;
		var y4 = h2;
		var p4 = this.transform(x4,y4,m);
		
		ctx.beginPath();
		ctx.moveTo(x+p1.x,y+p1.y);
		ctx.lineTo(x+p2.x,y+p2.y);
		ctx.lineTo(x+p3.x,y+p3.y);
		ctx.lineTo(x+p4.x,y+p4.y);
		ctx.lineTo(x+p1.x,y+p1.y);
		ctx.fill();
		
	},
	transform:function(x,y,m)
	{
		return{x:m[0]*x+m[1]*y,y:m[2]*x+m[3]*y};
	}
	
});
var InputHandler = Class.create({
	left:false,
	right:false,
	up:false,
	down:false,
	fire:false,
	tank:null,
	initialize:function(ai)
	{
		this.ai = ai;
	},
	setTank:function(tank)
	{
		this.tank = tank;
	},
	resetAll:function()
	{
		this.left=false;
		this.right=false;
		this.up=false;
		this.down=false;
		this.fire=false;
	},
	
	control:function()
	{
		if (this.ai)
		{
		
			this.resetAll();
			//find the closest tank and move towards it
			var me = this.tank;
			var myPos = {x:me.xPos,y:me.yPos};
			var others = Canvas.getTanksInfo();
			var minDist = 1000000;
			var closestTank;
			for (var id in others)
			{
				var tank = others[id];
				if (id == me.id)
					continue;
					
				var distance = Canvas.getDistance(myPos.x,myPos.y,tank.x,tank.y);
				if (distance < minDist)
				{
					minDist = distance;
					closestTank = tank;
				}
			}
			var targetX = closestTank.x;
			var targetY = closestTank.y;
			var targetAngle = Math.atan2(targetY-myPos.y,targetX-myPos.x);
			var degree = Math.PI/180;
			var deg2 = 2*degree;
			
			
			
			var diff = targetAngle - me.rot;
			
			if (diff > 1.5*Math.PI)
				targetAngle -= 2*Math.PI;
			if (diff < -1.5*Math.PI)
				targetAngle += 2*Math.PI;
			if (targetAngle > me.rot+deg2)
				this.right = true
			else if (targetAngle < me.rot-deg2)
				this.left = true;
			else
				this.fire = true;
			
			
			if (minDist > 50)
				this.up = true;
			else
				this.down = true;
				
		}
	}
	
});
var Tank = Class.create({
	xVel:0,
	yVel:0,
	xAcc:0,
	yAcc:0,
	xPos:50,
	yPos:50,
	rot:0,
	size: 20,
	acc:.2,
	maxVel:8,
	friction:.05,
	steeringAngle:0,
	fireDelay:20,
	physicsType:'normal',
	initialize:function(ctx,inputHandler)
	{
		this.id = "tank_" + Math.random();
		this.inputHandler = inputHandler;
		this.inputHandler.setTank(this);
		this.ctx = ctx;
		this.gl = new GraphicLibrary(ctx);
		this.fireReady = 0;
		this.score = 0;
		this.color = "#00cc00";
		
	},
	reset:function()
	{
		var min = 0;
		var max = Canvas.width;
		this.xPos = Math.floor(Math.random() * (max - min + 1)) + min;
		max = Canvas.height;
		this.yPos = Math.floor(Math.random() * (max - min + 1)) + min;
		this.xVel = 0;
		this.yVel = 0;
		this.xAcc = 0;
		this.yAcc = 0;
		this.rot = 0;
	},
	setPos:function(x,y)
	{
		this.xPos = x;
		this.yPos = y;
	},
	setRot:function(rot)
	{
		this.rot = rot;
	},
	setColor:function(color)
	{
		this.color = color;
	},
	draw:function()
	{
		var ctx = this.ctx;
		ctx.save();
		
		
		
		var x = this.xPos;
		var y = this.yPos;
		var size = this.size;
		var theta = this.rot;
		
		this.gl.drawRectangle(x,y,size,size,theta,"#000000");
		
		var c = Math.cos(theta);
		var s = Math.sin(theta);
		var m = {0:c,1:-s,2:s,3:c};
		var p1 = this.gl.transform(10,0,m);
		this.gl.drawRectangle(x+p1.x,y+p1.y,size,size-16,theta,"#000000");
		this.gl.drawRectangle(x,y,size+4,size-10,theta,this.color);
	},
	
	firer:function()
	{
		if (this.inputHandler.fire && this.fireReady == 0 && Canvas.allowFire())
		{
			var rocket = new Rocket(this.ctx,this.xPos,this.yPos,this.rot,this);
			Canvas.rocketRegistry.push(rocket);
			this.fireReady = this.fireDelay;
		}
		if (this.fireReady > 0)
		{
			this.fireReady--;
		}
	},
	motion:function()
	{
		var degree = (Math.PI/180);
		var vel = Math.sqrt(this.xVel*this.xVel+this.yVel*this.yVel);
		var negX = this.xVel < 0;
		var negY = this.yVel < 0;
		var yv = Math.tan(this.rot);
		
		if (this.inputHandler.right)
		{
			this.rot+=(5*degree);
			
		}
		else if (this.inputHandler.left)
		{
			this.rot-=(5*degree);
		}
		var threesixty = 2*Math.PI;
		if (this.rot < -Math.PI)
			this.rot += threesixty;
		if (this.rot > Math.PI)
			this.rot -= threesixty;
		if (this.inputHandler.up)
		{
			
			this.xAcc = (this.acc*Math.cos(this.rot));
			this.yAcc = (this.acc*Math.sin(this.rot));
			
		}
		else if (this.inputHandler.down)
		{
			this.xAcc = -(this.acc*Math.cos(this.rot));
			this.yAcc = -(this.acc*Math.sin(this.rot));
		}
		
		if (!this.inputHandler.up && !this.inputHandler.down)
		{
			this.xAcc = 0;
			this.yAcc = 0;
			
			
			if (this.xVel > 0.05)
			{
				this.xAcc -= this.friction;
			}
			else if (this.yVel < -0.05)
			{
				this.xAcc += this.friction;
			}
			if (this.yVel > 0.05)
			{
				this.yAcc -= this.friction;
			}
			else if (this.yVel < -0.05)
			{
				this.yAcc += this.friction;
			}
		}
		
		this.xPos += this.xVel;
		this.yPos += this.yVel;
		
		if (vel > this.maxVel)
			vel = this.maxVel;
		
		if (Canvas.getPhysicsType() == 'normal')
		{
			this.xVel = vel*Math.cos(this.rot) + this.xAcc;
			this.yVel = vel*Math.sin(this.rot) + this.yAcc;
		}
		else
		{
			this.xVel += this.xAcc;
			this.yVel += this.yAcc;
		}
			
		
		//check collision
		if (this.xPos < (0+(this.size/2)))
		{
			this.xPos = this.size/2;
			this.xAcc = 0;
			this.xVel = -.2*this.xVel;
		}
		if (this.yPos < (0+(this.size/2)))
		{
			this.yPos = this.size/2;
			this.yAcc = 0;
			this.yVel = -.2*this.yVel;
		}
		if (this.xPos > (Canvas.width-(this.size/2)))
		{
			this.xPos = Canvas.width-this.size/2;
			this.xAcc = 0;
			this.xVel = -.2*this.xVel;
		}
		if (this.yPos > (Canvas.height-(this.size/2)))
		{
			this.yPos = Canvas.height-this.size/2;
			this.yAcc = 0;
			this.yVel = -.2*this.yVel;
		}
	}
	
});

var Rocket = Class.create({
	xPos:0,
	yPos:0,
	rot:0,
	vel:15,
	visible:true,
	initialize:function(ctx,x,y,t,shooter)
	{
		this.ctx = ctx;
		this.gl = new GraphicLibrary(ctx);
		this.xPos = x;
		this.yPos = y;
		this.rot = t;
		this.shooter = shooter;
		this.xVel = this.vel*Math.cos(this.rot);
		this.yVel = this.vel*Math.sin(this.rot);
	},
	draw:function()
	{
		this.gl.drawRectangle(this.xPos,this.yPos,4,2,this.rot,"#000000");
	},
	motion:function()
	{
		this.xPos+=this.xVel;
		this.yPos+=this.yVel;
		
		
		//check collision
		if (this.xPos < 0 || this.yPos < 0 || this.xPos > Canvas.width || this.yPos > Canvas.height)
		{
			this.visible = false;
		}
		
		for (var i=0,ilen=Canvas.tankRegistry.length;i<ilen;i++)
		{
			var tank = Canvas.tankRegistry[i];
			var id = tank.id;
			if (id == this.shooter.id)
				continue;
			var distance = Canvas.getDistance(this.xPos,this.yPos,tank.xPos,tank.yPos);
			if (distance < 10)
			{
				this.shooter.score++;
				tank.reset();
				this.visible = false;
				
				break;
			}
		}
	}
	
})