/* Sky Ride Led Rides (details) - JS Functionality */
var map = null;
var geocoder = null;
var directions = null;

var route_points = [];
var route_markers = [];
var polylines = [];
var gdir_listener = null;

var elevation_vertexes = []; 	// GLatLng points

var elevation_points = [];		// textual lat / lng pairs, like route_points above (is it the same?)
var elevation_polylines = [];	// this is where we stick the lines we're drawing
var elevation_ints = [];		// array of integers with the elevation data we get from the callback
var elevation_max = 0;
var elevation_min = 10000;

var elevationGraph = null;

var show_elevations_after_fetch = false;

var gdir_listeners = [];
var gdir_listener_count = 0;
var gdirections_stack = [];

var distances = [];

var markerInfoWindowListeners = [];

var linesPoints = [];

var routeType = 'return';


$(document).ready(function()
{
//	map = new GMap2(document.getElementById("map_canvas"), {
  //      draggableCursor: 'crosshair',
    //    draggingCursor: 'crosshair'
    //});

	map = new GMap2(document.getElementById("map_canvas"));

	var start_lat = document.getElementById('start_lat').value;
	var start_long = document.getElementById('start_long').value;
    

	map.setCenter(new GLatLng(start_lat, start_long), 14);
	map.setUIToDefault();
	bounds = new GLatLngBounds();
	geocoder = new GClientGeocoder();
	// we are now using multiple directions lines so no longer using one global GDirections
	//directions = new GDirections(undefined, document.getElementById("map_directions_text"));


	// this and toggleRouteType and the code at the end of showRoute() need to be refactored  
	var rt = $("input#route_type").val();
	if(rt == 'Circular') {
		routeType = 'circular';
	} else {
		routeType = 'return';
	}


	// add points and route to map
	addRide( eval('('+$("input#eventjson").val()+')') , eval('('+$("input#eventroute").val()+')') );
	
	var zoomLev = map.getBoundsZoomLevel(bounds);
	map.setZoom( zoomLev-1 );
	map.setCenter(bounds.getCenter());
	map.checkResize();
	
	// new, load elevations that came from the DB if we've got em
	addElevations( eval('('+$("input#elevation").val()+')') );
//	hideRoutePoints();
	GEvent.addListener(map, "load", afterLoad());
});


function afterLoad()
{
	hideRoutePoints();
	showRoute();
}

function hideRoutePoints()
{
	// we always want to see the first and last markers
	for(var i = 1; i < route_markers.length - 1; i ++) {
		// and we want our info points to be visible also
		if(route_markers[i].bubbleHtml == '') {
			route_markers[i].hide();
		}
	}
}



