/* 

THIS IS THE MASTER FILE FOR ALL SERVICE MAPS

Currently used by

	buses.co.uk/travel/service.aspx
	kinchbus.co.uk/servicemap.aspx
*/

var map;
var debug = false;
var tooltip;
var selectedRouteIndex = 0;
var mgr=null;	// the active marker manager

function serviceMap()
{
	// this object should contain all member elements and all methods
	this.serviceId=0;
	this.routeCollection = new Object();
	this.mapHtmlDivId;
	this.routeRadioDivId;
	this.stopSelectId;
	this.realtimeDivId=null;
	this.realtimeUrl=null;
	this.realtimeXslFile="xsl/realtime_long.xsl";
	this.defaultLat;
	this.defaultLng;
	this.defaultZoom;
	this.routes = new Array();
	this.lastKnownSelectedStopId=-1;
	this.realtimeTimerId=null;	
	this.loadingimage=null;
	this.recenterMap=false;
	this.realTimeShowAllServices=false;
	this.bubbleContents="services";
	this.bubbleHeight=null;
	this.bubbleWidth=null;
	this.stopZoomLevel=15;
}


// Handles the intiial load of the map.  This MUST be called before any other methods.
serviceMap.prototype.load=function()
{
	// set up the google map
	if (GBrowserIsCompatible()) 
	{ 
		map = new GMap2($(this.mapHtmlDivId));
		// Put the loading message underneath the tiles
		
		
		$(this.mapHtmlDivId).style.backgroundImage = "url(" + publicWebServiceUrl.substring(0, publicWebServiceUrl.lastIndexOf("/")) + "/clientimages/loading.gif)";
		
		map.addControl(new GLargeMapControl());
		//map.addControl(new GMapTypeControl());
		map.enableScrollWheelZoom();
		
		// set the center of the map to the default values set
		map.setCenter(new GLatLng(this.defaultLat,this.defaultLng), this.defaultZoom);
		GEvent.bind(map, "click", this, this.mapClickHandler);
		
		
	}
	else 
	{
		alert("Sorry, the Google Maps API is not compatible with this browser");
	}
	if (this.serviceId>0)
	{
		this.getServiceInfo();
	}
}


// Handles all clicks on the map.  Code in here all possible things that should happen when map elements are clicked.
serviceMap.prototype.mapClickHandler=function(overlay, point)
{
	if (overlay) 	// clicked on an overlay
	{
		// call maker click event handler
		this.marker_click_handler(overlay);
	}
	else if (point) 
	{
		//whatever you want to happen if you don't click on an overlay.
	}
	else
	{
		// anything else

	}
}

serviceMap.prototype.marker_click_handler=function(overlay)
{
	if (overlay.stopId!=null)
	{
		//this.showStopCustomToolTip(overlay, "<b>Some value</b>");
		overlay.openInfoWindowHtml(this.getStopBubbleHtml(overlay.stopId));
		this.showRealtimeData(overlay.stopId);
	}
}

// Takes a stop id and returns the HTML that will appear in the 'Bubble' on the map.
serviceMap.prototype.getStopBubbleHtml=function(stopid)
{
	try
	{
		var rtidx = this.getSelectedRouteIndex();
		var stop = this.routes[rtidx].stops[stopid];	// WRONG SHOULD BE SELECTED ROUTE NOT 0
		var toRet="";
		

		
		// call the web service to update the servicesInfoWindow div even tho it doesn't exist yet.
		
		switch (this.bubbleContents)
		{
		case "realtime":
		  	toRet += "<strong>" + stop.stopName + "</strong><div id='bubbleRealtimeDiv'>";
		  	
			if (this.loadingimage!=null)
			{
				toRet +=  "<img src=\"" + this.loadingimage + "\" />";
			}
			else
			{
				toRet +=  "<strong>loading ...</strong>";
			}
		  	
		  	toRet += "</div>";
		  break;
		case "services":
			stop_getServicesByStop(stop.stopId, this.getServicesByStop_success.bind(this), this.ajaxError.bind(this));
			// toRet += "<strong>" + stop.stopName + "</strong>" + " (" + stop.stopId + ")<p>Other services that stop here: <div id=\"servicesInfoWindow\"></div></p>";
			toRet += "<strong>" + stop.stopName + "</strong>" + "<br/>(text <strong>" + stop.naptanCode + "</strong> to 84268 for <a href=\"/travel/text.aspx\" title=\"Details of our text message service\">times of next bus</a>)<p>Other services that stop here: <div id=\"servicesInfoWindow\"></div></p>";
		  break;
		default:
		  	toRet += "<strong>" + stop.stopName + "</strong>";
		}
		
		if (this.bubbleHeight!=null)
		{
			toRet = '<div style="height:' + this.bubbleHeight + 'px;width:' + this.bubbleWidth + 'px;">' + toRet + '</div>';
		}		

		return toRet;
		
	}
	catch(e) {
		return "";
	}
	
}

