var allImages = new Array(); // This array contains all the images (for pre-loading)
var boat; // This is the boat image
var popupImage; // This is the popup image for certain path points
var popupHead; // This is the popup image for the headshot
var popupDiv; // This is the popup message box
var myInterval; // This is the interval for moving the boat
// These values are the base map size used for the initial point values
var STANDARD_MAP_WIDTH = 550;
var STANDARD_MAP_HEIGHT = 444;
// This contains all the points along the path
var points;
// The starting point is the index of the first point along the path to use
var STARTING_POINT = 0;
// These values will update as the boat moves along the path
var nextPointIndex = STARTING_POINT + 1;
var currentPathIndex = 0;
var currentX = 0, currentY = 0;
var isMoving = false, processingStep = false;

var ORIENTATION_NE = 1;
var ORIENTATION_SE = 2;
var ORIENTATION_SW = 3;
var ORIENTATION_NW = 4;

var SPEAKER_DEAN = 0;
var SPEAKER_JON = 1;
var SPEAKER_DAVID = 2;
var SPEAKER_GINA = 3;
var SPEAKER_MAGGIE = 4;
var SPEAKER_CHRISTIE = 5;

// Returns an array containing the absolute coordinates of the upper left corner of the object
function findPos(obj) {
	var curleft = curtop = 0;
	if (obj.offsetParent) {
		curleft += obj.offsetLeft;
		curtop += obj.offsetTop;
		while (obj = obj.offsetParent) {
			curleft += obj.offsetLeft;
			curtop += obj.offsetTop;
		}
	}
	return [curleft,curtop];
}

// Returns N, S, E, W, NE, SE, SW, NW
function calculateShipDir(startX, startY, endX, endY) {
	if (endX == startX) {
		// Straight up or down
		if (endY > startY) {
			return "S";
		} else {
			return "N";
		}
	} else {
		var slope = (endY - startY) / (endX - startX);
		if ( Math.abs(slope) < BOAT_HORIZONTAL_SLOPE ) {
			// We're going straight left or right
			if (endX > startX) {
				return "E";
			} else {
				return "W";
			}
		} else if ( Math.abs(slope) > BOAT_VERTICAL_SLOPE ) {
			// We're going straight up or down
			if (endY > startY) {
				return "S";
			} else {
				return "N";
			}
		} else {
			// We're going diagonally.  Figure out the quadrant
			var quad = 0;
			if  (endY > startY) {
				if (endX > startX) {
					quad = 4;
				} else {
					quad = 3;
				}
			} else {
				if (endX > startX) {
					quad = 1;
				} else {
					quad = 2;
				}
			}
			switch (quad) {
				case 1: return "NE";
				case 2: return "NW";
				case 3: return "SW";
				case 4: return "SE";
			}
		}
	}
}

// Loads all the relevant images into memory for quick rendering
function preloadImages() {
	storeImage( MAP_IMG );
	storeImage( MAP_IMG_COMPLETE );
	storeImage( BOAT_IMG_N );
	storeImage( BOAT_IMG_S );
	storeImage( BOAT_IMG_E );
	storeImage( BOAT_IMG_W );
	storeImage( BOAT_IMG_NW );
	storeImage( BOAT_IMG_NE );
	storeImage( BOAT_IMG_SW );
	storeImage( BOAT_IMG_SE );
	storeImage( BUBBLE_IMG_NW );
	storeImage( BUBBLE_IMG_NE );
	storeImage( BUBBLE_IMG_SW );
	storeImage( BUBBLE_IMG_SE );
	storeImage( HEAD_IMG_DEAN );
	storeImage( HEAD_IMG_JON );
	storeImage( HEAD_IMG_DAVID );
	storeImage( HEAD_IMG_GINA );
	storeImage( HEAD_IMG_CHRISTIE );
	storeImage( HEAD_IMG_MAGGIE );
}

// Stores an image in the image array
function storeImage( imageArray ) {
	var tempImg;
	tempImg = new Image( imageArray[1], imageArray[2] );
	tempImg.src = imageArray[0];
	allImages[allImages.length] = tempImg;
}