function addRide(event, route)
{
	// add carpark location marker; if we have
	if (event.carpark_lat != '' && event.carpark_long != '')
	{
		var carparkMarker = {
			'point': new GLatLng(event.carpark_lat, event.carpark_long),
			'markerImg': '/zuvvi/ext/component/bc/com_bc_skyride_ledrides_details/img/markerparking.png',
			'markerSize': new GSize(30, 34),
			'bubbleHtml': ''
		}
		addMapMarker(carparkMarker, G_DEFAULT_ICON, false);
	}

	// add train station marker
	if (event.nearest_trainstation_lat != '' && event.nearest_trainstation_long != '')
	{
		var trainMarker = {
			'point': new GLatLng(event.nearest_trainstation_lat, event.nearest_trainstation_long),
			'markerImg': '/zuvvi/ext/component/bc/com_bc_skyride_ledrides_details/img/markertube.png',
			'markerSize': new GSize(30, 34),
			'bubbleHtml': ''
		}
		addMapMarker(trainMarker, G_DEFAULT_ICON, false);
	}

	// add event location marker - now being done with route generation below
	var bubbleHtml = '';
	bubbleHtml = '<div class="mapbubble"><h5>'+event.route_title+'</h5>';
	bubbleHtml = bubbleHtml + '<p class="date">'+event.event_date_formatted+'</p>';
	bubbleHtml = bubbleHtml + '<p>Meeting point: '+event.meeting_point_title+'</p>';
	bubbleHtml = bubbleHtml + '<p class="url"><a href="/skyride/local/register?event='+event.id+'">Register now</a></p>';
	bubbleHtml = bubbleHtml + '</div>';

	// if we don't have a route, create a marker for the meeting point
	// otherwise it will be created in the route
	if(route.markers.length == 0) {
		var eventMarker = {
			'point': new GLatLng(event.meeting_point_lat, event.meeting_point_long),
			'markerImg': '/zuvvi/ext/component/bc/com_bc_skyride_ledrides_details/img/markerblue.png',
			'bubbleHtml': bubbleHtml,
			'lineType': 's'
		} // s = straight line

		addMapMarker(eventMarker, G_START_ICON, true);
	}
	
	// add route markers, if there is a route defined
	for (var i=0; i < route.markers.length; i++) {
		// keep the bubbleHtml info defined above for the first marker point, otherwise, use that which came with the markers
		if(i != 0) {
			bubbleHtml = '';
			if(route.markers[i].info != '') {
				// if we have an info window, give an info marker icon
				bubbleHtml = route.markers[i].info;
				var image = '/zuvvi/ext/component/bc/com_bc_skyride_ledrides_details/img/markerinfo.png';
			} else {
				// otherwise the plain blue marker icon
				var image = '/zuvvi/ext/component/bc/com_bc_skyride_ledrides_details/img/markerblue.png'
			}
		}
		
		// we need a default value for this
		if(route.markers[i].line == '') {
			route.markers[i].line = 'd';
		}

		var routeMarker = {
			'point': new GLatLng(route.markers[i].la, route.markers[i].lo),
			'markerImg': image,
			'bubbleHtml': bubbleHtml,
			'lineType': route.markers[i].line,  // will be 's' for straight line or 'd' for directed line	
			'label': route.markers[i].label
		}
		
		var icon = null;
		if(i == 0) {
			icon = G_START_ICON;
		} else if(i == route.markers.length - 1) {
			icon = G_END_ICON;
		} else {
			icon = G_DEFAULT_ICON;
		}
		
		addMapMarker(routeMarker, icon, true);
	}
	
	/*// no longer opening start point info window immediately by default
	if(route.markers.length == 0) {
		map.openInfoWindowHtml(eventMarker.point, bubbleHtml);
	} else {
		map.openInfoWindowHtml(markers[0].point, markers[0].bubbleHtml);
	}
	*/	
} // end addRide




// TODO pass in optional arg to specify which line to redraw, not all of them
// (OR all of them)
function showRoute() 
{	
	// sometimes not all of our GDirections requests come back.
	// so it could be best to kill all the listeners and start again each to we want to show the route
	gdir_listener_count = 0;
	gdir_listeners.length = 0; 
	
	var lineToRedraw = null; // this means all - yes it's a bit ambiguous
	if(arguments.length == 1) {
		lineToRedraw = arguments[0];
	}
	
	
	if (route_points.length > 1) {

		if(lineToRedraw == null) {
			// remove all direction lines	
			var z = polylines.length;
			for (var i = 0; i < z; i++) { 
				map.removeOverlay(polylines.pop());
			}

		} else {
			// remove just that polyline IF it exists
			if(polylines.length > lineToRedraw) {
				map.removeOverlay(polylines[lineToRedraw]);
				polylines[lineToRedraw] = null;				
			}
		}
		
		var no_gdirections = true;

		// if we're re redrawing ALL lines
		if(lineToRedraw == null) {
			
			for(var i = 0; i < linesPoints.length; i ++) {
						
				var q = linesPoints[i][1];
				if(route_markers[q].lineType == 's') {
					addMultiPointStraightLine(i);
				} else {
					no_gdirections = false;
					addMultiPointDirectionLine(i);
				}
			}
			
		} else {
			var q = linesPoints[lineToRedraw][1];

			if(route_markers[q].lineType == 's') {
				addMultiPointStraightLine(lineToRedraw);
			} else {
				no_gdirections = false;
				addMultiPointDirectionLine(lineToRedraw);
			}
			
		}
		
		if(no_gdirections == true) {
			// as we don't have any gdirections lines, we don't have to wait for them 
			// to come back to run these functions (see decrement_gdirections_listeners() below)
			render_polylines();
			show_distance();
			//calculateElevationPoints();
		}
		

		// now finally, if we've completed a circular route, we want to hide the final marker icon
		var pointA = route_markers[0].getLatLng();
		var pointB = route_markers[route_markers.length - 1].getLatLng();
		if(pointA.lat() == pointB.lat() && pointA.lng() == pointB.lng()) {
			route_markers[route_markers.length - 1].hide();
		} else {
			route_markers[route_markers.length - 1].show();
		}




	} else {
		// kill any polylines if any exist, there's only one point on the map!!
		z = polylines.length;
		if(z > 0) {
			for (var j = 0; j < z; j++) { 
				map.removeOverlay(polylines.pop());
			}
		}
	}
}


