"use strict";
/**
 * Game tested on: FF 16, Chrome 23, IE 9
 */
var game = (function() {
	var canvas;
	var ctx;
	var board;
	var width;
	var height;
	var fps;
	var realFps;
	var lastFps;
	var lastFpsValues = [];
	var performance = false;
	var readFPS = true;
	var period;
	var objectTypes = {
		PLAYER : 1,
		PLAYER_MISSILE : 2,
		ENEMY : 4,
		ENEMY_MISSILE : 8,
		STAR : 16,
		EARTH : 32,
		HUD : 64,
		POWER_UP : 128,
		MSG : 256,
		OVERHEAT : 512
	};
	var levelLoopPtr;
	var mainLoopPtr;
	var paused = false;
	var pauseNow = false;
	var gameOnGoing = false;
	var home;
	var appHiddenTime;
	var titleScreen;
	var hud;
	var lastLoop = new Date();
	/**
	 * Main game loop - here all begins
	 */
	var mainLoop = function() {
		var t1 = new Date();
		isGameOver();
		var thisLoop = new Date();
		staticBackground.draw(ctx);
		realFps = 1000 / (thisLoop - lastLoop);
		if (readFPS) {
			lastFps = realFps;
			readFPS = false;
		}
		lastLoop = thisLoop;
		var dt = period / 1000;
		if (board)
			board.step(dt);
		if (board)
			board.draw(ctx);
		var t2 = new Date();
		var timeOffset = t2 - t1;
		/**
		 * The time spent on rendering is subtracted from period to keep the defined FPS. There is 5msec left for edge cases to handle events
		 */
		var diff = period - timeOffset;
		if (diff < 5)
			diff = 5;
		mainLoopPtr = setTimeout(mainLoop, diff);
		if (pauseNow) {
			clearInterval(mainLoopPtr);
			pauseNow = false;
		}
	};

	var isGameOver = function() {
		if (board && board.cnt && (board.cnt[objectTypes.PLAYER] === 0 || board.cnt[objectTypes.EARTH] === 0)) {
			if (gameOnGoing) {
				board.removeAll();
				highScores.add();
				gameOnGoing = false;
			}
		}
	};

	var removeBoard = function() {
		board = null;
	};

	var setBoard = function(mBoard) {
		board = mBoard;
		messageBoxManager.setBoard(board);
	};

	/**
	 * 
	 */
	var performanceCheck = function(lastFps) {
		var sum = 0;
		var mean;
		if (lastFpsValues.length > 25 && mean !== null) {
			for ( var i = 0; i < lastFpsValues.length; i++) {
				sum += lastFpsValues[i];
			}
			mean = sum / lastFpsValues.length;
			lastFpsValues = [];
		}
		if (gameOnGoing && mean && mean < config.fps.removeBackgroundEffects)
			disableBackgroundEffects();

		if (gameOnGoing && mean && mean < config.fps.minThreshold && performance) {
			performance = false;
			alert("Device performance is low. Please consider upgrade.");
		}
		lastFpsValues.push(lastFps);
	};

	var disableBackgroundEffects = function() {
		board.removeByTypes([ game.getObjectTypes().STAR ]);
	}

	return {
		keys : [],

		points : 0,

		inputHandler : null,

		/**
		 * Initializes the game - background is loaded, sprites are loaded, actions for moving game background, foreground defined
		 * 
		 * @returns
		 */
		initialize : function() {
			game.log("initialize");
			fps = config.fps.nominal;
			period = 1 / (fps / 1000);
			game.log("fps = " + fps);
			game.log("period = " + period + "ms");
			game.log("Is it Android WebView? " + view.isItAndroidWebView());
			game.log("Is it Tizen? " + view.isItTizen());
			game.log("Is it mobile? " + view.isItMobile());
			tlib.visibility.onApplicationVisible(this.onVisible);
			tlib.visibility.onApplicationHidden(this.onHidden);
			staticBackground.draw(ctx);
			spriteSheet.load(function() {
				game.titleScreenShow();
				mainLoop();
			});
			home = new Home();
			menu.initialize();
		},

		/**
		 * Displays title screen
		 * 
		 * @returns
		 */
		titleScreenShow : function() {
			game.log('titleScreenShow');
			var that = this;
			home.hide();
			removeBoard();
			var titleBoard = new GameBoard();
			if (config.debug) {
				var fps = new Fps();
				titleBoard.add(fps, 3);
			}
			titleScreen = new TitleScreen("TAP/ENTER to fight!", that.mainMenuShow);
			titleBoard.add(titleScreen, 2);
			var starfield = new Starfield(25, titleBoard, ctx);
			setBoard(titleBoard);
		},

		/**
		 * Shows game initial menu
		 * 
		 * @returns
		 */
		mainMenuShow : function() {
			removeBoard();
			performance = true;
			menu.open('earthguard');
		},

		/**
		 * Starts the new game on a given level
		 * 
		 * @param {Number} level
		 */
		start : function(level) {
			game.log('start');
			game.points = 0;
			if (board)
				board.removeAll();
			game.resume();
			var gameBoard = new GameBoard();
			if (config.debug) {
				var fps = new Fps();
				gameBoard.add(fps, 3);
			}
			home.display();
			hud = new Hud(0, 0);
			var earth = new Earth(hud);
			gameBoard.add(earth, config.earth.zIndex);
			var overheat = new Overheat();
			var ship = new PlayerShip(hud, overheat);
			gameBoard.add(ship, config.player.zIndex);
			gameBoard.add(hud, 101);
			gameBoard.add(overheat, 101);
			var starfield = new Starfield(20, gameBoard, ctx);
			setBoard(gameBoard);
			gameOnGoing = true;
			powerUpsHandler.reset();
			levelManager.start(level);
		},
		/**
		 * If game was not paused before it pauses the game
		 * 
		 * @returns
		 */
		pause : function() {
			if (!paused) {
				pauseNow = true;
				paused = true;
			}
		},
		/**
		 * If game was paused before it resumes the game
		 * 
		 * @returns
		 */
		resume : function(updateLevelStartTime) {
			game.inputHandler.clearStartPosition();
			if (paused) {
				var currentLevel = levelManager.getCurrentLevel();
				if (currentLevel && menu.getOpeningTime() && updateLevelStartTime) {
					currentLevel.updateStartTime(menu.getOpeningTime());
					powerUpsHandler.updateDoublePointsStartTime(menu.getOpeningTime());
				}
				pauseNow = false;
				mainLoop();
				paused = false;
			}
		},
		/**
		 * Informs if game is in paused state
		 * 
		 * @returns {boolean} true/false
		 */
		isPaused : function() {
			return paused;
		},
		/**
		 * Application Exit - works only on Tizen
		 * 
		 * @returns
		 */
		exit : function() {
			if (typeof tizen !== "undefined" && tizen.application) {
				if (confirm("Exit?")) {
					var app = tizen.application.getCurrentApplication();
					app.exit();
				}
			} else if (view.isItAndroidWebView()) {
				Android.exitApplication();
			} else {
				alert("Not supported");
			}
		},
		getObjectTypes : function() {
			return objectTypes;
		},
		/**
		 * Get currently displayed board
		 * 
		 * @returns {Object} currently displayed board
		 */
		getBoard : function() {
			return board;
		},
		/**
		 * Get hud
		 * 
		 * @returns {Object}
		 */
		getHud : function() {
			return hud;
		},
		/**
		 * Returns enemies types
		 * 
		 * @returns
		 */
		getEnemiesTypes : function() {
			return enemies;
		},
		/**
		 * Defines standard prototype inheritance
		 * 
		 * @param child
		 * @param parent
		 * @returns
		 */
		inherit : function(child, parent) {
			child.prototype = new parent();
		},
		log : function(txt) {
			if (config.debug && typeof console !== "undefined" && typeof console.log !== "undefined")
				console.log(txt);
		},

		/**
		 * Returns current real FPS value, if the change was more then 1fps
		 * 
		 * @returns {Number} real FPS value
		 */
		getCurrentFps : function() {
			if (Math.abs(lastFps - realFps) >= 1) {
				lastFps = realFps;
			}
			performanceCheck(lastFps);
			return lastFps;
		},
		/**
		 * Checks if game is on going
		 * 
		 * @returns {Boolean} true if game is on going
		 */
		isRunning : function() {
			return gameOnGoing;
		},
		setOnGoing : function(on) {
			gameOnGoing = on;
		},
		getTitleScreen : function() {
			return titleScreen;
		},
		/**
		 * Sets main canvas context
		 * 
		 * @param ctx
		 * @returns
		 */
		setContext : function(pcanvas, pctx) {
			canvas = pcanvas;
			ctx = pctx;
		},
		getCtx : function() {
			return ctx;
		},
		onVisible : function() {
			game.log("onVisible");
			var now = new Date();
			var currentLevel = levelManager.getCurrentLevel();
			if (currentLevel && appHiddenTime && !menu.isAnyMenuOpened()) {
				var timeDiff = now - appHiddenTime;
				currentLevel.updateStartTime(timeDiff);
				powerUpsHandler.updateDoublePointsStartTime(timeDiff);
			} else {
				game.log("Unable to determine current level or time or time update not needed");
			}
			if (!menu.isAnyMenuOpened())
				game.resume();
		},

		onHidden : function() {
			game.log("onHidden");
			appHiddenTime = new Date();
			clearInterval(mainLoopPtr);
			game.pause();
			if (!config.defaultPosition)
				game.inputHandler.clearStartPosition();
		}
	}
})();