// This is an object representing a vertex on the path
function point(index, mapLoc, map, xpos, ypos, ornt, sp, dur, txt ) {
	this.index = index;
	// Each point is scaled to the map's size and adjusted based upon the map's location
	this.x = mapLoc[0] + Math.round( xpos * ( map.offsetWidth / STANDARD_MAP_WIDTH ) );
	this.y = mapLoc[1] + Math.round( ypos * ( map.offsetHeight / STANDARD_MAP_HEIGHT ) );
	this.rawLength = 0; // Raw length of the segment ending at this point
	this.adjustedLength = 0; // Length of segment scaled according to the fraction of the total path
	this.totalLengthSoFar = 0; // Total adjusted length achieved when boat reaches this point
	if (ornt) {
		// We have a popup for this point.  Store the characteristics
		this.popup = true;
		this.orientation = ornt;
		this.speaker = sp;
		this.duration = dur;
		this.popupText = txt;
		this.popupComplete = false;
	} else {
		this.popup = false;
	}
	this.status = function() {
		var statusStr = "";
		statusStr += "Point #" + this.index + "\n";
		statusStr += "x:" + this.x + "\n";
		statusStr += "y:" + this.y + "\n";
		statusStr += "rawLength:" + this.rawLength + "\n";
		statusStr += "adjustedLength:" + this.adjustedLength + "\n";
		statusStr += "totalLengthSoFar:" + this.totalLengthSoFar + "\n";
		if (this.popup)
		{
			statusStr += "Popup message:" + this.popupText + "\n";
			statusStr += "Popup orientation:" + this.orientation + "\n";
			statusStr += "Popup speaker:" + this.speaker + "\n";
			statusStr += "Popup duration:" + this.duration + "\n";
		}
		alert(statusStr);
	}
}

// This function adds a point to the path
function addPoint(mapLoc, map, x, y, orientation, speaker, duration, popupText ) {
	points[points.length] = new point(points.length, mapLoc, map, x, y, orientation, speaker, duration, popupText);
}

// Called once... this assigns the correct lengths to each point
function initPathLengths() {
	var lastX = points[STARTING_POINT].x;
	var lastY = points[STARTING_POINT].y;
	var rawTotal = 0;
	for (var i = STARTING_POINT + 1; i < points.length; i++ ) {
		var pt = points[i];
		pt.rawLength = Math.sqrt( Math.pow( (pt.y - lastY), 2) + Math.pow( (pt.x - lastX), 2) );
		lastX = pt.x;
		lastY = pt.y;
		rawTotal += pt.rawLength;
	}
	var factor=TOTAL/rawTotal;
	var adjustedTotal = 0;
	for (var i = STARTING_POINT + 1; i < points.length; i++ ) {
		var pt = points[i];
		pt.adjustedLength = factor*pt.rawLength;
		adjustedTotal += pt.adjustedLength;
		pt.totalLengthSoFar = Math.round( adjustedTotal );
	}
}