function addMultiPointStraightLine(lineIndex)
{
	var linePoints = [];
	
	for(var i = 0; i < linesPoints[lineIndex].length; i ++) {
		var p = linesPoints[lineIndex][i];
		linePoints.push(route_markers[p].getLatLng()); // TODO refactor "p" - once it's right no point with the extra var def
	}
	
		// NB - 1st of 2 places where you define the line path, colour, pixel width and opacity (see method below)
		var polyline = new GPolyline(linePoints, "#FB0F95", 5, .7, {geodesic:true});		
//		var polyline = new GPolyline(linePoints, "#FF0000", 5, .7, {geodesic:true});		

	polylines[lineIndex] = polyline;
	
	var distance = 0;
	for(var i = 0; i <= linePoints.length - 2; i ++) {
		distance += linePoints[i].distanceFrom(linePoints[i + 1]);
	}
	distances[lineIndex] = distance;
}



function addMultiPointDirectionLine(lineIndex)
{
	var sLatLng;
	var localWayPoints = [];
	
	for(var i = 0; i < linesPoints[lineIndex].length; i ++) {
		var p = linesPoints[lineIndex][i];
		sLatLng = route_markers[p].getLatLng().lat() + ',' + route_markers[p].getLatLng().lng();
		localWayPoints.push(sLatLng);
	}

	var temp_vertexes = [];
	
//	var directions = new GDirections(undefined, document.getElementById("map_directions_text"));
	var count = gdirections_stack.length;
	gdirections_stack[count] = new GDirections(undefined);
	
	// might want to re-set this to zero when we go to showRoute() again...
	increment_gdir_listeners();

	gdirections_stack[count].loadFromWaypoints(localWayPoints, {preserveViewport:true, getPolyline:true}); 
	gdir_listeners[lineIndex] = GEvent.addListener(gdirections_stack[count],"load", function(){			
		
       	var poly = gdirections_stack[count].getPolyline(); 
        for (var i = 0; i < poly.getVertexCount(); i++) { 
			temp_vertexes[i] = poly.getVertex(i);   
        } 

		// NB - 2nd of 2 places where you define the line path, colour, pixel width and opacity (see method above)
        //var polyline = new GPolyline(temp_vertexes, "#FF0000", 5, .7); 
      	var polyline = new GPolyline(temp_vertexes, "#FB0F95", 5, .7); 

		polylines[lineIndex] = polyline;
		distances[lineIndex] = gdirections_stack[count].getDistance().meters;
		
        GEvent.removeListener(gdir_listeners[lineIndex]);
		decrement_gdir_listeners(); // once this reaches zero the lines will actually be drawn
	}); // end GEvents listener		
}




function increment_gdir_listeners()
{
	gdir_listener_count ++;
}

