// set up application globals
var blockSize = 32;
var cb = document.getElementById('canvas-background');
cb.width = 512;
cb.height = 448;
var ctx = cb.getContext('2d');
var framerate = 60;
var framerateMultiplier = (60/framerate);
var stage = new Stage(2560, cb.height, cb);
stage.gravity = new Vector2D(0, (.6 * framerateMultiplier));
stage.terminalVelocity = new Vector2D(0, 15);
stage.mapOffset = 32;
var showMapBitmaps = true;
var controlKeys = {
	16: { code: 16, alias: "shift",      pressed: false },
	18: { code: 18, alias: "alt",        pressed: false },
	32: { code: 32, alias: "spaceBar",   pressed: false },
	37: { code: 37, alias: "leftArrow",  pressed: false },
	39: { code: 39, alias: "rightArrow", pressed: false }
}
var assets = {
	images: [{ name: 'background', src: 'assets/images/smw_yi1_bg.png' },
			 { name: 'map', 	   src: 'assets/images/smw_yi1_map.png' },
			 { name: 'hero',       src: 'assets/images/smw_mario_sheet.png' }],
	sounds: [{ name: 'jump',       src: 'assets/sounds/smb_jumpsmall.wav' }],
	music:  [{ name: 'bgmusic',    src: 'assets/music/sweet_cake_galaxy.mp3' }]
}

var al = new AssetLoader(assets, function() {
	jQuery.ajax({
		url: 'data/actors/hero.js',
		success: function(data) {
			loadActorSprites(jQuery.parseJSON(data));
		}
	});
});

// create all the actors on the screen
var actor = new Actor();
actor.position = new Vector2D(64, 0);
actor.width = actor.height = 32
actor.hitBox = new HitBox(9, 0, 16, 32);
var maxWalkSpeed = 3;
var hAcceleration = 0.2;
var peakJumpVelocity = -13;
var walkAnimUpdateDefault = 125;
var walkAnimUpdateRun = 250;
var walkAnimUpdate = walkAnimUpdateDefault;

var sprites = {}
var animations = {}

// intialize the game
$(document).ready(function() {
	$(cb).
		attr('tabindex', 1).
		keydown(function(e) {
			try {
				controlKeys[e.which].pressed = true;
			} catch(err) {}
		}).
		keyup(function(e) {
			try {
				controlKeys[e.which].pressed = false;
			} catch (err) {}
		}).
		focus();
		
		jQuery.ajax({
			url: 'data/stages/default.js',
			success: function(data) {
				loadStageData(jQuery.parseJSON(data));
			}
		});
});

function loadActorSprites(data) {
	for (dataType in data) {
		switch (dataType) {
		case 'sprites':
			for (idx in data[dataType]) {
				var spr = data[dataType][idx];
				spr.src = al.assets.images[spr.src];
				sprites[idx] = new Sprite(spr);
			}
			break;
		case 'animations':
			for (idx in data[dataType]) {
				var anim = data[dataType][idx];
				animations[idx] = new SpriteAnimation({ loop: anim.loop });
				for (frame in data[dataType][idx].frames) {
					animations[idx].addFrame(sprites[anim.frames[frame].label], anim.frames[frame].lifetime);
				}
			}
			break;
		}
	}
	
	actor.sprite = sprites.standRight;
	startGame();
}

function loadStageData(data) {
	stage.loadAndParseStageData(data);
	al.loadAssets();
}

function startGame() {
	al.assets.sounds.jump.volume = 0.2;
	al.assets.music.bgmusic.volume = 0.2;
	al.assets.music.bgmusic.play();
	setInterval(main, 1000/framerate);
}

function main() {
	moveActors(actor, stage);
	updateSprites(actor, sprites, animations);
	drawActors(actor, stage);
}

function playSoundClip(sound) {
	if (!sound.ended) {
		sound.pause();
		sound.currentTime = sound.startTime;
	}
	sound.play();
}

function toggleMapBitmaps() {
	showMapBitmaps = !showMapBitmaps;
	$(cb).focus();
}