serviceMap.prototype.getServicesByStop_success=function(resp)
{
	var val = cleanResponse(resp.responseText);
	eval(val);
	var htmlString = "";
	for (var i=0; i<response.services.length; i++)
	{
		if (response.services[i].serviceId!=this.serviceId)
		{
			htmlString += "<a href=\"service.aspx?serviceid=" + response.services[i].serviceId + "&lng=" + map.getCenter().lng() + "&lat=" + map.getCenter().lat() + "&zm=" + map.getZoom() + "&st="+this.lastKnownSelectedStopId+"\" title=\"" + response.services[i].serviceName + " - " + response.services[i].serviceDescription + "\n\rClick to view this services route\">" + response.services[i].serviceName + "</a> | ";
			
			//htmlString += "<a href=\"javascript:loadService(" + response.services[i].serviceId + ");\" title=\"" + response.services[i].serviceName + " - " + response.services[i].serviceDescription + "\n\rClick to view this services route\">" + response.services[i].serviceName + "</a> | ";
		}
	}

	// update the div 'servicesInfoWindow'
	if (!$('servicesInfoWindow'))
	{
		// wait 3 seconds and try again
		setTimeout(function(){try { $('servicesInfoWindow').innerHTML = htmlString;} catch(e){}}, 300);
	}
	else
	{
		$('servicesInfoWindow').innerHTML = htmlString;
	}
}


serviceMap.prototype.getServiceInfo=function()
{
	// get service info from database
	servicemap_getServiceInfo(this.serviceId, this.getServiceInfo_success.bind(this), this.ajaxError.bind(this));
}

serviceMap.prototype.getServiceInfo_success=function(resp)
{
	var val = cleanResponse(resp.responseText);
	eval(val);
	this.routes = response.service.routes;
	this.createRouteRadios();
	this.showRoute(this.routes[0].routeId);
	
	// any other set up
	
	$(this.stopSelectId).onchange=this.selectStop.bind(this);
}



serviceMap.prototype.showRoute=function(routeid)
{
	this.hideAllRoutes();
	this.showRoutePolyline(routeid);
	this.showRouteStops(routeid);
}



serviceMap.prototype.hideAllRoutes=function()
{
	for (var key in this.routeCollection) {  this.hideRoutePolylineFromWS(key);}
}


serviceMap.prototype.showRoutePolyline=function(routeid)
{
	if (this.routeCollection[routeid]==null)
	{
		this.getRoutePolyline(routeid);
	}
	else
	{
		if(this.routeCollection[routeid].isHidden())
		{
			this.routeCollection[routeid].show();
		}
	}
}



serviceMap.prototype.hideRoutePolylineFromWS=function(routeid)
{

	// note either method can be used - need to see what uses most/least memory
	// map.removeOverlay(routeCollection[routeid]);
	
	this.routeCollection[routeid].hide();
}


serviceMap.prototype.getRoutePolyline=function(routeid)
{
	// call the web service proxy

	
	locationmap_getRoutePolyline(routeid, 10, this.getPolyline_success.bind(this), this.ajaxError.bind(this))
	
}

serviceMap.prototype.getPolyline_success=function(resp)
{
	var val = cleanResponse(resp.responseText);
	eval(val);
	response.polyline.weight=7;
	this.routeCollection[response.routeid] = response.polyline;
	if (response.routeName)	this.routeCollection[response.routeid].routeName = response.routeName;
	if (this.recenterMap)
	{
		map.setCenter(new GLatLng(response.centerLat, response.centerLng), response.initialZoom);
		this.recenterMap = false;	// no more re-centering when loading other routes for this service
	}
	map.addOverlay(this.routeCollection[response.routeid]);
}