function decrement_gdir_listeners()
{
	gdir_listener_count --;
	if(gdir_listener_count == 0) {
		gdir_listeners.length = 0; // empty the listeners array
		
		// all these functions HAVE to wait for all the directions polylines to return - which is why they're here
		render_polylines();
		show_distance();
		//calculateElevationPoints();
	}
}

function render_polylines()
{
	for (var i = 0; i < polylines.length; i++) { 
		map.addOverlay(polylines[i]);
	}
}


function show_distance()
{	
	var meters = 0;
	for(var i = 0; i < distances.length; i ++) {
		meters += distances[i];
	}
	
	if(routeType == 'return') { 
		meters = meters * 2;
	}

//	var distance = "Distance " + (meters / 1609.334).toFixed(2) + " miles";
	var distance = (meters / 1000).toFixed(2) + "km";
	/*
	if(routeType == 'return') { 
		distance += ' (there and back)';
	} else {
		distance += ' (circular route)';
	}*/


	document.getElementById("distance").innerHTML = distance;
	//document.getElementById("distance").innerHTML = "Distance " + (directions.getDistance().meters/1609.334).toFixed(2)+" miles";
	//document.getElementById("distance").innerHTML = "Distance " + (directions.getDistance().meters/1000.0).toFixed(2)+" km";
	
}












function addMapMarker(markerInfo, icon, addToRoute)
{
	// NB for G_DEFAULT_ICON we are really using the default, rather than rewriting the image as in the else branch
	if(icon == G_START_ICON) {
		var markerIcon = new GIcon(G_START_ICON);

	} else if (icon == G_END_ICON) {
		// this isn't very beautiful but it's the most pragmatic place to have it
		if(routeType == 'return') {
			var markerIcon = new GIcon(G_DEFAULT_ICON);
			markerIcon.image = '/zuvvi/ext/component/bc/com_bc_skyride_ledrides_details/img/marker_turnaround.png';
		} else {
			var markerIcon = new GIcon(G_END_ICON);
		}

	} else {
		var markerIcon = new GIcon(G_DEFAULT_ICON);
		markerIcon.image = markerInfo.markerImg;
	}
	
	
	
	if (markerInfo.markerSize) {
		markerIcon.iconSize = markerInfo.markerSize;
	}

	var marker = new GMarker(markerInfo.point, {icon: markerIcon, title: markerInfo.label, draggable: true});


	// NB - gotta put the listener somewhere or we'll get errors closing the windows
	var n = markerInfoWindowListeners.length;
	if(markerInfo.routeIndex) {
		n = markerInfo.routeIndex;
	} 

	var q = route_markers.length;
	if(markerInfo.routeIndex) {
		q = markerInfo.routeIndex;
	} 

	// we only want a bubble listener if there's something to show
	if(markerInfo.bubbleHtml != '') {
		markerInfoWindowListeners[n] = GEvent.addListener(marker, 'click', function() {
			map.panTo(marker.getLatLng());
			marker.openInfoWindowHtml(markerInfo.bubbleHtml); 
		});
	}

	// NEW - add the bubbleHtml as a property to the marker object
	marker.bubbleHtml = markerInfo.bubbleHtml;

	bounds.extend(markerInfo.point);
	map.addOverlay(marker);


	// Now, if it's part of the route, work out which line this point belongs to
	// and store that info, both in the marker, and in the linesPoints multidimenional array
	if(addToRoute == true) {
		// hang on to the lat / long  and marker for later line manipulation
		route_points.push(markerInfo.point.lat() + ',' + markerInfo.point.lng());
	
		// new - add to the marker whether it is a straight or directed line to this point
		marker.lineType = markerInfo.lineType;	
		// new - as we haven't pushed the the marker onto route_markers yet this will actually give the right length
		marker.routeIndex = route_markers.length;
		
		// let's work out the marker's line index and update linesPoints too...
		if(route_markers.length == 0) {
			// special case for the first point
			marker.lineIndex = 0;
			linesPoints[0] = [0];
			  
		} else if(marker.lineType !=  route_markers[route_markers.length - 1].lineType) {
			// special case for the second point
			if(route_markers.length == 1) {
				marker.lineIndex = 0;
				linesPoints[0].push(1);
			
			} else { 
				// if it's a new line type, increase the line index by one
				marker.lineIndex = route_markers[route_markers.length - 1].lineIndex + 1;
				// start a new lines points entry, starting with the PREVIOUS POINT
				linesPoints[marker.lineIndex] = [route_markers.length - 1, route_markers.length];
			}

		} else {
			marker.lineIndex = route_markers[route_markers.length - 1].lineIndex;
			linesPoints[marker.lineIndex].push(route_markers.length);
		}
		route_markers.push(marker);
	
	
	} else { // this is for updating marker icons, where we're not adding to the route
		// this could be zero, thus we need to compare typeof rather than just saying if(markerInfo.lineIndex)
		if(typeof markerInfo.lineIndex != 'undefined') {
			marker.lineIndex = markerInfo.lineIndex;
		}
		if(markerInfo.lineType) {
			marker.lineType = markerInfo.lineType;
		}
		if(typeof markerInfo.routeIndex != 'undefined') {
			marker.routeIndex = markerInfo.routeIndex;
		}
	}	
	return marker;
}



