// Set dimensions
var _height, _width, _margin = 20, _aspect = 16/9, _minw = 640, _minh = _minw/_aspect;
if (window.innerWidth > window.innerHeight) {
	_height = Math.max(_minh, window.innerHeight - 2*_margin);
	_width = _height*_aspect;
}
else {
	_width = Math.max(_minw, window.innerWidth - 2*_margin);
	_height = _width/_aspect;
}




// Game constants
var constants = {
	SCREEN_DIM: [_width, _height],
	SCREEN_CENTER: [_width/2, _height/2],
	
	BG_COLOR: '#223322',
	
	WORLD_RADIUS: _height/4,
	WORLD_POS: [_width/2, _height/2],
	WORLD_CONTOUR: 5,
	WORLD_CONTOUR_COL: '#003300',
	ZERO_POTENSIAL_RADIUS: _height*0.6,
	
	PLAYER_SPEED: Math.PI/20, // rad/s
	BARREL_SPEED: Math.PI/10,
	BARREL_MAX: Math.PI/3,
	BARREL_MIN: -Math.PI/3,
	
	MISSILE_RADIUS: 5,
	MISSILE_LOAD_SPEED: 100
};

// Game states
var state = {
	LOADING: 1,
	PLAY: 2,
	P_CHANGE: 3,
	PRE_FIRE: 4,
	FIRE: 5
};

// Event manager
var keys_down = {};
addEventListener('keydown', function (e) {
	keys_down[e.keyCode] = true;
}, false);
addEventListener('keyup', function (e) {
	delete keys_down[e.keyCode];
}, false);


// Assets. Preload
var assets_loaded = 0;
function assetLoadCB() {
	assets_loaded++;
}
var assets = {
	space_bg: new Image(),
	evil_player: new Image(),
	green_player: new Image(),
	evil_barrel: new Image(),
	green_barrel: new Image()
};
assets.space_bg.src = 'images/space_bg.jpg'; assets.space_bg.onload = function() {assetLoadCB();};
assets.evil_player.src = 'images/evil.png'; assets.evil_player.onload = function() {assetLoadCB();};
assets.green_player.src = 'images/green.png'; assets.green_player.onload = function() {assetLoadCB();};
assets.evil_barrel.src = 'images/gun.png'; assets.evil_barrel.onload = function() {assetLoadCB();};
assets.green_barrel.src = 'images/gun.png'; assets.green_barrel.onload = function() {assetLoadCB();};


// Timer
function Timer() {}

Timer.prototype.isset = function() {
	return this.start !== 'undefined' && this.interval !== 'undefined';
}

Timer.prototype.set = function(msec) {
	this.start = Date.now();
	this.interval = msec;
}

Timer.prototype.finished = function() {
	return ((Date.now() - this.start) > this.interval)
}




function Polar(origo, r0, theta0) {
	this.origo = origo;
	this.r = r0;
	this.theta = theta0;
}

Polar.prototype.euler = function(rp, thetap) {
	var add_r = (typeof rp === 'undefined') ? 0:rp,
		add_theta = (typeof thetap === 'undefined') ? 0:thetap;
	return [this.origo[0] + (this.r + add_r)*Math.cos(this.theta + add_theta), this.origo[1] - (this.r + add_r)*Math.sin(this.theta + add_theta)]
}


function Missile(ctx, polar0, color) {
	this.fired = false;
	this.ctx = ctx;
	
	this.polar = polar0;
	this.polar_v = new Polar(constants.WORLD_POS, 0, 0);
	this.v0 = 0;
	this.color = color;
}

Missile.prototype.fire = function(gun_theta) {
	this.polar_v.r = Math.cos(gun_theta) * this.v0;
	this.polar_v.theta = Math.sin(gun_theta) * (Math.PI/180) * this.v0;
	console.log(this.polar_v.r + ' ' + this.polar_v.theta);
	this.fired = true;
}

Missile.prototype.outOfBounds = function() {
	return (this.euler[0] > constants.SCREEN_DIM[0] || this.euler[0] < 0 || this.euler[1] > constants.SCREEN_DIM[1] || this.euler[1] < 0);
}

Missile.prototype.checkCollision = function() {
	return (this.polar.r < constants.WORLD_RADIUS);
}


function getGravity(r) {
	var dr = r - (constants.WORLD_RADIUS);
	return -10000000/(r*r); // Empirical
}

Missile.prototype.update = function(delta) {
	this.polar_v.r += getGravity(this.polar.r) * delta;
	this.polar.r += this.polar_v.r * delta;
	this.polar.theta += this.polar_v.theta * delta;
	this.euler = this.polar.euler();
}