serviceMap.prototype.createStopMarker=function(stop)
{
	// takes a stop and returns a GMarker
	
	var marker = new GMarker(new GLatLng(parseFloat(stop.Lat), parseFloat(stop.Lng)),{draggable:false, icon:G_DEFAULT_ICON});
        marker.stopId = stop.stopId;
	//GEvent.bind(marker,"mouseover", this, this.showStopCustomToolTip );
	var _htmlstring = this.getTooltipHtml(stop);
	GEvent.addListener(marker,"mouseover", function() { showCustomTooltip(marker, _htmlstring); });      
	GEvent.addListener(marker,"mouseout", function() { tooltip.style.visibility="hidden" });         
	return marker;
	
}


serviceMap.prototype.hideAllStops=function()
{

}

serviceMap.prototype.showRouteStops=function(routeid)
{
	var ridx =  -1;
	for (var i=0; i<this.routes.length;i++)
	{
		if (this.routes[i].routeId==routeid)
			ridx = i;
	}
	
	if (ridx==-1 || this.routes[ridx]==null)
	{
		this.getRouteStops(routeid);
	}
	else
	{
		// if we dont already have this route's stops loaded, call the web service proxy
		if (!this.routes[ridx].stops)
		{
			this.getRouteStops(routeid);
		}
		else	// otherwise just update our dropdown and show them
		{
			

				this.populateStopsDropdown(ridx);

				// need to show them as well
				this.plotStopsOnMap(ridx);

		}
	}
}



serviceMap.prototype.getRouteStops=function(routeid)
{
	locationmap_getRouteStops(routeid, this.getRouteStops_success.bind(this), this.ajaxError.bind(this))
}

var orderedStops;
serviceMap.prototype.getRouteStops_success=function(resp)
{
	var val = cleanResponse2(resp.responseText);
	if (debug)
	$('debugConsole').innerHTML = (val);
	eval(val);
	
	var rid = response.routeid;
	var ridx =  -1;
	for (var i=0; i<this.routes.length;i++)
	{
		if (this.routes[i].routeId==rid)
			ridx = i;
	}
	
	if (ridx==-1)	// if we don't have a matching route in our service (because service id not set)
	{
		this.routes[ridx] = new Object();
	}
	
	this.routes[ridx].stops = new Object();
	
	// create the markers
	
	markers = new Array();
	orderedStops = new Array();
	var a=0;
	
    	for (i=0; i<response.stops.length; i++) {
    		response.stops[i].stopName = decodeURI(response.stops[i].stopName);		// un encode URL encoded values
    		var stopMarker = this.createStopMarker(response.stops[i]);
    		markers[a] = stopMarker;
    		this.routes[ridx].stops[response.stops[i].stopId] = response.stops[i];
    		orderedStops[a] = this.routes[ridx].stops[response.stops[i].stopId];
    		a++;
    	}
    	
    	// save the markers in the route object
    	this.routes[ridx].stopMarkers = markers;

	this.plotStopsOnMap(ridx);
    	
	// update the stop drop down
	this.populateStopsDropdown(ridx);
	
}


serviceMap.prototype.ajaxError=function(error)
{
        var val = error.responseText;
        //alert("An AJAX error occured");
        //alert(val);
}


serviceMap.prototype.createRouteRadios=function()
{
	var str = "";
	for (var i=0; i<this.routes.length; i++)
	{
		var radioName = "directionOfTravel";
		var radioValue = this.routes[i].routeId;
		var radioText = this.routes[i].routeName;
		if (i==0)
		{
			// WRONG THESE LINES CONTAIN A HARD CODED REFERENCE TO 's'
			str += "<input type=\"radio\" checked=\"true\" name=\"" + radioName + " \" value=\"" + radioValue + "\" id=\"directionOfTravel_" + i + "\" onclick=\"s.showRoute(" + this.routes[i].routeId + "); selectedRouteIndex = " + i + ";\" />" + radioText + "<br/>";
		}
		else
		{
			str += "<input type=\"radio\" name=\"" + radioName + " \" value=\"" + radioValue + "\" id=\"directionOfTravel_" + i + "\" onclick=\"s.showRoute(" + this.routes[i].routeId + "); selectedRouteIndex = " + i + ";\" />" + radioText + "<br/>";
		}
	}

	$(this.routeRadioDivId).innerHTML = str;
}


serviceMap.prototype.getSelectedRouteIndex=function()
{
	return selectedRouteIndex;	// could iterate through radios to see which is selected, or use teh global var which isn't really the best
}