// this is for putting the elevations on the map from the saved JSON
function addElevations(route)
{
	if(route.elevations.length == 0) {
		
		document.getElementById("toggle_elevations").style.display = 'none';
		return;
	}
	
	// add route markers, if there is a route defined
	for (var i=0; i < route.elevations.length; i++) {
		var point = new GLatLng(route.elevations[i].la, route.elevations[i].lo);
		elevation_vertexes.push(point);
		elevation_points.push(route.elevations[i].la + ',' + route.elevations[i].lo);
		elevation_ints.push(parseInt(route.elevations[i].hi));
	}

	setElevationMaxMin();
	drawElevationsLine();
}


function setElevationMaxMin()
{
	for (var i = 0; i < elevation_ints.length; i++){
		if(elevation_max < elevation_ints[i]) {
			elevation_max = elevation_ints[i];
		} 
		if(elevation_min > elevation_ints[i]) {
			elevation_min = elevation_ints[i];
		} 
	};
	
	// for some reason these are turning into strings somewhere, so:
	elevation_max = parseInt(elevation_max);
	elevation_min = parseInt(elevation_min);
}



function toggleElevations()
{
	var b = document.getElementById("toggle_elevations");
	if(b.innerHTML == "Show Elevations") {
		b.innerHTML = "Hide Elevations";
		showElevationsGraph();
		hideRouteLine();
		showElevationsLine();
		
	} else {
		b.innerHTML = "Show Elevations";
		hideElevationsGraph();
		hideElevationsLine();
		showRouteLine();
	}
}

function thinGraphElevationData(fixedURLPartsLength) {
	var intLengthTotal = 0;
	for(var i = 0; i < elevation_ints.length; i ++) {
		intLengthTotal += elevation_ints[i].toString().length;
		intLengthTotal ++; 
	}
	var averageLength = intLengthTotal / elevation_ints.length;
	var maxLength = 2048 - 20 - fixedURLPartsLength;
	var maxPoints = Math.floor(maxLength / averageLength);
	var pointsToLose = elevation_ints.length - maxPoints;
	var oneInEvery = Math.ceil(elevation_ints.length / pointsToLose);
	var thinned_elevations = [];
	
	for(var i = 0; i < elevation_ints.length; i ++) {
		if((i % oneInEvery) == 0) {
			continue;
		}
		thinned_elevations.push(elevation_ints[i]);
	}
	return thinned_elevations.join(',');
}