function moveActors(actor, stage) {
	var pos = actor.getPosition();
	
	if (controlKeys[32].pressed && actor.getVelocityY() == 0) { // jump!
		actor.stepVelocityY(peakJumpVelocity * framerateMultiplier);
		playSoundClip(al.assets.sounds.jump);
	} else { // add gravity to the guy
		actor.stepVelocityY(stage.gravity.y);
		if (actor.getVelocityY() > stage.terminalVelocity.y) {
			actor.setVelocityY(stage.terminalVelocity.y);
		}
	}
	
	// run!
	if (controlKeys[16].pressed) {
		walkAnimUpdate = walkAnimUpdateRun;
		maxWalkSpeed = 5;
	} else {
		walkAnimUpdate = walkAnimUpdateDefault;
		maxWalkSpeed = 3;
	}
	
	if (controlKeys[37].pressed) { // accelerate left
		actor.direction = -1;
		if (actor.getVelocityX() > -maxWalkSpeed * framerateMultiplier) {
			actor.stepVelocityX(-hAcceleration * framerateMultiplier);
			if (actor.getVelocityX() < -maxWalkSpeed * framerateMultiplier) {
				actor.setVelocityX(Math.floor(-maxWalkSpeed * framerateMultiplier));
			}
		} else if (actor.getVelocityX() < -maxWalkSpeed * framerateMultiplier) {
			actor.setVelocityX(Math.floor(-maxWalkSpeed * framerateMultiplier));
		}
	} else if (controlKeys[39].pressed) { // accelerate right
		actor.direction = 1;
		if (actor.getVelocityX() < maxWalkSpeed * framerateMultiplier) {
			actor.stepVelocityX(hAcceleration * framerateMultiplier);
			if (actor.getVelocityX() > maxWalkSpeed * framerateMultiplier) {
				actor.setVelocityX(Math.ceil(maxWalkSpeed * framerateMultiplier));
			}
		} else if (actor.getVelocityX() > maxWalkSpeed * framerateMultiplier) {
			actor.setVelocityX(Math.ceil(maxWalkSpeed * framerateMultiplier));
		}
	} else if (actor.getVelocityX() != 0) { // momentum!
		if (actor.getVelocityX() < 0) {
			actor.stepVelocityX(hAcceleration * framerateMultiplier);
		} else if (actor.getVelocityX() > 0) {
			actor.stepVelocityX(-(hAcceleration * framerateMultiplier));
		}
		if (Math.abs(actor.getVelocityX()) < hAcceleration * framerateMultiplier) {
			actor.setVelocityX(0);
		}
	}
	
	var orig_x = actor.getPositionX();
	var orig_y = actor.getPositionY();
	
	if (actor.getVelocityX() != 0) {
		// adjust all items on the screen
		var didMoveStage = stage.doesMoveStageLeft(actor, cb, actor.getVelocityX()) || stage.doesMoveStageRight(actor, cb, actor.getVelocityX());
		if (didMoveStage) {
			stage.move(actor.getVelocityX());
		}
	
		for (var i = 0; i < stage.bounds.length; i++) {
			var velX = actor.getVelocityX();
			if (didMoveStage) {
				stage.move(velX);
			} else {
				if (velX != 0) {
				
				}
				actor.stepPositionX(velX);
			}
		
			if (actor.collides(stage.bounds[i])) {
				if (velX < 0) { // colliding from the right
					if (didMoveStage) {
						stage.move(stage.bounds[i].getPositionX() + stage.bounds[i].hitBox.x + stage.bounds[i].hitBox.w - (actor.getPositionX() + actor.hitBox.x));
					} else {	
						pos.x = stage.bounds[i].getPositionX() + stage.bounds[i].hitBox.x + stage.bounds[i].hitBox.w - actor.hitBox.x;
					}
				} else if (velX > 0) { // colliding from the left
					if (didMoveStage) {
						stage.move(-((actor.getPositionX() + actor.hitBox.x + actor.hitBox.w) - (stage.bounds[i].getPositionX() + stage.bounds[i].hitBox.x)));
					} else {
						pos.x = stage.bounds[i].getPositionX() - actor.hitBox.w;
					}
				}
				actor.setVelocityX(0);
			}
		
			if (didMoveStage) {
				stage.move(-velX);
			} else {
				actor.setPosition(orig_x, orig_y);
			}
		}
	
		var diff_x = orig_x - pos.x;
		if (diff_x != 0 && didMoveStage) {
			stage.move(diff_x);
		} else {
			actor.position = pos;
		}
	}
	
	// check for vertical collisions
	for (var i = 0; i < stage.bounds.length; i++) {
		var velY = Math.ceil(actor.getVelocityY());
		actor.setPositionY(actor.getPositionY() + velY);
		if (actor.collides(stage.bounds[i])) {
			actor.stepPositionY(-velY);
			if (actor.getVelocityY() < 0) { // colliding from the bottom
				pos.y = stage.bounds[i].getPositionY() + stage.bounds[i].hitBox.y + stage.bounds[i].hitBox.h;
			} else { // colliding from the top
				pos.y = stage.bounds[i].getPositionY() - actor.hitBox.y - actor.hitBox.h;
				/*
				if (stage.bounds[i].active) {
					stage.textParticles.push(stage.bounds[i].textParticle);
					stage.bounds[i].active = false;
					stage.bounds[i].textParticle = null;
				}
				*/
			}
			actor.setVelocityY(0);
		}
		actor.setPosition(orig_x, orig_y);
	}
	
	actor.position = pos;
}