serviceMap.prototype.plotStopsOnMap=function(ridx)
{
	// add the markers to the map
	if (mgr == null)
	
	mgr = new MarkerManager(map);


	mgr.clearMarkers();
	mgr.addMarkers(this.routes[ridx].stopMarkers, this.stopZoomLevel);
	mgr.refresh();
	
	// if the last know stop is known then open it (but don't pan to it??)
	/*
	if (this.lastKnownSelectedStopId>-1)
	{
		route_getRealtimeData(_stopid, this.serviceId, this.routes[selectedRouteIndex].routeId, dataurl, "xsl/realtime.xsl", this.stop_getRealtimeData_Complete.bind(this) ,this.ajaxError.bind(this));
	}
	*/
}	

serviceMap.prototype.populateStopsDropdown=function(routesIndex)
{
	if (this.stopSelectId!=null)
	{
		// need to populate the stops dropdown list
		clearSelectOptions(this.stopSelectId);

		addOptionToEndOfSelect(this.stopSelectId, "Please select a bus stop", "-1");

		var cnt=0;

		// this needs to be in an explicit order
		for (var a=0; a< orderedStops.length; a++)
		{
			addOptionToEndOfSelect(this.stopSelectId, orderedStops[a].stopName, orderedStops[a].stopId);
			cnt++;
		}
		
		/* this behaviour didnt work in Chrome or Opera as they dont preserve order */
		/*
		for (stop in this.routes[routesIndex].stops)
		{
			addOptionToEndOfSelect(this.stopSelectId, this.routes[routesIndex].stops[stop].stopName, this.routes[routesIndex].stops[stop].stopId);
			cnt++;
		}
		*/
		
		// if there are no stops then hide the drop down
		if (cnt==0)
		{
			$(this.stopSelectId).hide();
			if ($('stopsContainer')!=null) $('stopsContainer').hide();
			if ($('stopsMissing')!=null) $('stopsMissing').show();
		}
		else
		{
			$(this.stopSelectId).show();
			if ($('stopsContainer')!=null) $('stopsContainer').show();
			if ($('stopsMissing')!=null) $('stopsMissing').hide();
		}
		
		// now select the appropriate stop if any in the querystring
		var qstring = document.location.search;
		var restOfqstring = qstring.substring(qstring.indexOf("stopid=")+7);
		var qstopid = "";
		if (restOfqstring.indexOf("&")>0)
		{
			qstopid = restOfqstring.substring(0, restOfqstring.indexOf("&"));
		}
		else
		{
			qstopid = restOfqstring
		}
		$(this.stopSelectId).value = qstopid;
		this.selectStop();
		
	}

}


serviceMap.prototype.selectStop=function()
{
	var ddl = $(this.stopSelectId);
	// pan and zoom to show stop
	if (ddl.selectedIndex!=-1)
	{
		var rtidx = this.getSelectedRouteIndex();
		var selectedStopIndex = ddl.selectedIndex-1;	// this only works if the stop index is the same as the index in the 'this.routes[0].stopMarkers' array.  It should be as both are populated at the same time
		var mkr = this.routes[rtidx].stopMarkers[selectedStopIndex];  // WRONG, should be selected route NOT 0
		var stp = this.routes[rtidx].stops[mkr.stopId];		  // WRONG, should be selected route NOT 0
	
		map.setCenter(mkr.getPoint(),15); 	
		this.showRealtimeData(mkr.stopId);	
		
		// cant open info window as marker in markermanager may not be visible. Instead show tooltip
		//this.routes[0].stopMarkers[ddl.selectedIndex].openInfoWindowHtml(this.getStopBubbleHtml(this.routes[0].stopMarkers[ddl.selectedIndex].stopId));
		
		if (tooltip)
		{
			tooltip.style.visibility="hidden";
		}
		showCustomTooltip(mkr, this.getTooltipHtml(stp));
		
		if ($$('.busexpress-clientwidgets-favourites').length>0)
		{
		
		 var linksUrl = 'http://www.buses.co.uk/travel/service.aspx?serviceid='+this.serviceId + '&stopid=' + stp.stopId;
		
		 var newRecentlyViewedTitle = stp.stopName;
		 var newRecentlyViewedUrl = linksUrl;
		 addToRecentlyViewed(newRecentlyViewedTitle, newRecentlyViewedUrl);
		} 
		
	}
}