Missile.prototype.draw = function() {
	drawFilledCircle(this.ctx, this.euler, constants.MISSILE_RADIUS, 0, 2*Math.PI, this.color, 1, 'black');
}


function Player(ctx, is_evil, polar) {
	this.ctx = ctx;
	this.polar = polar;
	this.barrel_theta = 0;
	
	// Load images
	this.image = (is_evil) ? assets.evil_player:assets.green_player;
	this.barrel_img = (is_evil) ? assets.evil_barrel:assets.green_barrel;
	
	//center
	//console.log(this.image.height);
	this.polar.theta += this.image.height/(4*Math.PI*constants.WORLD_RADIUS); // simplify, use arcsin.
	this.polar.r += this.image.width/2;
	this.euler = this.polar.euler();
}

Player.prototype.moveBarrel = function(modifier, delta) {
	if (!((this.barrel_theta > constants.BARREL_MAX && modifier == 1) ||
		(this.barrel_theta < constants.BARREL_MIN && modifier == -1))) {
		this.barrel_theta += modifier*constants.BARREL_SPEED*delta;
	}
}

Player.prototype.move = function(modifier, delta) {
	this.polar.theta += modifier*constants.PLAYER_SPEED*delta;
	this.euler = this.polar.euler();
}

Player.prototype.drawBarrel = function() {
	drawRotatedImage(this.ctx, this.barrel_img, this.polar.euler(this.barrel_img.width + 5), [this.barrel_img.width, this.barrel_img.height], this.polar.theta + this.barrel_theta);
}

Player.prototype.draw = function() {
	drawRotatedImage(this.ctx, this.image, this.euler, [this.image.width, this.image.height], this.polar.theta);
	this.drawBarrel();
}




var sector_cols = {
	'land': '#8B4513',
	'water': '#104E8B',
	'ice': '#E0FFFF',
	'forest': '#228B22'
};

function Sector(ctx, type, theta0, theta) {
	this.ctx = ctx;
	
	this.type = type;
	this.theta0 = theta0;
	this.theta = theta;
	
	this.health = 0; //[-1, 1]
}

Sector.prototype.hit = function(h) {
	if (Math.abs(this.health) === 1 && this.type !== 'ice') {
		return; //sector is locked
	}	
	this.health += h;
	this.health = (this.health < -1) ? -1:this.health;
	this.health = (this.health > 1) ? 1:this.health;
}

Sector.prototype.draw = function() {
	drawFilledCone(
		this.ctx,
		constants.WORLD_POS,
		constants.WORLD_RADIUS,
		this.theta0, this.theta,
		sector_cols[this.type]
	);
	
	if (this.health !== 0) {
		drawFilledCone(
			this.ctx,
			constants.WORLD_POS,
			constants.WORLD_RADIUS,
			this.theta0, this.theta,
			(this.health < 0) ? ('rgba(255, 0, 0, ' + (-this.health) + ')'):('rgba(0, 255, 0, ' + (this.health) + ')')
		);
	}
}





function Game() {
	console.log('state LOADING');
	this.state = state.LOADING;
	
	// Initialize canvases
	this.bg_canvas = document.getElementById('layer1');
	this.g_canvas = document.getElementById('layer2');
	this.hud_canvas = document.getElementById('layer3');
	
	this.bg_canvas.width = this.g_canvas.width = this.hud_canvas.width = constants.SCREEN_DIM[0];
	this.bg_canvas.height = this.g_canvas.height = this.hud_canvas.height = constants.SCREEN_DIM[1];
	
	this.bg_ctx = this.bg_canvas.getContext('2d'); // BG and world
	this.draw_bg = true; // Save some resources. Background is pretty static. 
	
	this.g_ctx = this.g_canvas.getContext('2d'); // Game layer. Always draw
	
	this.hud_ctx = this.hud_canvas.getContext('2d'); // Menu and stats
	this.draw_hud = true; // Also pretty static

}

function randRange(lower, upper) {
	return  lower + (upper - lower)*Math.random();
}