function drawActors(actor, stage) {
	// draw the background
	ctx.drawImage(al.assets.images.background, stage.viewX * 0.7 + stage.mapOffset, 0, cb.width, cb.height, 0, 0, cb.width, cb.height);
	if (showMapBitmaps) {
		ctx.drawImage(al.assets.images.map, stage.viewX + stage.mapOffset, 0, cb.width, cb.height, 0, 0, cb.width, cb.height);
	} else {
		ctx.save();
		ctx.fillStyle = 'rgba(128, 128, 255, 128)';
		for (bound in stage.bounds) {
			ctx.fillRect(stage.bounds[bound].position.x, stage.bounds[bound].position.y, stage.bounds[bound].width, stage.bounds[bound].height);
		}
		ctx.restore();
	}

	// draw the actors
	actor.draw(ctx);
	
	// draw some debug information
	ctx.save();
	ctx.fillStyle = 'rgba(255, 255, 0, 255)';
	ctx.font = "8pt Monaco";
	ctx.fillText("pos: (" + Math.floor(actor.getPositionX()) + "," + Math.floor(actor.getPositionY()) + ")", 2, 15);
	ctx.fillText("vel: (" + Math.floor(actor.getVelocityX()) + "," + Math.floor(actor.getVelocityY()) + ")", 2, 30);
	ctx.fillText("stage.viewPort (x, width): (" + stage.viewX + "," + (stage.viewX + stage.canvas.width) + ")", 122, 15);
	ctx.restore();
}

function updateSprites(actor, sprites, animations) {
	// update the actor's sprite
	var direction = (actor.direction == 1) ? 'Right' : 'Left';
	if (actor.getVelocityY() == 0) {
		if (actor.velocity.x != 0) {
			var slowingRight = actor.direction == 1 && actor.getVelocityX() < 0;
			var slowingLeft = actor.direction == -1 && actor.getVelocityX() > 0;
			if (slowingRight || slowingLeft) {
				actor.sprite = sprites['slow' + ((slowingRight) ? 'Right' : 'Left')];
			} else {
				if (animations['walk' + direction].isPlaying) {
					animations['walk' + direction].update(Math.abs(actor.getVelocityX() * walkAnimUpdate));
				} else {
					animations['walk' + direction].playFromBeginning();
				}
				actor.sprite = animations['walk' + direction].currentFrame.sprite;
			}
		} else {
			if (animations['walk' + direction].isPlaying) {
				animations['walk' + direction].stop();
			}
			actor.sprite = sprites['stand' + direction];
		}
	} else {
		actor.sprite = sprites[((actor.getVelocityY() < 0) ? 'air' : 'drop') + direction];
	}
}

