<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>GBA.js</title>
<link rel="stylesheet" href="resources/main.css">
<script src="js/util.js"></script>
<script src="js/core.js"></script>
<script src="js/arm.js"></script>
<script src="js/thumb.js"></script>
<script src="js/mmu.js"></script>
<script src="js/io.js"></script>
<script src="js/audio.js"></script>
<script src="js/video.js"></script>
<script src="js/video/proxy.js"></script>
<script src="js/video/software.js"></script>
<script src="js/irq.js"></script>
<script src="js/keypad.js"></script>
<script src="js/sio.js"></script>
<script src="js/savedata.js"></script>
<script src="js/gpio.js"></script>
<script src="js/gba.js"></script>
<script src="resources/xhr.js"></script>

<script>
var gba;
var runCommands = [];
var debug = null;

try {
	gba = new GameBoyAdvance();
	gba.keypad.eatInput = true;
	gba.setLogger(function(level, error) {
		console.log(error);
		gba.pause();
		var screen = document.getElementById('screen');
		if (screen.getAttribute('class') == 'dead') {
			console.log('We appear to have crashed multiple times without reseting.');
			return;
		}
		var crash = document.createElement('img');
		crash.setAttribute('id', 'crash');
		crash.setAttribute('src', 'resources/crash.png');
		screen.parentElement.insertBefore(crash, screen);
		screen.setAttribute('class', 'dead');
	});
} catch (exception) {
	gba = null;
}

window.onload = function() {
	if (gba && FileReader) {
		var canvas = document.getElementById('screen');
		gba.setCanvas(canvas);

		gba.logLevel = gba.LOG_ERROR;

		loadRom('resources/bios.bin', function(bios) {
			gba.setBios(bios);
		});

		if (!gba.audio.context) {
			// Remove the sound box if sound isn't available
			var soundbox = document.getElementById('sound');
			soundbox.parentElement.removeChild(soundbox);
		}

		if (window.navigator.appName == 'Microsoft Internet Explorer') {
			// Remove the pixelated option if it doesn't work
			var pixelatedBox = document.getElementById('pixelated');
			pixelatedBox.parentElement.removeChild(pixelatedBox);
		}
	} else {
		var dead = document.getElementById('controls');
		dead.parentElement.removeChild(dead);
	}
}

function fadeOut(id, nextId, kill) {
	var e = document.getElementById(id);
	var e2 = document.getElementById(nextId);
	if (!e) {
		return;
	}
	var removeSelf = function() {
		if (kill) {
			e.parentElement.removeChild(e);
		} else {
			e.setAttribute('class', 'dead');
			e.removeEventListener('webkitTransitionEnd', removeSelf);
			e.removeEventListener('oTransitionEnd', removeSelf);
			e.removeEventListener('transitionend', removeSelf);
		}
		if (e2) {
			e2.setAttribute('class', 'hidden');
			setTimeout(function() {
				e2.removeAttribute('class');
			}, 0);
		}
	}

	e.addEventListener('webkitTransitionEnd', removeSelf, false);
	e.addEventListener('oTransitionEnd', removeSelf, false);
	e.addEventListener('transitionend', removeSelf, false);
	e.setAttribute('class', 'hidden');
}

function run(file) {
	var dead = document.getElementById('loader');
	dead.value = '';
	var load = document.getElementById('select');
	load.textContent = 'Loading...';
	load.removeAttribute('onclick');
	var pause = document.getElementById('pause');
	pause.textContent = "PAUSE";
	gba.loadRomFromFile(file, function(result) {
		if (result) {
			for (var i = 0; i < runCommands.length; ++i) {
				runCommands[i]();
			}
			runCommands = [];
			fadeOut('preload', 'ingame');
			fadeOut('instructions', null, true);
			gba.runStable();
		} else {
			load.textContent = 'FAILED';
			setTimeout(function() {
				load.textContent = 'SELECT';
				load.onclick = function() {
					document.getElementById('loader').click();
				}
			}, 3000);
		}
	});
}

function reset() {
	gba.pause();
	gba.reset();
	var load = document.getElementById('select');
	load.textContent = 'SELECT';
	var crash = document.getElementById('crash');
	if (crash) {
		var context = gba.targetCanvas.getContext('2d');
		context.clearRect(0, 0, 480, 320);
		gba.video.drawCallback();
		crash.parentElement.removeChild(crash);
		var canvas = document.getElementById('screen');
		canvas.removeAttribute('class');
	} else {
		lcdFade(gba.context, gba.targetCanvas.getContext('2d'), gba.video.drawCallback);
	}
	load.onclick = function() {
		document.getElementById('loader').click();
	}
	fadeOut('ingame', 'preload');
}

function uploadSavedataPending(file) {
	runCommands.push(function() { gba.loadSavedataFromFile(file) });
}

function togglePause() {
	var e = document.getElementById('pause');
	if (gba.paused) {
		if (debug && debug.gbaCon) {
			debug.gbaCon.run();
		} else {
			gba.runStable();
		}
		e.textContent = "PAUSE";
	} else {
		if (debug && debug.gbaCon) {
			debug.gbaCon.pause();
		} else {
			gba.pause();
		}
		e.textContent = "UNPAUSE";
	}
}