Game.prototype.init = function() {
	// Instantiate players
	this.players = [
		new Player(this.g_ctx, true, new Polar(constants.WORLD_POS, constants.WORLD_RADIUS, Math.PI/2)),
		new Player(this.g_ctx, false, new Polar(constants.WORLD_POS, constants.WORLD_RADIUS, Math.PI/2))
	];
	this.curr_p = 0; // randomize later
	this.p_change = new Timer();
	
	// Init sectors
	var sec_borders = [],
		slack = 2*Math.PI/20,
		ice_width = 2*Math.PI/20,
		sec_width = 2*Math.PI/7;
	
	sec_borders.push(0);
	sec_borders.push(randRange(sec_width - slack, sec_width + slack));
	sec_borders.push(randRange(2*sec_width - slack, 2*sec_width + slack));
	sec_borders.push(randRange(3*sec_width - slack, 3*sec_width + slack));
	sec_borders.push(randRange(4*sec_width - slack, 4*sec_width + slack));
	sec_borders.push(randRange(5*sec_width - slack, 5*sec_width + slack));
	sec_borders.push(sec_borders[5] + ice_width);
	sec_borders.push(2*Math.PI);
	
	this.sectors = [
		new Sector(this.bg_ctx, 'land', sec_borders[0], sec_borders[1]),
		new Sector(this.bg_ctx, 'water', sec_borders[1], sec_borders[2]),
		new Sector(this.bg_ctx, 'land', sec_borders[2], sec_borders[3]),
		new Sector(this.bg_ctx, 'forest', sec_borders[3], sec_borders[4]),
		new Sector(this.bg_ctx, 'water', sec_borders[4], sec_borders[5]),
		new Sector(this.bg_ctx, 'ice', sec_borders[5], sec_borders[6]),
		new Sector(this.bg_ctx, 'forest', sec_borders[6], sec_borders[7])
	];
}

Game.prototype.loadAssets = function() {
	this.g_ctx.save();
	this.g_ctx.font = '20px Arial';
	this.g_ctx.fillStyle = 'red';
	this.g_ctx.fillText('Loading...', constants.SCREEN_CENTER[0] - 40, constants.SCREEN_DIM[1] - 40);
	this.g_ctx.restore();
	
	if (assets_loaded < Object.keys(assets).length) {
		return state.LOADING;
	}
	else {
		console.log('state PLAY');
		this.init();
		return state.PLAY;
	}
}

Game.prototype.drawWorld = function() {
	drawFilledCircle(
		this.bg_ctx,
		constants.WORLD_POS,
		constants.ZERO_POTENSIAL_RADIUS,
		0, 2*Math.PI,
		'rgba(255, 255, 255, 0.1)'
	);
	drawFilledCircle(
		this.bg_ctx,
		constants.WORLD_POS,
		constants.WORLD_RADIUS,
		0, 2*Math.PI,
		'white'
	);
}

Game.prototype.drawBGLayer = function() {
	if (this.draw_bg) {
		this.bg_ctx.clearRect(0, 0, constants.SCREEN_DIM[0], constants.SCREEN_DIM[1]);
		var x = y = 0;
		while (x < constants.SCREEN_DIM[0]) {
			while (y < constants.SCREEN_DIM[1]) {
				this.bg_ctx.drawImage(assets.space_bg, x, y);
				y += assets.space_bg.height;
			}
			y = 0
			x += assets.space_bg.width;
		}
		this.drawWorld();
		this.draw_bg = false;
	}
}

Game.prototype.drawGLayer = function() {
	this.g_ctx.clearRect(0, 0, constants.SCREEN_DIM[0], constants.SCREEN_DIM[1]);
	// Draw sectors
	for (var i=0; i<this.sectors.length; i++) {
		this.sectors[i].draw();
	}
	// Draw player
	this.players[this.curr_p].draw();
}

Game.prototype.changePlayer = function() {
	if (this.p_change.finished()) {
		this.curr_p = (this.curr_p === 0) ? 1:0;
		console.log('state PLAY');
		this.state = state.PLAY;
	}
	else {
		var next = (this.curr_p === 0) ? 2:1;
		this.g_ctx.clearRect(0, 0, constants.SCREEN_DIM[0], constants.SCREEN_DIM[1]);
		this.g_ctx.save();
		this.g_ctx.font = '20px Arial';
		this.g_ctx.fillStyle = (next === 1) ? 'red':'green';
		this.g_ctx.fillText(('Player ' + next), constants.SCREEN_CENTER[0] - 40, constants.SCREEN_DIM[1] - 40);
		this.g_ctx.restore();
	}
}

Game.prototype.preFire = function(delta) {
	if (keys_down[32]) {
		this.missile.v0 += constants.MISSILE_LOAD_SPEED * delta;
	}
	else {
		this.missile.fire(this.players[this.curr_p].barrel_theta);
		console.log('state FIRE');
		this.state = state.FIRE;
	}
}

Game.prototype.sectorHit = function() {
	var i = 0,
		_theta = this.missile.polar.theta,
		_modifier = (this.curr_p === 0) ? -1:1;
	
	while (_theta < 0) {
		_theta += 2*Math.PI;
	}
	while (_theta > 2*Math.PI) {
		_theta -= 2*Math.PI;
	}	
	while (_theta > this.sectors[i].theta) {
		i++;
	}
	console.log('Hit sector ' + i + ', theta = [' + this.sectors[i].theta0 + ',' + this.sectors[i].theta);
	console.log('Missile theta ' + _theta);
	this.sectors[i].hit(randRange(0.2, 0.8) * _modifier);
	//this.draw_bg = true;
}