// Add all the points of the path
function createPoints(map) {
	// Reset the points
	points = new Array();
	// Calculate the relative map location
	var mapLoc = findPos(map);
	// These are relative positions to the map scaled at (STANDARD_MAP_WIDTH, STANDARD_MAP_HEIGHT)
	addPoint(mapLoc, map, 102, 165); // 0
	addPoint(mapLoc, map, 106, 180, ORIENTATION_NE, SPEAKER_DEAN, 1.5, "And we're off... to Germany!");
	addPoint(mapLoc, map, 97, 185);
	addPoint(mapLoc, map, 96, 205, ORIENTATION_NE, SPEAKER_MAGGIE, 1.5, "Are we sure we're going the right way?");
	addPoint(mapLoc, map, 127, 217);
	addPoint(mapLoc, map, 133, 227, ORIENTATION_NE, SPEAKER_GINA, 1, "Positive.  Turn left here.");
	addPoint(mapLoc, map, 125, 236);
	addPoint(mapLoc, map, 110, 238);
	addPoint(mapLoc, map, 94, 245);
	addPoint(mapLoc, map, 84, 255);
	addPoint(mapLoc, map, 74, 261);  // 10
	addPoint(mapLoc, map, 75, 275, ORIENTATION_NE, SPEAKER_DEAN, 1, "Look it's Germany!");
	addPoint(mapLoc, map, 84, 306);
	addPoint(mapLoc, map, 82, 310);
	addPoint(mapLoc, map, 59, 317, ORIENTATION_NE, SPEAKER_MAGGIE, 1, "That was Germany?!?");
	addPoint(mapLoc, map, 55, 325);
	addPoint(mapLoc, map, 61, 335);
	addPoint(mapLoc, map, 68, 337);
	addPoint(mapLoc, map, 109, 329, ORIENTATION_NE, SPEAKER_DAVID, 1.5, "AAAAAHHHH!!! We're going to crash!");
	addPoint(mapLoc, map, 112, 320);
	addPoint(mapLoc, map, 116, 310); // 20
	addPoint(mapLoc, map, 127, 306);
	addPoint(mapLoc, map, 137, 308);
	addPoint(mapLoc, map, 140, 311);
	addPoint(mapLoc, map, 128, 329, ORIENTATION_NE, SPEAKER_DEAN, 1, "Give me that wheel!");
	addPoint(mapLoc, map, 137, 336);
	addPoint(mapLoc, map, 156, 333);
	addPoint(mapLoc, map, 176, 336);
	addPoint(mapLoc, map, 185, 335, ORIENTATION_NE, SPEAKER_JON, 1.5, "Wait, turn left here!");
	addPoint(mapLoc, map, 198, 360);
	addPoint(mapLoc, map, 207, 364, ORIENTATION_NE, SPEAKER_JON, 1, "No, the other left!");  // 30
	addPoint(mapLoc, map, 216, 357);
	addPoint(mapLoc, map, 221, 342);
	addPoint(mapLoc, map, 217, 323);
	addPoint(mapLoc, map, 211, 308);
	addPoint(mapLoc, map, 196, 285);
	addPoint(mapLoc, map, 180, 261);
	addPoint(mapLoc, map, 178, 241, ORIENTATION_NE, SPEAKER_JON, 1.5, "Look!  They have a Statue of Liberty in Germany, too!");
	addPoint(mapLoc, map, 185, 223);
	addPoint(mapLoc, map, 185, 210, ORIENTATION_NE, SPEAKER_DEAN, 1.5, "You bag of morons!  That's America!");
	addPoint(mapLoc, map, 190, 205);  // 40
	addPoint(mapLoc, map, 217, 220);
	addPoint(mapLoc, map, 212, 234);
	addPoint(mapLoc, map, 230, 255);
	addPoint(mapLoc, map, 230, 273);
	addPoint(mapLoc, map, 244, 290, ORIENTATION_NE, SPEAKER_GINA, 1.5, "Oh, how I love to sail the open seas!");  // 40
	addPoint(mapLoc, map, 258, 306);
	addPoint(mapLoc, map, 264, 324);
	addPoint(mapLoc, map, 267, 329);
	addPoint(mapLoc, map, 284, 334);
	addPoint(mapLoc, map, 289, 340); // 50
	addPoint(mapLoc, map, 298, 346);
	addPoint(mapLoc, map, 310, 352);
	addPoint(mapLoc, map, 316, 361);
	addPoint(mapLoc, map, 319, 367, ORIENTATION_NW, SPEAKER_DEAN, 1.5, "Hey, we just crossed the line!");  // EQUATOR CROSS
	addPoint(mapLoc, map, 325, 377, ORIENTATION_NW, SPEAKER_CHRISTIE, 1, "Which line?");
	addPoint(mapLoc, map, 339, 382, ORIENTATION_NW, SPEAKER_DEAN, 1, "The equator!");
	addPoint(mapLoc, map, 356, 377, ORIENTATION_NW, SPEAKER_CHRISTIE, 1, "THE <i>EQUATOR</i>?!?");
	addPoint(mapLoc, map, 364, 367);
	addPoint(mapLoc, map, 362, 358);
	addPoint(mapLoc, map, 349, 333); // 60
	addPoint(mapLoc, map, 348, 317, ORIENTATION_NW, SPEAKER_DAVID, 1, "Here, turn left.");
	addPoint(mapLoc, map, 349, 293, ORIENTATION_NW, SPEAKER_CHRISTIE, 1.5, "My left or Gina's left?");
	addPoint(mapLoc, map, 359, 281, ORIENTATION_NW, SPEAKER_DAVID, 1, "Does it matter?");
	addPoint(mapLoc, map, 368, 274);
	addPoint(mapLoc, map, 379, 271, ORIENTATION_NW, SPEAKER_MAGGIE, 2, "Funny... Germany seems a lot warmer than I remember it.");
	addPoint(mapLoc, map, 373, 262);
	addPoint(mapLoc, map, 375, 255);
	addPoint(mapLoc, map, 389, 247, ORIENTATION_NW, SPEAKER_DEAN, 2, "Wait... which side is larboard and which is starboard?");
	addPoint(mapLoc, map, 386, 240);
	addPoint(mapLoc, map, 381, 229, ORIENTATION_NW, SPEAKER_GINA, 1.5, "You know, he's not a real captain."); // 70
	addPoint(mapLoc, map, 387, 220, ORIENTATION_NW, SPEAKER_DAVID, 1, "Big surprise.");
	addPoint(mapLoc, map, 391, 222);
	addPoint(mapLoc, map, 394, 215);
	addPoint(mapLoc, map, 384, 205, ORIENTATION_SW, SPEAKER_JON, 1, "Let's go this way!");
	addPoint(mapLoc, map, 360, 199);
	addPoint(mapLoc, map, 344, 197);
	addPoint(mapLoc, map, 266, 225);
	addPoint(mapLoc, map, 255, 220);
	addPoint(mapLoc, map, 244, 203, ORIENTATION_SE, SPEAKER_CHRISTIE, 1, "No, this way!");
	addPoint(mapLoc, map, 251, 183); // 80
	addPoint(mapLoc, map, 267, 172);
	addPoint(mapLoc, map, 305, 175);
	addPoint(mapLoc, map, 350, 178);
	addPoint(mapLoc, map, 369, 176);
	addPoint(mapLoc, map, 375, 171, ORIENTATION_SW, SPEAKER_JON, 1.5, "No, the sun sets in the East!");
	addPoint(mapLoc, map, 373, 164);
	addPoint(mapLoc, map, 361, 160);
	addPoint(mapLoc, map, 318, 153);
	addPoint(mapLoc, map, 296, 138, ORIENTATION_SE, SPEAKER_MAGGIE, 1.5, "Only in the southern hemisphere.");
	addPoint(mapLoc, map, 296, 133, ORIENTATION_SE, SPEAKER_JON, 1, "Oh, yeah.  My bad."); // 90
	addPoint(mapLoc, map, 317, 121);
	addPoint(mapLoc, map, 382, 121, ORIENTATION_SW, SPEAKER_DAVID, 1.5, "Shoot!  I forgot my jacket!  We have to go back!");
	addPoint(mapLoc, map, 394, 113);
	addPoint(mapLoc, map, 392, 107);
	addPoint(mapLoc, map, 384, 103, ORIENTATION_SW, SPEAKER_DAVID, 1.5, "Wait... here it is.  Never mind.");
	addPoint(mapLoc, map, 382, 109);
	addPoint(mapLoc, map, 394, 117);
	addPoint(mapLoc, map, 409, 118);
	addPoint(mapLoc, map, 434, 114); // 99
}