serviceMap.prototype.showRealtimeData=function(_stopid)
{
	this.stopRealtimeUpdateTimer();
	// only do this if the calling page has associated a DIV with it and set a realtime URL endpoint
	if (this.realtimeDivId!=null && this.realtimeUrl!=null)
	{
		// show our 'loading' message
		// show our 'loading' message
		if (this.loadingimage!=null)
		{
			$(this.realtimeDivId).innerHTML = "<img src=\"" + this.loadingimage + "\" />";
		}
		else
		{
			$(this.realtimeDivId).innerHTML = "<strong>loading ...</strong>";
		}		

		var dataurl = this.realtimeUrl;
		this.lastKnownSelectedStopId = _stopid;
		if (!this.realTimeShowAllServices)
		{
			//alert(this.routes[selectedRouteIndex].routeId);
			route_getRealtimeData(_stopid, this.serviceId, this.routes[selectedRouteIndex].routeId, dataurl, this.realtimeXslFile, this.stop_getRealtimeData_Complete.bind(this) ,this.ajaxError.bind(this));
		}
		else
		{
			route_getRealtimeData(_stopid, -1, -1, dataurl, this.realtimeXslFile, this.stop_getRealtimeData_Complete.bind(this) ,this.ajaxError.bind(this));
		}
		if ($('allServicesToggle'))
		$('allServicesToggle').style.display='inline';
	}
}

serviceMap.prototype.updateRealtimeData=function()
{
	var _stopid = this.lastKnownSelectedStopId

	// same as method above but doesn't show the loading message
	// instead should show an icon possibly?
	if (this.realtimeDivId!=null && this.realtimeUrl!=null)
	{
		var dataurl = this.realtimeUrl;
		//stop_getRealtimeData(_stopid, this.serviceId, dataurl, "xsl/realtime.xsl", this.stop_getRealtimeData_Complete.bind(this) ,this.ajaxError.bind(this));
		if (!this.realTimeShowAllServices)
		{
			route_getRealtimeData(_stopid, this.serviceId, this.routes[selectedRouteIndex].routeId, dataurl, this.realtimeXslFile, this.stop_getRealtimeData_Complete.bind(this) ,this.ajaxError.bind(this));
		}
		else
		{
			route_getRealtimeData(_stopid, -1, -1, dataurl, this.realtimeXslFile, this.stop_getRealtimeData_Complete.bind(this) ,this.ajaxError.bind(this));
		}
	}
}


serviceMap.prototype.stop_getRealtimeData_Complete=function(resp)
{
	var val = resp.responseText.evalJSON(); 
	$(this.realtimeDivId).innerHTML = val;
	if ($('bubbleRealtimeDiv'))
	{
		$('bubbleRealtimeDiv').innerHTML = val;
	}
	this.startRealtimeUpdateTimer();
}


serviceMap.prototype.startRealtimeUpdateTimer=function()
{
	this.realtimeTimerId = setTimeout(this.updateRealtimeData.bind(this), 30000 );
}


serviceMap.prototype.stopRealtimeUpdateTimer=function()
{
	if (this.realtimeTimerId!=null)
	{
		clearTimeout(this.realtimeTimerId);
	}
	this.realtimeTimerId=null;
}


serviceMap.prototype.setRealTimeShowAllServices=function(boolVal)
{
	this.realTimeShowAllServices=boolVal;
	this.updateRealtimeData();
}



serviceMap.prototype.enlargeMap=function()
{
	$(this.mapHtmlDivId).style.position='absolute';
	$(this.mapHtmlDivId).style.top='100px';
	$(this.mapHtmlDivId).style.left='100px';
	$(this.mapHtmlDivId).style.width='1000px';
	$(this.mapHtmlDivId).style.height='600px';
}


serviceMap.prototype.getTooltipHtml=function(stop)
{
	return "<strong>" + stop.stopName + "</strong><br />Click to view service times and text message code";
}



/*** HACK * v2.76 only  
GMarkerManager.prototype.clearMarkers = function() {

    var me = this;

    // clear currently shown

    if (me.Wd > 0) {
        me.Id(me.K, me.Kd);
    }

    // reset arrays
    var maxZoom = me.df;
    var maxWidth = 256;
    for (var zoom = 0; zoom < maxZoom; ++zoom){
        me.ed[zoom] = [];
        me.oc[zoom] = 0;
        me.gc[zoom] = Math.ceil(maxWidth/me.Ec);
        maxWidth <<= 1;
     }
    me.refresh();
    
} */


