/**
 * Author: Erik Rasmussen
 * URL: http://www.erik-rasmussen.com/blog/2007/05/21/javascript-gunner/
 */
var Gunner = function()
{
	this.initialize.apply(this, arguments);
}
Gunner.prototype =
{
	initialize: function(size, color)
	{
		this.canvas = document.createElement('canvas');
		if (this.canvas.getContext)
		{
			this.canvas.setAttribute('width', size);
			this.canvas.setAttribute('height', size);
			document.body.appendChild(this.canvas);
			this.context = this.canvas.getContext('2d');
			this.bullets = [];
			var scrollInfo = this.getScrollInfo();
			this.position = {x: Math.random() + scrollInfo.width, y: Math.random() * scrollInfo.height};
			this.edge = ['top', 'right', 'bottom', 'left'][Math.floor(Math.random() * 4)];
			this.canvas.style.position = 'fixed';
			this.direction = Math.PI / 2;
			this.barrelWidth = this.canvas.width / 10;
			this.barrelLength = this.canvas.height / 2;
			if (!color)
				color = 'black';
			this.color = color;
			this.observe(document, 'mousemove', function(event)
			{
				var scrollInfo = this.getScrollInfo();
				this.mouse = { x: this.pointerX(event) - scrollInfo.left, y: this.pointerY(event) - scrollInfo.top };
			}.bindAsEventListener(this));
			this.draw();
			this.changeSpeed();
			window.setInterval(this.move.bind(this), 50);
			window.setInterval(this.fire.bind(this), 2000);
		}
	},

	changeSpeed: function()
	{
		this.speed = Math.random() > 0.5 ? 3 : -3;
		window.setTimeout(this.changeSpeed.bind(this), 5000);
	},

	fire: function()
	{
		if (this.gunTip && this.mouse)
		{
			this.bullets.push(new Bullet(this.gunTip.x, this.gunTip.y, this.barrelWidth, this.direction, this.color));
		}
	},

	move: function()
	{
		var scrollInfo = this.getScrollInfo();
		switch (this.edge)
				{
			case 'top':
				this.position.y = 0;
				this.position.x += this.speed;
				if (this.position.x - this.canvas.width / 4 < 0)
				{
					this.edge = 'left';
					this.position.x = 0;
					this.position.y = this.canvas.height / 4;
				}
				else if (this.position.x + (this.canvas.width / 4) > scrollInfo.width)
				{
					this.edge = 'right';
					this.canvas.style.left = null;
					this.position.y = this.canvas.height / 4;
				}
				break;
			case 'right':
				this.position.x = scrollInfo.width;
				this.position.y += this.speed;
				if (this.position.y - this.canvas.height / 4 < 0)
				{
					this.edge = 'top';
					this.canvas.style.right = null;
					this.position.x = scrollInfo.width - (this.canvas.width / 4);
					this.position.y = 0;
				}
				else if (this.position.y + (this.canvas.height / 4) > scrollInfo.height)
				{
					this.edge = 'bottom';
					this.canvas.style.top = null;
					this.canvas.style.right = null;
					this.position.x = scrollInfo.width - (this.canvas.width / 4);
				}
				break;
			case 'bottom':
				this.position.y = scrollInfo.height;
				this.position.x -= this.speed;
				if (this.position.x - this.canvas.width / 4 < 0)
				{
					this.edge = 'left';
					this.canvas.style.bottom = null;
					this.position.x = 0;
					this.position.y = scrollInfo.height - (this.canvas.height / 4);
				}
				else if (this.position.x + (this.canvas.width / 4) > scrollInfo.width)
				{
					this.edge = 'right';
					this.canvas.style.left = null;
					this.canvas.style.bottom = null;
					this.position.y = scrollInfo.height - (this.canvas.height / 4);
				}
				break;
			case 'left':
				this.position.x = 0;
				this.position.y -= this.speed;
				if (this.position.y - this.canvas.height / 4 < 0)
				{
					this.edge = 'top';
					this.position.x = this.canvas.width / 4;
					this.position.y = 0;
				}
				else if (this.position.y + (this.canvas.height / 4) > scrollInfo.height)
				{
					this.edge = 'bottom';
					this.canvas.style.top = null;
					this.position.x = this.canvas.width / 4;
				}
				break;
		}
		var newBullets = [];
		for (var i = 0; i < this.bullets.length; i++)
		{
			if (this.bullets[i].move(scrollInfo))
				newBullets.push(this.bullets[i]);
		}
		this.bullets = newBullets;
		this.draw();
	},

	observe: function(element, name, observer)
	{
		if (element.addEventListener)
			element.addEventListener(name, observer, false);
		else if (element.attachEvent)
			element.attachEvent('on' + name, observer);
	},

	draw: function()
	{
		this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
		if (this.mouse)
		{
			var diffX = this.mouse.x - this.position.x;
			var diffY = this.mouse.y - this.position.y;
		}
		var width = new Number(this.canvas.width);
		var height = new Number(this.canvas.height);
		var halfWidth = width / 2;
		var halfHeight = height / 2;
		switch (this.edge)
				{
			case 'top':
				if (this.mouse)
				{
					this.direction = Math.atan(Math.abs(diffY) / Math.abs(diffX));
					if (diffX < 0)
						this.direction = Math.PI - this.direction;
				}
				else
					this.direction = Math.PI / 2;
				this.canvas.style.top = '0';
				this.canvas.style.left = (this.position.x - halfWidth) + 'px';
				this.context.beginPath();
				this.context.arc(halfWidth, 0, halfWidth / 2, 0, Math.PI, false);
				this.context.fillStyle = this.color;
				this.context.fill();
				this.context.beginPath();
				this.context.lineWidth = this.barrelWidth;
				this.context.moveTo(halfWidth, 0);
				this.context.lineTo(halfWidth + Math.cos(this.direction) * this.barrelLength, Math.sin(this.direction) * this.barrelLength);
				this.context.strokeStyle = this.color;
				this.context.stroke();
				this.gunTip = { x: this.position.x + Math.cos(this.direction) * this.barrelLength, y: Math.sin(this.direction) * this.barrelLength };
				break;
			case 'right':
				if (this.mouse)
					this.direction = Math.atan(diffY / diffX) + Math.PI;
				else
					this.direction = Math.PI;
				this.canvas.style.right = '0';
				this.canvas.style.top = (this.position.y - halfHeight) + 'px';
				this.context.beginPath();
				this.context.arc(width, halfHeight, halfHeight / 2, Math.PI / 2, 3 * Math.PI / 2, false);
				this.context.fillStyle = this.color;
				this.context.fill();
				this.context.beginPath();
				this.context.lineWidth = this.barrelWidth;
				this.context.moveTo(width, halfHeight);
				this.context.lineTo(width + (Math.cos(this.direction) * this.barrelLength), halfHeight + Math.sin(this.direction) * this.barrelLength);
				this.context.strokeStyle = this.color;
				this.context.stroke();
				this.gunTip = { x: this.position.x + Math.cos(this.direction) * this.barrelLength, y: this.position.y + Math.sin(this.direction) * this.barrelLength };
				break;
			case 'bottom':
				if (this.mouse)
				{
					this.direction = Math.PI + Math.atan(Math.abs(diffY) / Math.abs(diffX));
					if (diffX > 0)
						this.direction = Math.PI - this.direction;
				}
				else
					this.direction = -Math.PI / 2;
				this.canvas.style.bottom = '0';
				this.canvas.style.left = (this.position.x - halfWidth) + 'px';
				this.context.beginPath();
				this.context.arc(halfWidth, height, halfWidth / 2, 0, Math.PI, true);
				this.context.fillStyle = this.color;
				this.context.fill();
				this.context.beginPath();
				this.context.lineWidth = this.barrelWidth;
				this.context.moveTo(halfWidth, height - 1);
				this.context.lineTo(halfWidth + Math.cos(this.direction) * this.barrelLength, height + Math.sin(this.direction) * this.barrelLength);
				this.context.strokeStyle = this.color;
				this.context.stroke();
				this.gunTip = { x: this.position.x + Math.cos(this.direction) * this.barrelLength, y: this.position.y + Math.sin(this.direction) * this.barrelLength };
				break;
			case 'left':
				if (this.mouse)
				{
					this.direction = Math.atan(diffY / diffX);
					if (diffX < 0)
						this.direction = Math.PI - this.direction;
				}
				else
					this.direction = 0;
				this.canvas.style.left = '0';
				this.canvas.style.top = (this.position.y - halfHeight) + 'px';
				this.context.beginPath();
				this.context.arc(0, halfHeight, halfHeight / 2, Math.PI / 2, 3 * Math.PI / 2, true);
				this.context.fillStyle = this.color;
				this.context.fill();
				this.context.beginPath();
				this.context.lineWidth = this.barrelWidth;
				this.context.moveTo(0, halfHeight);
				this.context.lineTo(Math.cos(this.direction) * this.barrelLength, halfHeight + Math.sin(this.direction) * this.barrelLength);
				this.context.strokeStyle = this.color;
				this.context.stroke();
				this.gunTip = { x: Math.cos(this.direction) * this.barrelLength, y: this.position.y + Math.sin(this.direction) * this.barrelLength };
				break;
		}
	},

	pointerX: function(event)
	{
		return event.pageX || (event.clientX +
		                       (document.documentElement.scrollLeft || document.body.scrollLeft));
	},

	pointerY: function(event)
	{
		return event.pageY || (event.clientY +
		                       (document.documentElement.scrollTop || document.body.scrollTop));
	},

	getScrollInfo: function()
	{
		var T, L, W, H;
		with (window.document)
		{
			if (window.document.documentElement && documentElement.scrollTop)
			{
				T = documentElement.scrollTop;
				L = documentElement.scrollLeft;
			}
			else if (window.document.body)
			{
				T = body.scrollTop;
				L = body.scrollLeft;
			}
			if (window.innerWidth)
			{
				W = window.innerWidth;
				H = window.innerHeight;
			}
			else if (window.document.documentElement && documentElement.clientWidth)
			{
				W = documentElement.clientWidth;
				H = documentElement.clientHeight;
			}
			else
			{
				W = body.offsetWidth;
				H = body.offsetHeight
			}
		}
		return { top: T, left: L, width: W, height: H };
	}
}
var Bullet = function()
{
	this.initialize.apply(this, arguments);
}
Bullet.prototype =
{
	initialize: function(x, y, size, angle, color)
	{
		this.x = x;
		this.y = y;
		this.size = size;
		this.angle = angle;
		this.speed = 4;
		this.canvas = document.createElement('canvas');
		this.canvas.setAttribute('width', size);
		this.canvas.setAttribute('height', size);
		document.body.appendChild(this.canvas);
		this.context = this.canvas.getContext("2d");
		this.canvas.style.position = 'fixed';
		this.canvas.style.left = (this.x - this.size / 2) + 'px';
		this.canvas.style.top = (this.y - this.size / 2) + 'px';
		this.context.arc(this.size / 2, this.size / 2, this.size / 2, 0, 2 * Math.PI, false);
		this.context.fillStyle = color;
		this.context.fill();
	},

	move: function(scrollInfo)
	{
		if (this.canvas)
		{
			this.x += this.speed * Math.cos(this.angle);
			this.y += this.speed * Math.sin(this.angle);
			if (this.x > 0 && this.x + this.size < scrollInfo.width && this.y > 0 && this.y + this.size < scrollInfo.height)
			{
				this.canvas.style.left = (this.x - this.size / 2) + 'px';
				this.canvas.style.top = (this.y - this.size / 2) + 'px';
				return true;
			}
			else
			{
				this.canvas.parentNode.removeChild(this.canvas);
				this.canvas = null;
				return false;
			}
		}
	}
}

var $A = Array.from = function(iterable)
{
	if (!iterable) return [];
	if (iterable.toArray)
	{
		return iterable.toArray();
	}
	else
	{
		var results = [];
		for (var i = 0; i < iterable.length; i++)
			results.push(iterable[i]);
		return results;
	}
}

Function.prototype.bind = function()
{
	var __method = this, args = $A(arguments), object = args.shift();
	return function()
	{
		return __method.apply(object, args.concat($A(arguments)));
	}
}

Function.prototype.bindAsEventListener = function(object)
{
	var __method = this;
	return function(event)
	{
		return __method.call(object, event || window.event);
	}
}