function screenshot() {
	var canvas = gba.indirectCanvas;
	window.open(canvas.toDataURL('image/png'), 'screenshot');
}

function lcdFade(context, target, callback) {
	var i = 0;
	var drawInterval = setInterval(function() {
		i++;
		var pixelData = context.getImageData(0, 0, 240, 160);
		for (var y = 0; y < 160; ++y) {
			for (var x = 0; x < 240; ++x) {
				var xDiff = Math.abs(x - 120);
				var yDiff = Math.abs(y - 80) * 0.8;
				var xFactor = (120 - i - xDiff) / 120;
				var yFactor = (80 - i - ((y & 1) * 10) - yDiff + Math.pow(xDiff, 1 / 2)) / 80;
				pixelData.data[(x + y * 240) * 4 + 3] *= Math.pow(xFactor, 1 / 3) * Math.pow(yFactor, 1 / 2);
			}
		}
		context.putImageData(pixelData, 0, 0);
		target.clearRect(0, 0, 480, 320);
		if (i > 40) {
			clearInterval(drawInterval);
		} else {
			callback();
		}
	}, 50);
}

function setVolume(value) {
	gba.audio.masterVolume = Math.pow(2, value) - 1;
}

function setPixelated(pixelated) {
	var screen = document.getElementById('screen');
	var context = screen.getContext('2d');
	if (context.webkitImageSmoothingEnabled) {
		context.webkitImageSmoothingEnabled = !pixelated;
	} else if (context.mozImageSmoothingEnabled) {
		context.mozImageSmoothingEnabled = !pixelated;
	} else if (window.navigator.appName != 'Microsoft Internet Explorer') {
			if (pixelated) {
				screen.setAttribute('width', '240');
				screen.setAttribute('height', '160');
			} else {
				screen.setAttribute('width', '480');
				screen.setAttribute('height', '320');
			}
			if (window.navigator.appName == 'Opera') {
			// Ugly hack! Ew!
			if (pixelated) {
				screen.style.marginTop = '0';
				screen.style.marginBottom = '-325px';
			} else {
				delete screen.style;
			}
		}
	}
}

function enableDebug() {
	window.onmessage = function(message) {
		if (message.origin != document.domain && (message.origin != 'file://' || document.domain)) {
			console.log('Failed XSS');
			return;
		}
		switch (message.data) {
		case 'connect':
			if (message.source == debug) {
				debug.postMessage('connect', document.domain || '*');
			}
			break;
		case 'connected':
			break;
		case 'disconnect':
			if (message.source == debug) {
				debug = null;
			}
		}
	}
	window.onunload = function() {
		if (debug && debug.postMessage) {
			debug.postMessage('disconnect', document.domain || '*');
		}
	}
	if (!debug || !debug.postMessage) {
		debug = window.open('debugger.html', 'debug');
	} else {
		debug.postMessage('connect', document.domain || '*');
	}
}

document.addEventListener('webkitfullscreenchange', function() {
	var canvas = document.getElementById('screen');
	if (document.webkitIsFullScreen) {
		canvas.setAttribute('height', document.body.offsetHeight);
		canvas.setAttribute('width', document.body.offsetHeight / 2 * 3);
		canvas.setAttribute('style', 'margin: 0');
	} else {
		canvas.setAttribute('height', 320);
		canvas.setAttribute('width', 480);
		canvas.removeAttribute('style');
	}
}, false);
</script>
</head>
<body>
<canvas id="screen" width="480" height="320"></canvas>
<section id="controls">
	<div id="preload">
		<button class="bigbutton" id="select" onclick="document.getElementById('loader').click()">SELECT</button>
		<input id="loader" type="file" accept=".gba" onchange="run(this.files[0]);">
		<button onclick="document.getElementById('saveloader').click()">Upload Savegame</button>
		<input id="saveloader" type="file" onchange="uploadSavedataPending(this.files[0]);">
	</div>
	<div id="ingame" class="hidden">
		<button id="pause" class="bigbutton" onclick="togglePause()">PAUSE</button>
		<button class="bigbutton" onclick="reset()">RESET</button>
		<button onclick="gba.downloadSavedata()">Download Savegame</button>
		<button onclick="screenshot()">Screenshot</button>
		<label id="pixelated">
			<input type="checkbox" onchange="setPixelated(this.checked)">
			<p>Pixelated</p>
		</label>
		<div id="sound">
			<input type="checkbox" checked onchange="gba.audio.masterEnable = this.checked">
			<p>Sound</p>
			<input type="range" min="0" max="1" value="1" step="any" onchange="setVolume(this.value)" oninput="setVolume(this.value)">
		</div>
		<p id="openDebug" onclick="enableDebug()">Debugger</p>
	</div>
</section>
<ul id="links">
	<li><a href="http://github.com/endrift/gbajs/">Fork me on GitHub!</a></li>
	<li><a href="https://github.com/endrift/gbajs/wiki/Compatibility-List">Compatibility list</a></li>
</ul>
<footer>© 2012 – 2013 <a href="http://endrift.com/">Jeffrey Pfau</a></footer>
</body>
</html>