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

/*global window, WM */

WM.rollerCoaster = function (GLOBAL, WM, options) {
	
	"use strict";
	
	var that = WM.game(GLOBAL, WM, options),
		hud,
		backgroundView,
		currentRound = 0,
		targetViews = [],
		dataPoints = {},
		paths = [],
		idleTimeoutAmount = 12000,
		lastUserInputTime,
		rider,
		curPathIndex = 0,
		curDataPointIndex = 0,
		curState = "INTRO",
		pathDrawing,
		timeoutActive = false,
		timeoutDir = 1,
		timeoutStart,
		timeoutInterval = 2000,
		payoffView,
		background,
		successIndex,
		successInterval = 60,
		roundsNotPlayed = [],
		lastTouchPoint,
		payoff,
		payoffIndex,
		payoffInterval = 16,
		roundEndIndex,
		roundEndInterval = 60 * 2,
		successPath,
		lastRoundPlayed,
		
		rideAnimationComplete = function () {

			rider.removeEventListener("ANIMATION_COMPLETE", rideAnimationComplete);
			rider.setState("RIDE");
			that.playSound("PAYOFF");
			curState = "SUCCESS";
		},
		
		coasterComplete = function () {
			var i;
			
			for (i = 0; i < targetViews.length; i += 1) {
				if (!targetViews[i].active) {
					return false;
				}
			}
			return true;
		},
		
		roundComplete = function () {
			
			WM.logStats({
			    event: "round.end",
			    title: that.gameTitle,
			    id: that.gameId,
			    params : {
			    	round: currentRound
			    }
			});
			that.roundSuccess();
		},
		
		//  Loads the resources for the round specified via parameter
		//
		loadRoundResources = function (roundIndex) {
			var i,
				s,
				specs = {};
		
			// Load background image
			if (options.rounds[roundIndex].background.filename) {				
				that.resourcer.add(options.rounds[roundIndex].background.filename);
				options.rounds[roundIndex].background.imageResource = that.resourcer.get(options.rounds[roundIndex].background.filename);
			} else {
				WM.error({
					name: "ResourceMissing", 
					message: "Background not specified in configuration file for round " + roundIndex + "."
				});	
			}
			
			if (options.rounds[roundIndex].rider) {
				// Load rider images
				specs.rider = options.rounds[roundIndex].rider;	
				for (s = 0; s < specs.rider.states.length; s += 1) {
					that.resourcer.add(specs.rider.states[s].filename);
					specs.rider.states[s].imageResource = that.resourcer.get(specs.rider.states[s].filename);
				}
			} else {
				WM.error({
					name: "ConfigDataMissing", 
					message: "No rider character specified in configuration file for round " + roundIndex + "."
				});	
			}
			
			if (options.rounds[roundIndex].targetItems && options.rounds[roundIndex].targetItems.length) {
				for (i = 0; i < options.rounds[roundIndex].targetItems.length; i += 1) {
					that.resourcer.add(options.rounds[roundIndex].targetItems[i].filename);
				}
			} else {
				WM.error({
					name: "ConfigDataMissing", 
					message: "No data points specified in configuration file for round " + roundIndex + "."
				});	
			}
			
			// Load optional payoff track
			if (options.rounds[roundIndex].payoff) {
				that.resourcer.add(options.rounds[roundIndex].payoff.filename);
			}
		},
		
		introComplete = function () {
			pathDrawing.removeEventListener("DRAW_COMPLETE", introComplete);
			curState = "PLAY";
		},
		
		introAudioComplete = function (point) {
		
			if (that.audio) {
				that.audio.removeEventListener("COMPLETE", introAudioComplete);
				that.playMusic();
			}
				
			pathDrawing.addEventListener("DRAW_COMPLETE", introComplete);
			pathDrawing.visible = true;
			pathDrawing.draw();
		};

	// Initialization
	//
	that.load = function () {
		
		var i;

		for (i = 0; i < options.rounds.length; i += 1) {
			loadRoundResources(i);
		}

		that.resourcer.addEventListener("LOAD_PROGRESS", that.onResourceLoaded);
		that.resourcer.addEventListener("RESOURCES_LOADED", that.onResourcesLoaded);
		that.resourcer.loadAll();	
	};
	
	that.setup = function () {
		
		var view,
			dataPoint,
			path,
			i,
			j,
			s;
		
		// Choose a random target
		if (roundsNotPlayed.length === 0) {
			for (i = 0; i < options.rounds.length; i += 1) {
				roundsNotPlayed.push(i);
			}
			if (options.rounds.length > 1) {
				while (true) {
					roundsNotPlayed.sort(function () {
						return (Math.round(Math.random()) - 0.5);	
					});
					if (lastRoundPlayed === undefined || roundsNotPlayed[0] !== lastRoundPlayed) {
						break;
					}
				}
			}
		}
		
		// Set the current target to the first item in the array then remove that item
		currentRound = roundsNotPlayed[0];
		roundsNotPlayed.shift();
		lastRoundPlayed = currentRound;
		
		
		// DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG
		if (options.showOnlyThisRound !== undefined) {
			roundsNotPlayed = [];
			currentRound = options.showOnlyThisRound;
		}
		// DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG
		
		// Create background
		background = WM.sprite(GLOBAL, WM, that.contexts.background, options.rounds[currentRound].background);
		background.type = "BACKGROUND";
		background.offsetX = that.overallOffset.x,
		background.offsetY = that.overallOffset.y
		that.addView(background);
		
		// Create rider
		rider = WM.actor(GLOBAL, WM, options.rounds[currentRound].rider);
		for (s = 0; s < options.rounds[currentRound].rider.states.length; s += 1) {
			options.rounds[currentRound].rider.states[s].ctx = that.contexts.rider;
			rider.addState(options.rounds[currentRound].rider.states[s]);
		}
		rider.speed = options.rounds[currentRound].rider.speed || 5;
		rider.x = that.viewportWidth - rider.width;
		rider.y = that.viewportHeight - rider.height;
		that.addView(rider);

		// Create data points
		if (options.rounds[currentRound].dataPoints && options.rounds[currentRound].dataPoints.length) {
			for (i = 0; i < options.rounds[currentRound].dataPoints.length; i += 1) {
				dataPoint = options.rounds[currentRound].dataPoints[i];
				dataPoints[dataPoint.i] = {
					x: dataPoint.x + that.overallOffset.x,
					y: dataPoint.y + that.overallOffset.y
				}
			}
		} else {
			WM.error({
				name: "ConfigDataMissing", 
				message: "No data points specified in configuration file for round " + currentRound + "."
			});	
		}
		
		// Create hint paths
		if (options.rounds[currentRound].hintPaths && options.rounds[currentRound].hintPaths.length) {
			for (i = 0; i < options.rounds[currentRound].hintPaths.length; i += 1) {
				paths[i] = [];
				path = options.rounds[currentRound].hintPaths[i];
				for (j = 0; j < path.length; j += 1) {
					if (dataPoints[path[j]]) {
						paths[i].push(dataPoints[path[j]]);
					} else {
						WM.error({
							name: "DataPointMissing", 
							message: "Data Point index (" + path[j] + ") not found in configuration file for round " + currentRound + "."
						});	
					}
				}
			}
		} else {
			WM.error({
				name: "ConfigDataMissing", 
				message: "No paths specified in configuration file for round " + currentRound + "."
			});	
			return;
		}	
		
		// Add drawing that will trace the path
		pathDrawing = WM.path(GLOBAL, WM, that.contexts.path, options.hint);
		pathDrawing.visible = false;
		pathDrawing.paths = paths;
		that.addView(pathDrawing);
		
		// Create success path
		if (options.rounds[currentRound].successPath && options.rounds[currentRound].successPath.length) {

				successPath = [];
				path = options.rounds[currentRound].successPath;
				for (j = 0; j < path.length; j += 1) {
					if (dataPoints[path[j]]) {
						successPath.push(dataPoints[path[j]]);
					} else {
						WM.error({
							name: "DataPointMissing", 
							message: "Data Point index (" + path[j] + ") not found in configuration file for round " + currentRound + "."
						});	
					}
				}
		} else {
			WM.error({
				name: "ConfigDataMissing", 
				message: "No success path specified in configuration file for round " + currentRound + "."
			});	
			return;
		}

		for (i = 0; i < options.rounds[currentRound].targetItems.length; i += 1) {
			view = WM.targetView(GLOBAL, WM, that.contexts.track, {
				imageResource: that.resourcer.get(options.rounds[currentRound].targetItems[i].filename),
				numFrames: options.rounds[currentRound].targetItems[i].numFrames,
				autoStart: options.rounds[currentRound].targetItems[i].autoStart,
				loop: options.rounds[currentRound].targetItems[i].loop,
				x: options.rounds[currentRound].targetItems[i].x + that.overallOffset.x,
				y: options.rounds[currentRound].targetItems[i].y + that.overallOffset.y
			});
			targetViews.push(view);
			that.addView(view);
		}
		
		// Setup optional payoff track
		if (options.rounds[currentRound].payoff) {
			payoff = WM.view(GLOBAL, WM, that.contexts.payoff, {
				type: "PAYOFF",
				x: (options.rounds[currentRound].payoff.x + that.overallOffset.x) * window.innerWidth / 1024,
				y: (options.rounds[currentRound].payoff.y + that.overallOffset.y) * window.innerHeight / 768,
				imageResource: that.resourcer.get(options.rounds[currentRound].payoff.filename),
				numFrames: options.rounds[currentRound].payoff.numFrames,
				autoStart: options.rounds[currentRound].payoff.autoStart,
				loop: options.rounds[currentRound].payoff.loop,
				alwaysDirty: true
			});
		}
		payoff.visible = false;
		that.addView(payoff);
		
		if (that.audioEnabled) {
			that.audio.clearAllSounds();
			// Add game audio if it exists
			if (options.audio) {
				for (i = 0; i < options.audio.states.length; i += 1) {
					that.audio.setSound(options.audio.states[i]);
				}
			}
			// Add round audio if it exists
			if (options.rounds[currentRound].audio) {
				for (i = 0; i < options.rounds[currentRound].audio.states.length; i += 1) {
					that.audio.setSound(options.rounds[currentRound].audio.states[i]);
				}
			}
		}
		
		// Clear canvases
		that.clearCxt(that.contexts.transition);

		// Add events
		that.controller.addEventListener("TOUCH_START", that.touchStart);
		that.controller.addEventListener("TOUCH_MOVE", that.touchMove);
		that.controller.addEventListener("TOUCH_END", that.touchEnd);
		that.controller.addEventListener("ORIENTATION_CHANGE", that.onOrientationChange);
		
		lastUserInputTime = + new Date();
		timeoutStart = + new Date();

		payoffIndex = 0;
		successIndex = 0;
		curPathIndex = 0;
		curDataPointIndex = 0;
		rider.visible = false;
		
		rider.x = successPath[curDataPointIndex].x;
		rider.y = successPath[curDataPointIndex].y;

		WM.logStats({
		    event: "round.start",
		    title: that.gameTitle,
		    id: that.gameId,
		    params : {
		    	round: currentRound
		    }
		});
		curState = "INTRO";
		if (that.playSound("INTRO")) {
			that.audio.addEventListener("COMPLETE", introAudioComplete);
		} else {
			introAudioComplete();
		}
	};
	
	//  Clear resources after round
	//
	that.breakdown = function () {
		var i,
			curView,
			viewList = [];
			
		curState = "BREAKDOWN";
			
		for (i = 0; i < that.views.length; i += 1) {
			viewList.push(that.views[i])
		}

		for (i = 0; i < viewList.length; i += 1) {

			that.removeView(viewList[i]);
			viewList[i].destroy();
		}
		
		targetViews = [];
		dataPoints = {};
		paths = [];
	};
	
	// Update the game state
	//
	that.update = function () {
		
		var i,
			curView,
			now = + new Date(),
			magnitude,
			curVector,
			targetVector,
			dx,
			dy;
		
		if ((now - lastUserInputTime) > idleTimeoutAmount) {
			that.contexts.path.globalAlpha = 1;
			timeoutActive = true;
			lastUserInputTime = now;
			timeoutStart = now;
		}
		if (timeoutActive) {
			that.contexts.path.globalAlpha += 0.1 * timeoutDir;
			if (that.contexts.path.globalAlpha >= 1) {
				timeoutDir = -1;
			} else if (that.contexts.path.globalAlpha <= 0.1) {
				timeoutDir = 1;
			}
			if ((now - timeoutStart) > timeoutInterval) {
				that.contexts.path.globalAlpha = 1;
				timeoutActive = false;
				
				WM.logStats({
			    event: "timeout",
			    title: that.gameTitle,
			    id: that.gameId
			});
			}
		}
		
		if (curState === "START_RIDER_INTRO") {

			rider.addEventListener("ANIMATION_COMPLETE", rideAnimationComplete);
			rider.setState("INTRO");
			rider.visible = true;
			curState = "RIDER_INTRO";
		} else if (curState === "SUCCESS") {
			
			if (successPath[curDataPointIndex]) {
			
				if (payoffIndex >= payoffInterval){
					payoff.visible = !payoff.visible;
					payoff.dirty = true;
					payoffIndex = 0;
				} else {
					payoffIndex +=1;
				}

				targetVector = {
					x: successPath[curDataPointIndex].x, 
					y: successPath[curDataPointIndex].y
				};
				curVector = {
					x: rider.x, 
					y: rider.y
				};
				
				dx = targetVector.x - curVector.x;
				dy = targetVector.y - curVector.y;				
				magnitude = Math.sqrt(dx * dx + dy * dy);
				
				if (magnitude > rider.speed) {
					
					rider.x += rider.speed * dx / magnitude;
					rider.y += rider.speed * dy / magnitude;
					
				} else {
 
					rider.x = targetVector.x;
					rider.y = targetVector.y;
					if (curDataPointIndex >= successPath.length - 1) {
					
						curState = "START_PAYOFF";
						
					} else {
						curDataPointIndex += 1;
					}
				}
			}
		} else if (curState === "START_PAYOFF") {
		
			rider.visible = false;
			if (payoffIndex >= payoffInterval) {
				curState = "PAYOFF_COMPLETE"
			} else {
				payoffIndex += 1;
			}
			
		} else if (curState === "PAYOFF_COMPLETE") {
		
			roundEndIndex = 0;
			curState = "ROUND_END_TRANSITION";
			
		} else if (curState === "ROUND_END_TRANSITION") {
		
			if (roundEndIndex >= roundEndInterval) {
				roundComplete();
			} else {
				roundEndIndex += 1;
			}
			
		}
		
		for (i = 0; i < that.views.length; i += 1) {
			
			that.views[i].update();
		}
	};
	
	that.render = function () {
		
		var i,
			j;
		
		that.clearCxt(that.contexts.track);
		that.clearCxt(that.contexts.path);
		
		if (rider.dirty) {
			that.clearCxt(that.contexts.payoff);
		}
		
		if (rider.dirty) {
			that.clearCxt(that.contexts.rider);
		}
		
		if (curState === "ROUND_END_TRANSITION") {
			that.clearCxt(that.contexts.transition);
			that.contexts.transition.fillStyle = "rgba(255, 255, 255, " + (roundEndIndex / roundEndInterval) + ")";
			that.contexts.transition.fillRect(0, 0, that.viewportWidth, that.viewportHeight);
		}
	
		for (i = 0; i < that.views.length; i += 1) {
			
			that.views[i].render();
		}
	};
	
	// Fired when user touches the screen
	//
	that.touchStart = function (point) {
		
		var i,
			now = + new Date(),
			touchNothing = true;
				
		if (!that.paused) {
			
			lastUserInputTime = now;
			lastTouchPoint = point;
			
			if (curState === "PLAY") {

				for (i = 0; i < targetViews.length; i += 1) {
					
					if (!targetViews[i].active && targetViews[i].rectangleCollisionDetection({
						x: point.x, 
						y: point.y, 
						width: 1, 
						height: 1
					})) {
						touchNothing = false;
						WM.logStats({
						    event: "answer.selected",
						    title: that.gameTitle,
						    id: that.gameId,
						    params : {
						    	object: undefined,
						    	correct: true
						    }
						});
						targetViews[i].active = true;
					}
				}
				if (touchNothing) {
					WM.logStats({
					    event: "na.click",
					    title: that.gameTitle,
					    id: that.gameId,
					    params : {
					    	coordinates: point.x + ", " + point.y
					    }
					});
				}
			} else if (curState === "INTRO") {
				WM.logStats({
				    event: "vo.click",
				    title: that.gameTitle,
				    id: that.gameId,
				    params : {
				    	vo: "INTRO"
				    }
				});
			}
		}
	};
	
	that.touchMove = function (point) {
		var now = + new Date(),
			i,
			j,
			d,
			dx,
			dy,
			nx,
			ny,
			collide,
			resolution,
			curLoc = {};
			
		if (!that.paused && that.controller.dragging) {
			
			lastUserInputTime = now;
			
			if (curState === "PLAY") {
				
				d = WM.math.vectors.dist(point, lastTouchPoint);
				
				// If the target is moving
				if (d) {
					dx = point.x - lastTouchPoint.x;
					dy = point.y - lastTouchPoint.y;
					
					resolution = GLOBAL.Math.ceil(d);
					
					nx = dx / resolution;
					ny = dy / resolution;
					
					collide = false;
					
					curLoc.x = lastTouchPoint.x;
					curLoc.y = lastTouchPoint.y;
					
					// For the number of increments
					for (j = 0; j < resolution; j += 1) {
					
						curLoc.x += nx;
						curLoc.y += ny;
						
						for (i = 0; i < targetViews.length; i += 1) {
				
							if (!targetViews[i].active && targetViews[i].rectangleCollisionDetection({
								x: curLoc.x, 
								y: curLoc.y, 
								width: 1, 
								height: 1
							})) {
								WM.logStats({
								    event: "answer.selected",
								    title: that.gameTitle,
								    id: that.gameId,
								    params : {
								    	object: undefined,
								    	correct: true
								    }
								});
								targetViews[i].active = true;
							}
						}
					}
					lastTouchPoint = curLoc;
				}
				if (coasterComplete()) {
					
					curState = "START_RIDER_INTRO";
				}
			}
		}
	};
	
	that.touchEnd = function (point) {
		var i,
			now = + new Date();
			
		if (!that.paused) {
			
			lastUserInputTime = now;
			
			if (curState === "PLAY") {
				
				if (coasterComplete()) {
					
					curState = "START_RIDER_INTRO";
				}		
			}
		}
	};
	
	that.gameTitle = "Roller Coaster";
	that.gameId = "RollerCoaster";

	that.addCanvas("path");
	that.addCanvas("track");
	that.addCanvas("payoff")
	that.addCanvas("rider");
	that.addCanvas("transition");
	
	that.init();
	
	return that;
}