Game.prototype.missileFire = function(delta) {
	this.missile.update(delta);
	if (this.missile.checkCollision()) {
		this.sectorHit();
		console.log('state PLAY');
		this.state = state.PLAY;
	}
	else if (this.missile.outOfBounds()) {
		console.log('state PLAY');
		this.state = state.PLAY;
	}
	else {
		this.missile.draw();
	}
}

Game.prototype.playEventHandler = function(delta) {
	//space
	if (keys_down[32]) {
		this.missile = new Missile(
			this.g_ctx,
			new Polar(constants.WORLD_POS, this.players[this.curr_p].polar.r + this.players[this.curr_p].image.width, this.players[this.curr_p].polar.theta),
			(this.curr_p === 0) ? 'red':'green');
		console.log('state PRE_FIRE');
		this.state = state.PRE_FIRE;
	}
	//enter
	if (keys_down[13]) {
		this.p_change.set(1000);
		console.log('state P_CHANGE');
		this.state = state.P_CHANGE;
	} 
	//left
	if (keys_down[37]) {
		this.players[this.curr_p].move(1, delta);
	}
	//right
	if (keys_down[39]) {
		this.players[this.curr_p].move(-1, delta);
	}
	//down
	if (keys_down[40]) {
		this.players[this.curr_p].moveBarrel(1, delta);
	}
	//up
	if (keys_down[38]) {
		this.players[this.curr_p].moveBarrel(-1, delta);
	}
}

Game.prototype.mainLoop = function() {
	var now = Date.now(),
		delta = (now - this.then)/1000;
		
	switch (this.state) {
		case state.LOADING:
			this.state = this.loadAssets();
			break;
		
		case state.PLAY:
			this.playEventHandler(delta);
			// more logic
			this.drawBGLayer();
			this.drawGLayer();
			break;
		
		case state.P_CHANGE:
			this.drawBGLayer();
			this.changePlayer();
			break;
		
		case state.PRE_FIRE:
			this.drawBGLayer();
			this.drawGLayer();
			this.preFire(delta);
			break;
		
		case state.FIRE:
			this.drawBGLayer();
			this.drawGLayer();
			this.missileFire(delta);
			break;
		
		default:
			this.drawBGLayer();
	}
	
	this.g_ctx.fillText('FPS: ' + parseInt(1/delta), 10, 10); // Show FPS
	this.then = now;
}

Game.prototype.run = function() {
	//game start
	this.drawWorld();
	
	var self = this; // Alias this. stupid setInterval
	this.then = Date.now();
	setInterval(function() {
		self.mainLoop();
	}, 1);
}


/*
 * Helpers
 * */
function drawColorRect(ctx, pos, dims, col) {
	ctx.save();
	ctx.fillStyle = col;
	ctx.beginPath();
	ctx.rect(pos[0], pos[1], dims[0], dims[1]);
	ctx.closePath();
	ctx.fill();
	ctx.restore();
}

function drawFilledCone(ctx, pos, r, theta0, theta, fillcol, linewidth, linecol) {
	ctx.save();
	ctx.beginPath();
	ctx.arc(pos[0], pos[1], r, -theta, -theta0, false);
	ctx.lineTo(pos[0], pos[1]);
	ctx.fillStyle = fillcol;
	ctx.fill();
	if (linewidth !== 'undefined') {
		ctx.lineWidth = linewidth;
		ctx.strokeStyle = (linecol === 'undefined') ? 'white':linecol;
		ctx.stroke();
	}
	ctx.restore();
}

function drawFilledCircle(ctx, pos, r, theta0, theta, fillcol, linewidth, linecol) {
	ctx.save();
	ctx.beginPath();
	ctx.arc(pos[0], pos[1], r, -theta, -theta0, false);
	ctx.fillStyle = fillcol;
	ctx.fill();
	if (linewidth !== 'undefined') {
		ctx.lineWidth = linewidth;
		ctx.strokeStyle = (linecol === 'undefined') ? 'white':linecol;
		ctx.stroke();
	}
	ctx.restore();
}

function drawRotatedImage(ctx, image, origo, dims, theta) {
	ctx.save();
	ctx.translate(origo[0], origo[1]);
	ctx.rotate(-theta);
	ctx.drawImage(image, -dims[0]/2, -dims[1]/2);
	ctx.restore();
}


var game = new Game();
game.run();