function showElevationsGraph()
{
	var min = elevation_min - 20;
	if(min < 0) {
		min = 0;
	}
	var max = elevation_max + 20;

	var overlayWidth = 400;
	var overlayHeight = 125;

/*
	var graphURL = 'http://chart.apis.google.com/chart?cht=lc&chs=' + overlayWidth + 'x' + overlayHeight + '&chd=t:';
	graphURL += elevation_ints.join(',');
	graphURL += '&chds=' + min + ',' + max + '&chxr=0,' + min + ',' + max + '&chxt=y&chdl=meters&chdlp=l&chm=B&chco=303090&chm=B,76A4FC,0,0,0&chtt=Route+Elevation';
*/

	var graphURLPrefix = 'http://chart.apis.google.com/chart?cht=lc&chs=' + overlayWidth + 'x' + overlayHeight + '&chd=t:';
	var graphURLSuffix = '&chds=' + min + ',' + max + '&chxr=0,' + min + ',' + max + '&chxt=y&chdl=meters&chdlp=l&chm=B&chco=303090&chm=B,76A4FC,0,0,0&chtt=Route+Elevation';
	// maximum google graph api request length is 2048 chars when URL encoded.  In practice it seems to be a little longer,
	// so if our whole request is 2048 chars or less it will work as expected.
	
	var fixedURLPartsLength = graphURLPrefix.length + graphURLSuffix.length;
	var elevation_str = elevation_ints.join(',');
	
	if(fixedURLPartsLength + elevation_str.length > 2048) {
		// we're going to have to thin out the elevation data somewhat to get a graph back from google. 
		elevation_str = thinGraphElevationData(fixedURLPartsLength);
	}
	graphURL = graphURLPrefix + elevation_str + graphURLSuffix;



	var screenPoint = new GScreenPoint(50,35);
	
	var browser = navigator.userAgent;

	// these browsers think that half way across the x axis of the image is the zero point, so:
	if(browser.match('Firefox/3') || browser.match('MSIE 6')) {
		var overlayPoint = new GScreenPoint((overlayWidth / 2).toFixed(0),0);
	} else {
		var overlayPoint = new GScreenPoint(0,0);		
	}
	// other browsers seem to work OK

	var screenSize = new GScreenSize(overlayWidth, overlayHeight);

	elevationGraph = new GScreenOverlay(graphURL, screenPoint, overlayPoint, screenSize);
	map.addOverlay(elevationGraph);
}



function hideElevationsGraph()
{
	if(elevationGraph != null) {
		map.removeOverlay(elevationGraph);
		elevationGraph = null;
	}
}



function getElevationScaleColor(i)
{
	var rel_height = elevation_ints[i] - elevation_min;
	var zero_to_one = rel_height / (elevation_max - elevation_min);
	// from rainbow.js
	return scaleToRGB(zero_to_one);
}


function drawElevationsLine()
{
	for (var i = 0; i < elevation_vertexes.length; i++) {
    	var elpoly = new GPolyline([elevation_vertexes[i], elevation_vertexes[i + 1]], getElevationScaleColor(i), 5, 1);
		map.addOverlay(elpoly);
		elevation_polylines.push(elpoly);
	}
	hideElevationsLine(); // by default
}

function removeElevationsLine()
{
	var z = elevation_polylines.length;
	for (var i = 0; i < z; i++) {
		map.removeOverlay(elevation_polylines.pop());
	}
	elevation_polylines.length = 0;
}

function showElevationsLine()
{
	for (var i = 0; i < elevation_polylines.length; i++) {
		if(elevation_polylines[i].isHidden()) {
			elevation_polylines[i].show();
		}
	}
}


function hideElevationsLine()
{
	for (var i = 0; i < elevation_polylines.length; i++) {
    	elevation_polylines[i].hide();
	}
}


function showRouteLine()
{
	for (var i = 0; i < polylines.length; i++) {
		if(polylines[i].isHidden()) {
			polylines[i].show();
		}
	}
}


function hideRouteLine()
{
	for (var i = 0; i < polylines.length; i++) {
    	polylines[i].hide();
	}
}