// Called once.  Performs all page data initializing
function initMap() {
	// Load up the images
	preloadImages();
	// Set the points
	createPoints( document.getElementById( "imgMap" ) );
	// Initialize the path lengths
	initPathLengths();
}

function displayPopup(thisPoint) {
	// get the direction
	var popupDir = "NE";
	switch (thisPoint.orientation)
	{
		case ORIENTATION_NW : popupDir = "NW"; break;
		case ORIENTATION_NE : popupDir = "NE"; break;
		case ORIENTATION_SW : popupDir = "SW"; break;
		case ORIENTATION_SE : popupDir = "SE"; break;
	}
	eval("popupImage.src = BUBBLE_IMG_" + popupDir + "[0]");

	// move the popup image and text box to the correct location
	eval("popupImage.style.left = thisPoint.x + BUBBLE_OFFSET_HORIZ_" + popupDir);
	eval("popupImage.style.top = thisPoint.y + BUBBLE_OFFSET_VERT_" + popupDir);
	eval("popupDiv.style.left = thisPoint.x + POPUPTEXT_OFFSET_HORIZ_" + popupDir);
	eval("popupDiv.style.top = thisPoint.y + POPUPTEXT_OFFSET_VERT_" + popupDir);

	// Get the proper head image array
	var headImageArray;
	switch (thisPoint.speaker)
	{
		case SPEAKER_DEAN : headImageArray=HEAD_IMG_DEAN; break;
		case SPEAKER_JON : headImageArray=HEAD_IMG_JON; break;
		case SPEAKER_DAVID : headImageArray=HEAD_IMG_DAVID; break;
		case SPEAKER_GINA : headImageArray=HEAD_IMG_GINA; break;
		case SPEAKER_MAGGIE : headImageArray=HEAD_IMG_MAGGIE; break;
		case SPEAKER_CHRISTIE : headImageArray=HEAD_IMG_CHRISTIE; break;
	}

	// Get the location of the bubble image
	var bubbleLeftStr = popupImage.style.left.toString();
	var bubbleTopStr = popupImage.style.top.toString();
	var bubbleLoc = [ bubbleLeftStr.substr(0,bubbleLeftStr.length-2) * 1, bubbleTopStr.substr(0,bubbleTopStr.length-2) * 1];

	// Set up the head image
	popupHead.src = headImageArray[0];
	popupHead.width = headImageArray[1];
	popupHead.height = headImageArray[2];

	// move the headshot to the proper corner
	switch (thisPoint.orientation)
	{
		case ORIENTATION_NE : { popupHead.style.left = bubbleLoc[0] - 40; popupHead.style.top = bubbleLoc[1];  break; }
		case ORIENTATION_NW : { popupHead.style.left = bubbleLoc[0] - 40; popupHead.style.top = bubbleLoc[1];  break; }
		case ORIENTATION_SW : { popupHead.style.left = bubbleLoc[0] - 30; popupHead.style.top = bubbleLoc[1] + popupImage.height - popupHead.height + 50;  break; }
		case ORIENTATION_SE : { popupHead.style.left = bubbleLoc[0] - 30; popupHead.style.top = bubbleLoc[1] + popupImage.height - popupHead.height + 50;  break; }
	}

	// set the proper text
	popupDiv.innerHTML = thisPoint.popupText;
	// turn them both on
	popupImage.style.display = "block";
	popupHead.style.display = "block";
	popupDiv.style.display = "block";
	// mark the point
	thisPoint.popupComplete = true;
}

