

// SHARED
var cameras = []; // array of active camera IDs
var camCookie = "camCookie";
var camCookieParams = { expires: 365 };

// SINGLE CAMERA
var camIdx = 0; // index into the array of cameras being displayed
var camRefreshTimer; // timed interval for refreshing the next camera
var camRefreshSpeed = 180000; // refresh speed in milliseconds (3min = 180000)
var camRotateTimer; // timed interval for moving to the next camera
var camRotateSpeed = 5000; // rotation speed in milliseconds
var isCamRotating = true;

// DASHBOARD
var camDashID = "cameraDashboard";
var camDashRefresh = true; // default is true when a cookie doesn't exist
var camDashRefreshTimer;
var camDashRefreshSpeed = 180000;  // (3min = 180000)
var camDashRefreshCookie = "camDashRefresh";

// MAP
var camMapIcon;
var camMap; // Google map
var camMapID = "camMap";
var camMapCenterX = 43.741169984847446;
var camMapCenterY = -79.42590809499603;

// GRAPHICS
//var camGraphicsPath = "images/traffic/"; // path to the traffic images
var camGraphicsPath = "/images/traffic/";
var camGraphics = [];
camGraphics["single-loading"] = camGraphicsPath+"camera-loading.gif";
camGraphics["single-notfound"] = camGraphicsPath+"camera-not-found.gif";
camGraphics["single-none"] = camGraphicsPath+"camera-none.gif";
camGraphics["dash-loading"] = camGraphicsPath+"camera-loading-s.gif";
camGraphics["dash-notfound"] = camGraphicsPath+"camera-not-found.gif";
camGraphics["map-icon"] = camGraphicsPath+"cameraIcon-car.png";

// LANGUAGE
var camLang = [];
camLang["cam-loading"] = "Loading...";
camLang["fav-add"] = "Add to Favourites";
camLang["fav-del"] = "Remove";
camLang["fav-edit"] = "Edit Cameras";
camLang["btn-stop"] = "stop";
camLang["btn-play"] = "play";
camLang["msg-incompat"] = "<p>Sorry, the Google Maps API is not compatible with this browser</p>";
camLang["msg-cookies"] = "You must <a href='http://www.google.com/cookies.html'>enable cookies</a> to save your favourite cameras.";
camLang["msg-nofavs"] = "You haven't picked any favourite traffic cameras.";


// DEBUG
var camDebug = false;


/***************************************************************
CAMERA ROTATOR (FOR SIDEBAR)
***************************************************************/

/**
 * Initialization function for traffic cameras displayed
 * in a user configurable rotation.
 * Only call this function once!
 */
function initCameraSingle() {
	
	cameraLog("initCameraSingle():");
	
	if (loadCameraFavouites()) {
		$("#camerasMore").text(camLang["fav-edit"]);
	}
	
	// Bind click events to the camera controls/buttons
	$("#cameraPrev").click(function() { prevCamera(); return false;	});
	$("#cameraNext").click(function() { setCameraTimers(false); nextCamera(); return false;	});
	$("#cameraPlayStop").click(function() {
		setCameraTimers(!isCamRotating);
		if(isCamRotating) {
			nextCamera();
		}
		return false;
	});
	
	updateCameraControls(); 	// set the state of the buttons
	updateCameraSingle(); 	// display the first camera immediately	
}

/**
 * Update the current camera image displayed. Call this function after
 * changing the camIdx value. The src attribute of the image (homeImgCamera) 
 * is updated to reflect the new image URL.
 */
function updateCameraSingle() {
	
	if(cameras[camIdx]) {
	
		var src = imgArray[cameras[camIdx]];
		var label = labelArray[cameras[camIdx]];
		
		// temporarily stop rotation until the next camera loads
		rotateTimer(false);
		
		// Display a loading placeholder
		if(cameras.length > 1) {
			cameraLabel(camLang["cam-loading"]);
			cameraImage(camGraphics["single-loading"]);
		}
		
		// Preload the next camera image (to reduce flicker) and
		// display the image if successfully loaded. Load a Not Found
		// placeholder if the image is not found.
		$.preload( [ src ], {
			onFinish : function(data) {
				if(data.found) {
					cameraLabel(label);
					cameraImage(src);
				} else {
					cameraLabel(label);
					cameraImage(camGraphics["single-notfound"]);
				}
				rotateTimer(isCamRotating); // continue rotating
			}
		});	
		
		
	} else {
		// The camera was removed. The existing timed intervals
		// will move to the next one. If no cameras exist, we should
		// display a special graphic until one does.
		if(cameras.length < 1) {
			cameraImage(camGraphics["single-none"]);
			cameraLabel(camLang["msg-nofavs"]);
		}
	}
	
	cameraLog("updateCameraSingle()",cameras[camIdx],labelArray[camIdx],imgArray[camIdx]);
		
}


