/**
*  William Malone Game Framework
*
*  @author William Malone (www.williammalone.com)
*/

/*global window, WM */

WM.game = function (GLOBAL, WM, options) {
					
	"use strict";

	var that = WM.dispatcher(),
		loopInterval,
		loading,
		now,
		last = + new Date(),
		dtBuffer = 0,
		resourcePath = (options && options.resourcePath) || "",
		loadingView,
		successView,
		commonHud,
		landscapeOnlyOverlay,
		playing = false,
		audio,
		audioLoadStarted = false,
		audioLoaded = false,
		resourcesLoaded = false,
		pastTitleCard = false,
		debug = false,
		destroyed = false,
	
		frameRateCounter = (function () {
		
			var that = {},
				frameCounter = 0,
				lastFrameTime = + new Date();
			
			that.lastFrameCount = 0;	
			that.tick = function () {
					var now = + new Date();
					frameCounter += 1;
					if (now >= lastFrameTime + 1000) {	
						that.lastFrameCount = frameCounter;
						renderFpsDisplay();
						lastFrameTime = now;
						frameCounter = 0;
					}
			};
			return that;
		}()),
	
		// Game loop
		//
		loop = function () {
			
			var i,
				dt;
				
			now = +new Date();
			dt = now - last;
			// If dt is too large then chop it
			if (dt > 5000) {
				//WM.debug("Loop timeout (5 sec) reached: " + dt);
				dt = 5000;
			}
			last = now;

			
			if (!that.paused) {
			
				i = 1;
			
				dtBuffer += dt;
				while (dtBuffer >= that.step) {
					i += 1;
					that.update();
					dtBuffer -= that.step;
				}
				
				if (debug) {
				
					//if (i > 3) {
					//	WM.debug("Dropped " + (i - 1) + " Frames");
					//}
					frameRateCounter.tick();
				}
					
				that.render();
			}
		},
		
		renderFpsDisplay = function () {
			that.contexts.debug.clearRect(0, 0, 200, 200);
			that.contexts.debug.fillStyle = "#ff0000";
			that.contexts.debug.font = "14px Helvetica";
			that.contexts.debug.fillText("FPS: " + frameRateCounter.lastFrameCount, 20, 20);
		},
		
		commonTouchStart = function (point) {
			var audioFilename;
		
			if (!pastTitleCard) {
				pastTitleCard = true;
				
				WM.logStats({
				    event: "start.click",
				    title: that.gameTitle,
				    id: that.gameId
				});
	
				if (that.audioEnabled) {
					if (!audioLoadStarted) {

						audioLoadStarted = true;
						
						// If specific audio path then prepend that path, other use the resource path
						if (options.audioPath) {
							audioFilename = options.audioPath + options.audioFilename;
						} else {
							audioFilename = options.resourcePath + options.audioFilename;
						}
						that.audio = WM.audio(GLOBAL, WM, audioFilename);
						that.audio.addEventListener("LOAD_STARTED", function () {

							audioLoaded = true;
							if (resourcesLoaded) {
								that.onResourcesLoaded();
							}
						});
						
						that.audio.loadByUserInteraction();
						loadingView.touchStart(point);
					}
				} else {
					if (!resourcesLoaded) {
						loadingView.touchStart(point);
					}
					
				}
			}
		},
		
		commonTouchEnd = function (point) {
	
			if (commonHud !== undefined) {
				commonHud.touchEnd(point);
			}
		},
		
		updateAllCanvases = function (width, height) {
			var key;
			for (key in that.contexts) {
				if (that.contexts.hasOwnProperty(key)) {
					if (key !== "landscapeOnly") {
						that.contexts[key].canvas.width = width;
						that.contexts[key].canvas.height = height;
					}
				}
			}
		},
		
		updateGameSize = function () {
			var i;
		
			updateAllCanvases(that.viewportWidth, that.viewportHeight);
			
			for (i = 0; i < that.views.length; i += 1) {
				that.views[i].dirty = true;
			}
			
			if (loadingView && !playing) {
				loadingView.dirty = true;
				loadingView.render();
			}
			
			if (commonHud) {
				commonHud.dirty = true;
				commonHud.render();
			}
			that.onCanvasSizeUpdated();
		};
	
	// ---------------------------------------------------------------------------------
	// Public
	
	that.gameTitle;
	that.gameId;
	that.debug = options && options.debug;
	that.step = 16.666;
	that.views = [];
	that.paused = false;
	that.contexts = {};
	that.controller = WM.controller(GLOBAL, options.element);
	that.resourcer = WM.resourcer(GLOBAL, WM, resourcePath);
	that.roundComplete = false;
	that.numRoundsStarted = 0;
	that.audioEnabled = options && options.audioEnabled;
	that.viewportWidth = options && options.gameWidth !== undefined ? options.gameWidth : window.innerWidth;
	that.viewportHeight = options && options.gameHeight !== undefined ? options.gameHeight : window.innerHeight;
	that.overallOffset = options && options.overallOffset !== undefined ? options.overallOffset : {
		x: 0,
		y: 0
	};
	
	that.touchStart = function (point) {
		
	};
	
	that.update = function () {
		
	};
	
	that.playSound = function (soundName) {
	
		soundName = soundName.toUpperCase();
		
		if (that.audioEnabled && that.audio.getSound(soundName) !== undefined) {
			
			if (that.audio.play(soundName)) {
			
				WM.logStats({
				    event: "audio.play",
				    title: that.gameTitle,
				    id: that.gameId,
				    params : {
				    	name: soundName
				    }
				});
				
				return true;
			} else {
				return false;
			}
		} else {
			return false;
		}
	};
	
	that.render = function () {
		var i;
		for (i = 0; i < that.views.length; i += 1) {
			that.views[i].render();
		}
	};

	// Adds a view to the Game
	//
	that.addView = function (view) {
		that.views.push(view);
	};

	// Removes a view from the Game
	//
	that.removeView = function (view) {
		var i;
		for (i = 0; i < that.views.length; i += 1) {
			if (that.views[i] === view) {
				that.views.splice(i, 1);
				return;
			}
		}		
	};
	
	// Load the game resources
	//
	that.loadGame = function () {
	
		WM.logStats({
		    event: "game.load",
		    title: that.gameTitle,
		    id: that.gameId
		});
		
		loadingView.addEventListener("READY", that.load);
		loadingView.load();
		landscapeOnlyOverlay.load();
		commonHud.load();	
	};
	
	// Starts the game loop
	//
	that.playGame = function () {
	
		if (!playing) {
			playing = true;
			
			WM.logStats({
			    event: "game.start",
			    title: that.gameTitle,
			    id: that.gameId
			});
			
			that.setup();
			
			last = +new Date();
			that.clearCxt(that.contexts.loading);
			loopInterval = setInterval(loop, that.step);
		}
	};
	
	// Pause the game loop
	//
	that.pause = function () {
		if (!that.paused) {
			that.dispatchEvent("PAUSED");	
		}
		that.paused = true;
	};
	
	// Unpause the game loop
	//
	that.unpause = function () {
		if (that.paused) {
			that.dispatchEvent("UNPAUSED");	
		}
		that.paused = false;
	};
	
	// Start the game over
	//
	that.startOver = function () {
		that.unpause();
		that.roundSuccess();
	};
	
	// Destroy the views and end the update loop
	//
	that.destroy = function () {
	
		var i = 0;
		
		if (!destroyed) {
			destroyed = true;
			
			GLOBAL.clearInterval(loopInterval);
			
			if (that.audio) {
				that.audio.destroy();
			}
			
			for (i; i < that.views.length; i += 1) {
				that.views[i].destroy();
			}
			
			options.element.innerHTML = "";
		}
	};
	
	that.onOrientationChange = function () {
		updateGameSize();
	};
	
	that.onCanvasSizeUpdated = function () {
	
	};
	
	that.setGameSize = function (width, height) {
	
		that.viewportWidth = width;
		that.viewportHeight = height;
		updateGameSize();
	};
	
	that.onResourceLoaded = function (e) {
		
		//WM.log("Resource Load Progress: " + e.current + " / " + e.total);
		loadingView.percentage = e.current / e.total
		loadingView.render();
		
	};
	
	that.onResourcesLoaded = function () {
	
		resourcesLoaded = true;
		
		if (audioLoaded || !that.audioEnabled) {
			// Set the loading view to 100 percent
			loadingView.percentage = 1;
		
			pastTitleCard = true;
		
			WM.logStats({
			    event: "game.loaded",
			    title: that.gameTitle,
			    id: that.gameId
			});
			that.dispatchEvent("GAME_LOADED");
		}
	};
	
	that.roundSuccess = function () {
		that.breakdown();
		that.setup();
	};
	
	that.clearCxt = function (ctx) {
		ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
	};
	
	that.addCanvas = function (name) {
	
		var canvasElement = GLOBAL.document.createElement('canvas');
		canvasElement.width = that.viewportWidth;
		canvasElement.height = that.viewportHeight;
		canvasElement.className = "WMcanvas";
		
		if (that.contexts[name] !== undefined) {
			WM.error({
				name: "ContextAlreadyExists",
				message: "Cannot create context because it already exists with name " + name + "."
			});
		} else {
			that.contexts[name] = options.element.appendChild(canvasElement).getContext("2d");
		}
	};
	
	that.showLandscapeOnlyMessage = function (viewportWidth, viewportHeight) {

		if (landscapeOnlyOverlay) {

			if (viewportWidth !== undefined) {
				that.contexts.landscapeOnly.canvas.width = viewportWidth;
			}
			if (viewportHeight !== undefined) {
				that.contexts.landscapeOnly.canvas.height = viewportHeight;
			}
			//WM.debug("Show Landscape Only Message");
			landscapeOnlyOverlay.show();
		}
	};
	
	that.hideLandscapeOnlyMessage = function () {
		if (landscapeOnlyOverlay) {
			//WM.debug("Hide Landscape Only Message");
			landscapeOnlyOverlay.hide();
		}
	};
	
	that.playMusic = function () {
	
		if (that.audioEnabled) {
			that.audio.removeEventListener("COMPLETE", that.playMusic);
		
			if (that.audio.getSound("music") !== undefined) {
				that.audio.play("music");
			}
		}
	};
	
	that.init = function () {
		that.addCanvas("loading");
		that.addCanvas("commonHud");
		that.addCanvas("landscapeOnly");
		if (debug) {
			that.addCanvas("debug");
		}
		
		updateGameSize();
	
		that.controller.addEventListener("TOUCH_START", commonTouchStart);
		that.controller.addEventListener("TOUCH_END", commonTouchEnd);
		
		loadingView = WM.loadingView(GLOBAL, WM, that.contexts.loading, options);
		
		commonHud = WM.commonHud(GLOBAL, WM, that.contexts.commonHud, options);
		commonHud.addEventListener("EXIT_GAME", function () {
			that.dispatchEvent("EXIT_GAME");
		});
		
		landscapeOnlyOverlay = WM.landscapeOnlyOverlay(GLOBAL, WM, that.contexts.landscapeOnly, options);
		
		successView = WM.successView(GLOBAL, WM, that.contexts.message);
		that.addView(successView);
	};

	// Create common game canvases
	that.addCanvas("background");
	that.addCanvas("main");
	that.addCanvas("message");
	
	GLOBAL.onunload = that.destroy;
	
	return that;	
};