function hidePopupAndContinue() {
	popupDiv.style.display = "none";
	popupImage.style.display = "none";
	popupHead.style.display = "none";
	processingStep = false;
}

// This method is called continuously and it moves the boat along the path
function continueAlongPath() {
	// Check to see if we're in the process of dealing with a step
	if (!processingStep)
	{
		// We're not?  Well, we are now!
		processingStep = true;
		// Have we finished?
		if (currentPathIndex >= SO_FAR || currentPathIndex >= TOTAL) {
			// Finished!  Stop moving.
			currentPathIndex = SO_FAR;
			// Update the status
			document.getElementById("spanStatus").innerHTML = currentPathIndex;
			stopMoving();
			showFinalStatus(SO_FAR, TOTAL);
		} else {
			// Nope... keep going
			var nextPoint = points[nextPointIndex];
			// Have we reached (or overshot) the next point?
			if (currentPathIndex >= nextPoint.totalLengthSoFar) {
				// We got to the next point!
				if ( nextPoint.popup && !nextPoint.popupComplete ) {
					displayPopup(nextPoint);
					// Hide the popup after the proper amount of time
					setTimeout( hidePopupAndContinue, nextPoint.duration * 1000 );
					return;
				}
				// Set the position
				currentX = nextPoint.x;
				currentY = nextPoint.y;
				// Just in case we overshot the point, bring it back
				currentPathIndex = nextPoint.totalLengthSoFar;
				// Set the next point
				nextPointIndex++;
				nextPoint = points[nextPointIndex];
				// Set the ship direction for this segment
				boat.direction = calculateShipDir(currentX, currentY, nextPoint.x, nextPoint.y);
			} else {
				var upToLastPoint = points[nextPointIndex-1].totalLengthSoFar;
				var lastPointX = points[nextPointIndex-1].x;
				var lastPointY = points[nextPointIndex-1].y;
				// Figure out where we should be on this segment
				var fractionOfLegFinished = (currentPathIndex - upToLastPoint) / nextPoint.adjustedLength;
				// Move the boat to the appropriate place on this segment
				currentX = lastPointX + ( ( nextPoint.x - lastPointX ) * fractionOfLegFinished )
				currentY = lastPointY + ( ( nextPoint.y - lastPointY ) * fractionOfLegFinished )
			}
			// Set the graphic based upon the direction
			eval("boat.src = BOAT_IMG_" + boat.direction + "[0]");
			// Make sure we're displayed!
			boat.style.display = "";
			// Move the boat to where it belongs
			var newX = currentX + BOAT_OFFSET_X;
			if (boat.direction.indexOf('E') != -1) {
				newX += -20;
			} else if (boat.direction.indexOf('W') != -1) {
				newX += -20;
			}
			var newY = currentY + BOAT_OFFSET_Y;
			boat.style.left = newX + "px";
			boat.style.top = newY + "px";
			document.getElementById("spanStatus").innerHTML = currentPathIndex + ".00";
		}
		// Increment the path
		currentPathIndex += STEP;
		// And we're done
		processingStep = false;
	}
}