/**
 * Turns the rotation interval on or off.
 */
function rotateTimer(on) {
	if(on) {
		camRotateTimer = setInterval("nextCamera()",camRotateSpeed);	
	} else {
		clearInterval(camRotateTimer); // stops the interval	
	}
}

/**
 * Turns the refreshing interval on or off.
 */
function refreshTimer(on) {
	if(on) {
		camRefreshTimer = setInterval("updateCameraSingle()",camRefreshSpeed);	
	} else {
		clearInterval(camRefreshTimer); // stops the interval	
	}
}

/**
 * Sets the camera timers:
 *  - If rotation is on, set a timer to go to the next camera
 *  - If rotation is off, set a timer to refresh the camera
 */
function setCameraTimers(rotate) {
	
	isCamRotating = rotate;
	clearInterval(camRotateTimer);
	clearInterval(camRefreshTimer);
	
	if(isCamRotating) {
		rotateTimer(true); // stops the interval
	} else {
		refreshTimer(true);
	}
	
	updateCameraControls();
}


/**
 * Display the next traffic camera in the home Camera Array
 */
function nextCamera() {
	camIdx = getNextcamIdx();
	updateCameraSingle(); // calls function to switch camera passing in new ID
}

/**
 * Returns the index for the next camera in the cameras.
 * This implementation supports wrapping around to the first camera.
 */
function getNextcamIdx() {
	if (cameras[camIdx+1]) {
		return camIdx+1;
	} else {
		return 0;
	}
}

/**
 * Display the previous traffic camera in the cameras.
 */
function prevCamera() {
	setCameraTimers(false); // if we are going backwards, we automatically stop the rotation
	camIdx = getPrevcamIdx();
	updateCameraSingle(); // calls function to switch camera passing in new ID
}

/**
 * Returns the index for the previous camera in the cameras.
 * This implementation supports wrapping around to the last camera if
 * the current display is the first camera.
 */
function getPrevcamIdx() {
	if (camIdx == 1) {
		return cameras.length-1;
	} else {
		return camIdx-1;
	}
}

/**
 * Sets the state of the camera navigation buttons
 * depending on the current settings.
 */
function updateCameraControls() {
	if(isCamRotating) {
		$("#cameraPlayStop").text(camLang["btn-stop"]);
	} else {
		$("#cameraPlayStop").text(camLang["btn-play"]);
	}	
}


/***************************************************************
DASHBOARD FUNCTIONS
***************************************************************/

/**
 * Initialize the display of the visitors favourite traffic cameras
 * (dashboard) as stored in a cookie.
 */
function initCameraDashboard() {
	
	cameraLog("initCameraDashboard()");
	
	if($.isCookiesOn()) {
	
		// if there are no favourites
		// don't show the default cameras.
		if(!loadCameraFavouites()) {
			cameras = [];
		}
		bindDashboardControls();
		updateCameraDashboard();
	
	} else {
		
		dashboardMsg(camLang["msg-cookies"]);
		
	}

}

/**
 * Update all cameras in the dashboard with the latest camera image.
 */
function updateCameraDashboard() {
	
	if(cameras.length > 0) {
		
		$("#cameraDashMsg").hide();
		$("#cameraRefresh").show();
		$("#cameraRemoveAll").show();
		
		// show all favourite cameras
		for(var i=0; i< cameras.length; i++) {
			showCamera(cameras[i]);
		}
		
	} else {
		
		dashboardMsg(camLang["msg-nofavs"]);
		
	}
}

/**
 * Binds click events to the dashboard controls (refresh, remove all)
 */
function bindDashboardControls() {
	
	$("#cameraRemoveAll").click(function() {
		removeCameraFavs();
		updateCameraDashboard();
	});
	
	camDashRefresh = $.cookie(camDashRefreshCookie) == null;
	setDashboardRefreshTimer(camDashRefresh);

}


/**
 * Adds a given camera (ID) to the user's list of favourites.
 * Updates the camera array and saves it to a cookie.
 */
function addCameraFav(id) {
	
	cameraLog("addCameraFav()",id);

	if(indexOfCamFav(id) == -1) {
		
		urchinTracker('/traffic/favourite-added');
		
		cameras.push(id);
		saveCameras();
		bindCameraPopAction(id);
		updateCameraDashboard();
		
		
	}
	camMap.closeInfoWindow();
}

/**
 * Removes a given camera (ID) from the user's list of favourites.
 * Updates the camera array and saves it to a cookie.
 */
function removeCameraFav(id) {
	
	var removeID = indexOfCamFav(id);
	cameraLog("removeCameraFav()",id);
	
	if(removeID > -1) {
		cameras.remove(removeID);
		saveCameras();
		bindCameraPopAction(id);
		$("#camera"+id).fadeOut("slow"); // hides the camera from the dashboard
		updateCameraDashboard();

	}
	camMap.closeInfoWindow();
}

