(function(global) {
	'use strict';
	
	// globals
	var tizen = global.tizen;
	var localStorage = global.localStorage;
	var gse = global.gse;
	
	// shared reference to game object
	var sharedEngine = null;
	var gameDesignWidth = 0;
	var gameDesignHeight = 0;
	var gameCanvas = null;
	
	// state management
	var gameHasLaunched = false;
	var gameIsPaused = false;
	var isFullScreen = false;
	var forceScreenOn = false;
	
	// persistent data, for analytics
	var installationId = null;
	var pseudoDeviceId = null;
	
	// "forward declaration"
	var launchGame = null;
	

	// utility functions
	function hash(inputString) {
		// ASSUME: the engine defines the global MD5 function
		return global.MD5(inputString);
	}
	
	function randomInteger(n) {
		return Math.floor(Math.random() * n);
	}
	
	function pickRandomCharacter(characters) {
		var index = randomInteger(characters.length);
		return characters.charAt(index);
	}
	
	function generateRandomHexString(length) {
		var s = '';
		var hexCharacters = '0123456789abcdef';
		for ( var i = 0; i < length; i++) {
			s += pickRandomCharacter(hexCharacters);
		}
		return s;
	}
	
	function generateUUID() {
		var s = '';
		s += generateRandomHexString(8);
		s += '-';
		s += generateRandomHexString(4);
		s += '-4';
		s += generateRandomHexString(3);
		s += '-' + pickRandomCharacter('89ab');
		s += generateRandomHexString(3);
		s += '-';
		s += generateRandomHexString(12);
		return s;
	}
	
	
	// device helper functions
	function requestPowerScreen() {
		// if screen power control is enabled, we make the API call here
	}
	
	function releasePowerScreen() {
		// if screen power control is enabled, we make the API call here
	}
	
	
	// engine control functions
	function pauseGame() {
		// suspend the game via system event
		sharedEngine.pause();
		gameIsPaused = true;
	}
	
	function unpauseGame() {
		sharedEngine.unpause();
		gameIsPaused = false;
	}
	
	function suspendGame() {
		sharedEngine.suspend();
		pauseGame();
		
		// return screen power management to default
		releasePowerScreen();
	}
	
	function activateGame() {
		if (!gameHasLaunched) {
			launchGame();
		}
		else if (gameIsPaused) {
			unpauseGame();
		}
	}
	
	function unsuspendGame() {
		activateGame();
		sharedEngine.unsuspend();
		
		// ask to keep the screen on
		requestPowerScreen();
	}
	

	// event handlers...
	function onDocumentHidden() {
		if (gameHasLaunched) {
			suspendGame();
		}
	}
	
	function onDocumentVisible() {
		if (gameHasLaunched) {
			unsuspendGame();
		}
	}
	
	function onDocumentVisibilityChange(event) {
		var visibilityState = document.webkitVisibilityState;
		if (visibilityState === 'hidden') {
			onDocumentHidden();
		}
		else if (visibilityState === 'visible') {
			onDocumentVisible();
		}
	}
	
	function onHardwareKey(event) {
		if (event.keyName ==='back') {
			var didChangeRule = gse.postEvent('keyDown', 'back');
			if (!didChangeRule) {
				// if no rule change, fallback to terminate app
				tizen.application.getCurrentApplication().exit();
			}
			else {
				// after a time, simulate an up event
				setTimeout(function() {
					gse.postEvent('keyUp', 'back');
				}, 100);
			}
		}
	}
	

	// helper functions...
	function readOption(key) {
		var value = null;
		if (typeof widget !== "undefined" && typeof widget.preferences[key] !== "undefined") {
			value = widget.preferences.getItem(key);
		}
		return value;
	}
	
	function readOptionOrDefault(key, defaultValue) {
		var value = readOption(key);
		if (value === null || value === '' || value === key) {
			value = defaultValue;
		}
		return value;
	}
	
	function getSystemCountry() {
		return ''; // FIXME: can we determine this?
	}
	
	function getSystemLocale() {
		var locale = navigator.language;
		var match = /^(\w+)-(\w+)/.exec(locale);
		if (match) {
			return {
				language: locale,
				country: match[2]
			};
		}
		else {
			return {
				language: locale,
				country: getSystemCountry()
			};
		}
	}
	
	function getSystemPlatform() {
		return tizen.systeminfo.getCapabilities().platformName;
	}
	
	function getSystemVersion() {
		var userAgent = navigator.userAgent;
		var match = /Tizen\s*([.\d]+)/.exec(userAgent);
		return match ? match[1] : '';
	}
	
	function getDeviceID() {
		return tizen.systeminfo.getCapabilities().duid;
	}
	
	function loadPersistentData() {
		// For each unique installation, we generate a random string to serve as
		// an ID and then
		// persist it to app data storage so that it remains the same for this
		// installed app.
		// This is as close as we can get to a device ID.
		var storageKey = 'system/analytics/InstallationID';
		installationId = localStorage.getItem(storageKey);
		// pseudoDeviceId = readOption('pseudoDeviceId');
		if (!installationId || installationId === '') {
			installationId = generateUUID();
			localStorage.setItem(storageKey, installationId);
		}
	}
	
	launchGame = function() {
		gse.ready(function(engine) {
			sharedEngine = engine;
			
			// force tizen platform
			engine.platform = engine.tizen;
			
			// game path
			var game = readOptionOrDefault('game', 'default');
			var gamePath = 'game/' + game;
			
			// load stuff for analytics
			loadPersistentData();
			
			// loading animation delegates
			var preloading = document.getElementById('gse-preloading');
			var loading = document.getElementById('gse-loading');
			var isPreloading = true;
			var isLoading = false;
			var loadingTimeout = 250;
			engine.appendDelegate({

				onLoadingBegin: function() {
					if (isPreloading) {
						return;
					}
					isLoading = true;
					setTimeout(function() {
						if (isLoading) {
							loading.classList.remove('gse-hidden');
						}
					}, loadingTimeout);
				},

				
				onLoadingEnd: function() {
					if (isPreloading) {
						preloading.classList.add('gse-hidden');
						isPreloading = false;
					}
					else {
						loading.classList.add('gse-hidden');
					}
					isLoading = false;
				}
			});
			
			// browser delegate
			engine.appendDelegate({
				onBrowserOpen: function(url) {
					// discover the default app for viewing webpages
					var appControl = new tizen.ApplicationControl("http://tizen.org/appcontrol/operation/view", url);
					tizen.application.launchAppControl(appControl);
				}
			});
			
			// analytics data delegate
			var analyticsKeyMap = {
				APP_UNIQUE_ID: 'analytics/GSAppID',
				APP_DEVELOPER_ID: 'analytics/GSUserID',
				APP_CREATOR_VERSION: 'analytics/GSCVersion',
				APP_LICENSE_TYPE: 'analytics/GSLicenseType',
				APP_RELEASE_ID: 'analytics/GSReleaseID',
				APP_DEVELOPER_USERNAME: 'analytics/GSUsername'
			};
			engine.appendDelegate({
				onExternalReadSync: function(key) {
					var segments = key.split('/');
					if (segments[0] !== 'analytics') {
						return;
					}
					key = segments[1];
					if (key === 'UPLOAD_URLS') {
						// Upload URLs are special because they are returned as
						// an
						// array.
						var uploadUrls = [];
						var analyticsUrl1 = readOptionOrDefault('analytics/UploadURL1', null);
						var analyticsUrl2 = readOptionOrDefault('analytics/UploadURL2', null);
						// include only non-empty values
						if (analyticsUrl1) {
							uploadUrls.push(analyticsUrl1);
						}
						if (analyticsUrl2) {
							uploadUrls.push(analyticsUrl2);
						}
						return uploadUrls;
					}
					else if (key in analyticsKeyMap) {
						// it's in the resource key map, we we just pass through
						// whatever is in the resources file
						var resourceKey = analyticsKeyMap[key];
						return readOptionOrDefault(resourceKey, null);
					}
					else {
						// it's something else that we need to compute
						switch (key) {
						case 'APP_DISPLAY_NAME':
							return tizen.application.getCurrentApplication().appInfo.name;
						case 'APP_VERSION':
							return tizen.application.getCurrentApplication().appInfo.version;
						case 'APP_INSTALL_ID':
							return installationId;
						case 'DEVICE_PLATFORM':
							return getSystemPlatform();
						case 'DEVICE_OS_VERSION':
							return getSystemVersion();
						case 'DEVICE_COUNTRY':
							return getSystemLocale().country;
						case 'DEVICE_LOCALE_LANGUAGE':
							return getSystemLocale().language;
						case 'DEVICE_LOCALE_COUNTRY':
							return getSystemLocale().country;
						case 'DEVICE_UNIQUE_ID':
							return getDeviceID();
						case 'DEVICE_UNIQUE_ID_HASH':
							return hash(getDeviceID());
						}
					}
				}
			});
			
			// engine options
			engine.options.renderer = readOptionOrDefault('renderer', 'detect');
			engine.options.audio = readOptionOrDefault('audio', 'detect');
			engine.options.targetRate = parseFloat(readOptionOrDefault('target-rate', '0'));
			engine.options.drawFactor = parseFloat(readOptionOrDefault('draw-factor', '1'));
			engine.options.maxFrameDeltaMS = parseFloat(readOptionOrDefault('max-frame-delta-ms', '33'));
			engine.options.maxFrameDebtMS = parseFloat(readOptionOrDefault('max-frame-debt-ms', '16'));
			engine.options.hitchFrameMS = parseFloat(readOptionOrDefault('hitch-frame-ms', '66'));
			engine.options.hitchWindowMS = parseFloat(readOptionOrDefault('hitch-window-ms', '3000'));
			engine.options.viewportReference = readOptionOrDefault('viewport-reference', 'frame');
			engine.options.viewportFit = readOptionOrDefault('viewport-fit', 'letterbox');
			engine.options.viewportFactor = parseFloat(readOptionOrDefault('viewport-factor', '1'));
			engine.options.viewportScale = readOptionOrDefault('viewport-scale', 'quality');

			// read boolean string, convert to boolean
			var forceScreenOnString = readOptionOrDefault('force-screen-on', 'false');
			forceScreenOn = (forceScreenOnString === 'true');

			// bind renderer frame and play
			engine.setRenderFrame('gse-player');
			engine.play(gamePath);
			gameHasLaunched = true;

			// ask to keep screen on
			requestPowerScreen();
		});
	};
	
	// bootstrap...
	function main() {
		// wire up event handlers...
		document.addEventListener('webkitvisibilitychange', onDocumentVisibilityChange, false);
		document.addEventListener('tizenhwkey', onHardwareKey, false);
		
		// go!
		launchGame();
	}
	
	main();
}(this));