// Display all the points for debugging purposes
function displayPoints() {
	// Create a point for each vertex
	for (var ptInd = 0; ptInd < points.length; ptInd++) {
		document.write("<img id=\"dotImg" + ptInd + "\" src=\"images/progresschart/reddot.jpg\" style=\"position:absolute;top:" + points[ptInd].y + ";left:" + points[ptInd].x + ";\" onclick=\"points[" + ptInd + "].status();\" />");
	}
}

// When called, the boat will start moving along the path
function startMoving() {
	if (!isMoving) {
		isMoving = true;
		// Hide the play again link
		document.getElementById("playAgain").style.display = "none";
		// Start moving
		myInterval = setInterval(continueAlongPath, 1);
	}
}

// When called, the boat will stop where it is
function stopMoving() {
	if (isMoving) {
		isMoving = false;
		clearInterval(myInterval);
	}
}

// Set the boat back to the starting point
function resetBoat() {
	currentPathIndex = 0;
	nextPointIndex = STARTING_POINT + 1;
	currentX = 0;
	currentY = 0;
	// Reset the map image
	document.getElementById( "imgMap" ).src = MAP_IMG[0];

	// Store the boat
	boat = document.getElementById("imgBoat");
	// Store the popup image
	popupImage = document.getElementById("imgPopup");
	// Store the headshot image
	popupHead = document.getElementById("imgHeadshot");
	// Store the popup message box
	popupDiv = document.getElementById("divPopup");
	// Make sure the boat is above everything else
	boat.style.zIndex = 10;
	// set the initial boat direction
	boat.direction = calculateShipDir(points[STARTING_POINT].x, points[STARTING_POINT].y, points[STARTING_POINT+1].x, points[STARTING_POINT+1].y);
	// Reset all the displayImage flags
	for (var pointIndex = 0; pointIndex < points.length ; pointIndex++ ) {
		points[pointIndex].popupComplete = !points[pointIndex].popup;
	}
}

// Displays the final status at the end
function showFinalStatus(current, total) {
	document.getElementById("finalStatus").innerHTML = "<b>" + ( Math.round( (current*100*100)/total ) / 100 ) + "%</b> of our target <b>$" + total + "</b>";
	document.getElementById("playAgain").style.display = "";
	if (current >= total) {
		crosswipe( document.getElementById( "imgMap" ), MAP_IMG_COMPLETE[0], '2', 'cc', 'Thanks for all your help!');
	}
}