/**
 * Removes ALL camera favourites
 */
function removeCameraFavs() {
	for(var i=0; i<= cameras.length; i++) {
		$("#camera"+cameras[i]).fadeOut("slow"); // hides the camera from the dashboard
	}
	cameras = []; // empty the array
	saveCameras();
}


/**
 * Shows a camera (by ID) in the favourites dashboard. The generated DOM 
 * chunk is a new list item.
 */
function showCamera(id) {
	
	var camID= "camera"+id;
	var imgID = "img"+id;
	
	// checks if camera exists as it could have been
	// added and hidden when removed
	if( $("#"+camID).length ) { 
	
		$("#"+camID).show(); // reshows the hidden camera
		
	// add the html to the dashboard
	} else {
		var cam = [];
		cam.push("<li id='camera"+id+"' class='camera'>");
		cam.push("<label>"+labelArray[id]+"</label>");
		cam.push("<img id='"+imgID+"' src='"+camGraphics["dash-loading"]+"'>");
		cam.push("<a>Remove</a></li>");

		$("#"+camDashID).append(cam.join(''));
		
		$("#"+camID+" a").click(function(){
			removeCameraFav(id);
			return false;
		});
		
	}
	
	var imgSrc = imgArray[id] + cacheKill();
	
	$.preload( [ imgSrc ], {
		onFinish : function(data) {
			if(data.found) {
				cameraImage(imgSrc,id);
			} else {
				cameraImage(camGraphics["dash-notfound"],id);
			}
		}
	});	
	
	
	cameraLog("showCamera()",id);
	
}

/**
 * Returns a query parameter to kill any caching that
 * might occur with the traffic camera image.
 */
function cacheKill()
{
		return "?ck=" + new Date().getTime();
}

/**
 * Turns dashboard refreshing on or off.
 */
function setDashboardRefreshTimer(on){
	
	// check if the user turned refresh off
	if(!on) {
		
		$("#cameraRefreshOff").addClass("set");
		$("#cameraRefreshOn").removeClass("set");
		$("#cameraRefreshOff").unbind("click");
		$("#cameraRefreshOn").click(function(){setDashboardRefreshTimer(true);});
		
		$.cookie(camDashRefreshCookie,"off",camCookieParams);
		clearInterval(camDashRefreshTimer);
		
	// otherwise, turn on dashboard refreshing
	} else {
		
		$("#cameraRefreshOn").addClass("set");
		$("#cameraRefreshOff").removeClass("set");
		$("#cameraRefreshOn").unbind("click");
		$("#cameraRefreshOff").click(function(){setDashboardRefreshTimer(false);});
		
		$.cookie(camDashRefreshCookie,null); // delete cookie
		camDashRefreshTimer = setInterval("dashboardRefresh()",camDashRefreshSpeed);
	}	
	
	cameraLog("setDashboardRefreshTimer()",on);
	
}

/**
 * Refreshes all camera images in the dashboard by looping
 * through the cameras and setting their source to the same
 * source, prompting the browser to get the latest version.
 */
function dashboardRefresh(){

	var src;

	for(var i=0; i<=cameras.length; i++) {
		src = imgArray[cameras[i]] + cacheKill();
		//src = $("#camera"+cameras[i]+" img").attr("src");
		$("#camera"+cameras[i]+" img").attr("src",src);
	}
	
	cameraLog("dashboardRefresh()",cameras);
	
}

/**
 * Saves the current array of cameras to a
 * cookie for loading later (favourites).
 */
function dashboardMsg(msg) {
	$("#cameraRefresh").hide();
	$("#cameraRemoveAll").hide();
	$("#cameraDashMsg").html(msg);
	$("#cameraDashMsg").show();
}


/**
 * Saves the current array of cameras to a
 * cookie for loading later (favourites).
 */
function saveCameras() {
	if(cameras.length) {
		$.cookie(camCookie,cameras.join('|'), camCookieParams);
	} else {
		$.cookie(camCookie,null);
	}
	cameraLog("saveCameras()",cameras);
}

/***************************************************************
MAP FUNCTIONS
***************************************************************/


/**
 * Initialize the Google Map and adds all of the camera Map Markers
 */
function initCameraMap() {
	
		
	$(function(){ // called after the page has finished loading
	
		cameraLog("initCameraMap()");
		
		if (GBrowserIsCompatible()) {
			
			camMapIcon = new GIcon();
			camMapIcon.image = camGraphics["map-icon"];
			camMapIcon.iconSize = new GSize(12, 11);
			camMapIcon.iconAnchor = new GPoint(0, 30);
			camMapIcon.infoWindowAnchor = new GPoint(10, 10);	
			
			camMap = new GMap2(document.getElementById(camMapID));
			camMap.addControl(new GLargeMapControl());
			camMap.addControl(new GMapTypeControl());
			camMap.setCenter(new GLatLng(camMapCenterX, camMapCenterY), 11);
			
			// Add a map marker for each camera
			for (var id = 0; id <= latArray.length; id++) {
				if(latArray[id] && lngArray[id]) {
					addCameraMarker(id);
				}
			}		
			
		} else {
			$("#camMap").html(camLang["msg-incompat"]);
		}
		
		
  });

}

/**
 * Creates a Google Map Marker (GMarker), attaches click events to the marker
 * for displaying the informatino window, and adds the marker to the map.
 */
function addCameraMarker(id) {
	
	var point = new GLatLng(latArray[id],lngArray[id]);
	var html = cameraMapPopupHtml(id);
	var marker = new GMarker(point,camMapIcon);
	
	GEvent.addListener(marker, "click", function() {
		marker.openInfoWindowHtml(html);
		bindCameraPopAction(id);
		
		$.preload( [ imgArray[id] ], {
			onFinish : function(data) {
				if(data.found) {
					$("#cameraPop"+id+" img").attr("src",imgArray[id]);
				} else {
					$("#cameraPop"+id+" img").attr("src",camGraphics["dash-notfound"]);
				}
			}
		});	
		
	});
	
	camMap.addOverlay(marker);
	return true;
	
}

function bindCameraPopAction(id) {
	
	if($.isCookiesOn()) {
  	// is a favourite, so it can be removed
		if (indexOfCamFav(id) > -1) {
		
			$("#cameraPop"+id+" a").unbind("click");
			$("#cameraPop"+id+" a").text(camLang["fav-del"]);
			$("#cameraPop"+id+" a").click(function(){
				removeCameraFav(id);																 
			});		
		
		// not a favourite, can be added
		}	else {
		
			$("#cameraPop"+id+" a").unbind("click");
			$("#cameraPop"+id+" a").text(camLang["fav-add"]);
			$("#cameraPop"+id+" a").click(function(){
				addCameraFav(id);																 
			});	
		}
	}	
	
}


/**
 * Generates and returns the HTML pop up for display in the map
 */
function cameraMapPopupHtml(id) {
	var h = [];
	h.push("<div class='cameraMapPopup camera' id='cameraPop"+id+"'>");
	h.push("<label id='label"+id+"'>"+labelArray[id]+"</label>");
	h.push("<img src='"+camGraphics["dash-loading"]+"'>");
	h.push("<a></a>");	
	h.push("</div>");
	return h.join('');
}



/***************************************************************
GENERIC FUNCTIONS
***************************************************************/

/**
 * Checks if a camera is a favouirte by searching for
 * the existence of the given id in the array
 * @return index of the favourite.
 */
function indexOfCamFav(id) {
	for(var i=0; i<= cameras.length; i++) {
		if(parseInt(cameras[i],10) === parseInt(id,10)) return i;
	}
	return -1;
}

/**
 * Load any favouite camera IDs into the cameras array. If no
 * favourite are saved, load the default cameras.
 * @return true Returns true if there are saved favouites, false otherwise
 */
function loadCameraFavouites() {
	
	var tmpCookie = $.cookie(camCookie);
	
	
	if (tmpCookie) {
		cameras = tmpCookie.split("|");
	}
	cameraLog("loadCameraFavouites()",tmpCookie,cameras);
	
	if(cameras.length > 0) {
		return true;
	} else {
		cameras = defaultCameras.split("|");
		return false;
	}
}

/**
 * A debugging function for the traffic cameras (requires Firebug).
 */ 
function cameraLog() {
	if(camDebug) {
		if(window.console) {
			console.debug.apply( console, arguments );
		} else if($("#cameraDebug").length) {
			var out = [];
			out.push("<p>");
			for(var i=0; i<arguments.length; i++) {
				out.push(arguments[i].toString());
				out.push(", ");
			}
			out.push("</p>");
			$("#cameraDebug").prepend(out.join(''));
		}
	}
}

/**
 * Sets the text of the label for the current camera
 * or a specific camera id.
 */
function cameraLabel(text, id) {
	if(arguments.length== 1) {
		$("#camera label").text(text);	
	} else {
		$("#camera"+id+" label").text(text);	
	}
}

/**
 * Sets the image for the current camera or
 * for a specific camera id.
 */
function cameraImage(url, id) {
	if(arguments.length == 1) {
		$("#camera img").attr("src",url);	
	} else {
		$("#camera"+id+" img").attr("src",url);	
	}
}


/***************************************************************
UTILS
***************************************************************/

// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) {
  var rest = this.slice((to || from) + 1 || this.length);
  this.length = from < 0 ? this.length + from : from;
  return this.push.apply(this, rest);
};
