/**
 * Javascript file for the FreeMaps directions pages.
 */

var ajaxCallInProgress = false; //If we currently are waiting on a response from XLS...we don't want to create a new one.
//storing start and end of the route.
var startLat;
var startLon;
var endLat;
var endLon;
var zoomToExtent = false;


/**
 * Function to initialise variables and set up the OpenLayer map
 * @param none
 * @returns true if directions request has been made.
 */
function init_directions(){
	
    try {
        // Fetch URL parameters
        startLat  = getUrlParameter("startLat");
        startLon  = getUrlParameter("startLon");
        endLat    = getUrlParameter("endLat");
        endLon    = getUrlParameter("endLon");
        
	    // Get Directions
	    getDirections();
	 
    } catch(e) {
        // If there is an error go here
        setDefaultMap();
    }
}

/**
 * Function called after "from here" was selected
 * in the right-click-directions-bubble.
 * 
 * @param lon
 * @param lat
 * @return
 */
function setStartCoord(lon,lat)
{
	rightClickPopUp.hide();
	rightClickPopUp.destroy();
	startLon = lon;
	startLat = lat;
	getDirections();
}

/**
* Function called after "to here" was selected
* in the right-click-directions-bubble.
* 
* @param lon
* @param lat
* @return
*/
function setEndCoord(lon,lat)
{
	rightClickPopUp.hide();
	rightClickPopUp.destroy();
	endLon = lon;
	endLat = lat;
	getDirections();
}



/**
 * Function to set up the Route Server request and sent it to the Proxy Servlet
 *
 * @param {Object} startLat - Starting address latitude value
 * @param {Object} startLon - Starting address longitude value
 * @param {Object} endLat - Destination address latitude value
 * @param {Object} endLon - Destination address longitude value
 */
function getDirections(){
	markers = createDirectionsMarkersLayer();
	if(startLat && startLon)
    {
    	drawStartMarker();
	}
	if(endLat && endLon)
    {
    	drawEndMarker();
	}
	if(!(startLat && startLon && endLat && endLon))
    {
    	return; //dont have all the coordinates yet
	}
	if(ajaxCallInProgress)
	{
		alert("Freemaps is currently finding your directions. Please wait before requesting more directions");
		return;
	}
	
	directionAjaxCall();
	
}
	
/**
 * Function requesting directions.
 * 
 * @return
 */	
function directionAjaxCall() {
    
	ajaxCallInProgress=true;
	
    var gml = "<?xml version='1.0' encoding='UTF-8'?>"+
            "<XLS version='1.1' xmlns='http://www.opengis.net/xls' xmlns:gml='http://www.opengis.net/gml' "+
                "xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>"+
                    "<RequestHeader clientName='OpenGIS-XLS-Demo' clientPassword='OpenGIS-XLS-Demo'/>"+
                    "<Request methodName='RouteRequest' version='1.1' requestID='sample'>"+
                        "<DetermineRouteRequest distanceUnit='KM'>"+
                            "<RoutePlan>"+
                                "<RoutePreference>Fastest</RoutePreference>"+
                                "<WayPointList>"+
                                    "<StartPoint>"+
                                        "<Position>"+
                                            "<gml:Point>"+
                                                "<gml:pos>"+ startLat + " " + startLon +"</gml:pos>"+
                                            "</gml:Point>"+
                                        "</Position>"+
                                    "</StartPoint>"+
                                    "<EndPoint>"+
                                        "<Position>"+
                                            "<gml:Point>"+
                                                "<gml:pos>"+ endLat + " " + endLon +"</gml:pos>"+
                                            "</gml:Point>"+
                                        "</Position>"+
                                    "</EndPoint>"+
                                "</WayPointList>"+
                            "</RoutePlan>"+
                            "<RouteInstructionsRequest format='text/plain' provideGeometry='true'/>"+
                        "</DetermineRouteRequest>"+
                    "</Request>"+
                "</XLS>";
	
	$("#loading").css({display:"block"});
	
    /* 
     * OpenLayers way, but doesn't work until we can set 
     * the content-type to 'application/x-www-form-urlencoded'
     */
    data = "xls=" + gml;
    request = new OpenLayers.Ajax.Request("proxyServlet", 
                     {   method: 'post',
                         contentType : 'application/x-www-form-urlencoded',
                         postBody: data,
                         onComplete: load, 
                         onFailure: failure
                      }
                     );

    
 
}

/* 
 * Function to let a user know the Ajax request failed
 */
function failure() {
  $("#loading").css({display:"none"});
  alert('Our apologies. Our directions service is currently unavailable.' + '\n' + ' Please try again in a few minutes.');
  window.location="index.jsp";
}

/* 
 * Function to get a cross-browser version of getElementsByTagName using namespaces
 *
 * @param node
 * @param name
 * @param ns
 */
function customGetElementsByTagName (node, name, ns) {

    var elems;
    if (OpenLayers.Util.getBrowserName() == "msie") {
        elems =  node.getElementsByTagName(ns + ":" + name);
    } else {
        // Firefox3 is strict about full namespace name and not the abbrevation!
        // We assume here that all namespaces start with "http://www.opengis.net/
        elems = node.getElementsByTagNameNS("http://www.opengis.net/" + ns, name);
    }
    return elems;
}

/**
 * Function to complete the map and format the Route Server response
 *
 * @param {string} doc - Route directions
 */
function load(response) {

    var doc = response.responseXML || response.responseText;

    //if doc is not a DOM element, turn it into one
    if(!doc.documentElement) {
        var format = new OpenLayers.Format.XML();
        doc = format.read(doc);
    } 
    try {

        gf =  new OpenLayers.Format.GML(); 
    
        var vectorLayer = createDirectionsVectorLayer();
    
       /*****************************************
        *  Route Summary
        *****************************************/
        var routeSummary = customGetElementsByTagName(doc, 'RouteSummary', 'xls')[0];
        var nodes = customGetElementsByTagName(routeSummary, 'pos', 'gml');

        var bBox = new Array();

        // Get the Bounding Box
        for(var i=0; i<2; ++i) {
            coordString = nodes[i].firstChild.nodeValue;
            coordString = coordString.replace(gf.regExes.trimSpace, "");
            coords = coordString.split(gf.regExes.splitSpace);
            bBox.push(coords[1]);
            bBox.push(coords[0]);
        }


       /*****************************************
        *  Total Travel Time
        *****************************************/
        nodes = customGetElementsByTagName(doc, 'TotalTime', 'xls');
        var totalTime = "";
        if (nodes.length >= 1) {
            // Split on the decimal and get the first element
            var nodeStr = (nodes[0].firstChild.nodeValue).split('.')[0];
            if (nodeStr != null && nodeStr != '') {
                // Remove the 'P' from the start of the string
                nodeStr = nodeStr.replace('P','');
                // Remove the 'T' from the start of the string
                nodeStr = nodeStr.replace('T','');
                // Replace 'D' with days
                nodeStr = nodeStr.replace('D', ' days ');
                // Replace 'H' with hours
                nodeStr = nodeStr.replace('H', ' hours ');
                // Replace 'M' with minutes
                nodeStr = nodeStr.replace('M',' minutes ');
                nodeStr = nodeStr + ' seconds';
            }
              totalTime = "<br /><strong>Total Travel Time: " + nodeStr + "</strong>";
          }
   
        // Get the total distance travelled
        var totalDistance = "";
        nodes = customGetElementsByTagName(doc, 'TotalDistance', 'xls');
        var units;
        var distance;
        if (nodes.length >= 1) {
            node = nodes[0];
            distance = node.getAttribute("value");

            // Round the distance to 2 decimal places
            distance = Math.round(distance*100)/100;
            units = node.getAttribute("uom");
            totalDistance = "<br/><strong>Total Travel Distance: " + distance + units + "</strong>";
        }

       /*****************************************
        *  Route Instructions
        *****************************************/
        routeInstructionList = customGetElementsByTagName(doc, 'RouteInstructionsList', 'xls')[0];
        nodes = customGetElementsByTagName(routeInstructionList, 'Instruction', 'xls');
        var instructionList = [];
        for(var j=0; j<nodes.length; ++j) {
            if (nodes[j].firstChild) {
                instructionList.push(nodes[j].firstChild.nodeValue);
            }
        }

        // show textual instructions on page
        instructions = instructionList.join("</li>\n<li>");
        instructions = "<li>" + instructions + "</li>" + totalTime + totalDistance;
        
        $("#slideshow").animate({opacity:0},
				{
					complete:function(){
						$("#slideshow").css({height:"0px"});
						$("#nav_instructions").html(instructions).animate({opacity:1},500);
					},duration:500
				});


       /*****************************************
        *  Zoom to extent of route
        *****************************************/
        var bounds = new OpenLayers.Bounds.fromArray(bBox).transform(proj_4326, map.getProjectionObject());
        if(zoomToExtent) {
            map.zoomToExtent(bounds);
        }


       /*****************************************
        *  Route Geometry (map trail)
        *****************************************/
        var routeGeometry = customGetElementsByTagName(doc, 'RouteGeometry', 'xls')[0];
        nodes = customGetElementsByTagName(routeGeometry, 'pos', 'gml'); 


       /*****************************************
        *  Long Route Warning
        *****************************************/
        // If distance is too big and browser is IE, drawing map trail can take a long
        // time. We warn the user for this here.
        var drawMap = true;
        if ((OpenLayers.Util.getBrowserName() == "msie") && (distance > 1000) && (units == "KM")) {
            if (!confirm('It may take more than a minute to draw the map trail for the directions you have requested.' + '\n' +
				'Click "OK" to complete the map trail or "Cancel" to just see the directions')) {
                drawMap = false;
            }
        }
        
        /*****************************************
        *  Route Geometry (map trail)
        *****************************************/
        // Only draw the map if the distance is not too big or the user has given us permission 
        if (drawMap) {


            numElements = nodes.length;

            var pointList = [numElements];
            // loop through all points, fetch the coordinates and switch the Lat and the Lon!!!!
            //
            // <gml:pos>-34.879769 138.506367</gml:pos>
            //
            // then create an Openlayers.Point and push it in the stack
            for(var i=0; i < numElements; i++) {
                coords = nodes[i].firstChild.nodeValue.split(' ');
                pointList[i] =  new OpenLayers.Geometry.Point(coords[1], coords[0], null).transform(proj_4326, map.getProjectionObject());
            }
            
            // create a nice green line 
            /**
             * Green style for map trail
             */
            var style_green = {
                strokeColor: "#00FF00",
                strokeOpacity: 0.7,
                strokeWidth: 3,
                pointRadius: 6,
                pointerEvents: "visiblePainted"
            };
            
            var lineFeature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(pointList),null,style_green);
            vectorLayer.addFeatures(lineFeature);
        } // if(drawMap)


		
		ajaxCallInProgress=false;
        $("#loading").css({display:"none"});
        /*if(zoomToExtent) {
            console.info(vectorLayer.getExtent());
            map.zoomToExtent(vectorLayer.getExtent());
        }*/
    
    } catch (e) {
		
       
        // Set a default map - centre of Australia
        setDefaultMap();
    }

    return true;
}

 /**
  * Function that draws the start marker
  * of a rout. 
  * 
  * @return
  */
function drawStartMarker(){
	layer = getDirectionsMarkersLayer();
	
	var features = new Array(1);
	
	features[0] = new OpenLayers.Feature.Vector(
	        new OpenLayers.Geometry.Point(startLon, startLat).transform(proj_4326, map.getProjectionObject()),
	        {type: "0"}
	); 
	        
    layer.addFeatures(features);
}

/**
 * Function that draws the end marker
 * of a rout. 
 * 
 * @return
 */
function drawEndMarker(){
    layer = getDirectionsMarkersLayer();
    
    var features = new Array(1);
    
    features[0] = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Point(endLon, endLat).transform(proj_4326, map.getProjectionObject()),
            {type: "1"}
    ); 
            
    layer.addFeatures(features);
}

/**
 * create markers layer
 */
function createDirectionsMarkersLayer(){

	var markersLayer = map.getLayersByName("Markers")[0]; //get markers layer
	
	if (markersLayer == null) {
	    
	    var styleMap = new OpenLayers.StyleMap({
	        fillOpacity: 1,
	        pointRadius: 25,
	        graphicXOffset: -44,
	        graphicYOffset: -46
	    });
	    
	    // create a lookup table with different symbolizers for 0, 1 and 2
	    var lookup = {
	            0: {externalGraphic: "./images/start.gif"},
	            1: {externalGraphic: "./images/stop.gif"}
	    };
	    
	    // add rules from the above lookup table, with the keyes mapped to
	    // the "type" property of the features, for the "default" intent
	    styleMap.addUniqueValueRules("default", "type", lookup); 
	    
	    
	    markersLayer = new OpenLayers.Layer.Vector('Markers', {
	        styleMap: styleMap, displayInLayerSwitcher: false
	    });
	    
	    //map.addLayer(markersLayer);
	    //map.raiseLayer(markersLayer,-1);
	    
	    
	    var dragFeature = new OpenLayers.Control.DragFeature(markersLayer,
	            {
	                onComplete: function (feature, pixel) {
	                    
	                    var geo = feature.geometry.clone().transform(map.getProjectionObject(), proj_4326);
	        
	                    if (feature.data.type == "0") {
	                        startLon = geo.x;
	                        startLat = geo.y;
	                    } else {
	                        endLon = geo.x;
                            endLat = geo.y;
	                    }
	                
	                    if(startLat && startLon && endLat && endLon) {
	                        directionAjaxCall();
	                    }
	    
	                }
	    });
	    map.addControl(dragFeature);
	    dragFeature.activate(); 
	    
	} else {
		//remove old markers
		markersLayer.removeFeatures(markersLayer.features); 
	}
	
	return markersLayer;
}

/**
 * Returns the layer displaying the direction
 * markers.
 * 
 * The layer is created if it doesn't exist.
 * 
 * @return
 */
function getDirectionsMarkersLayer(){

	var markersLayer = map.getLayersByName("Markers")[0]; //get markers layer
	
	if (markersLayer == null) {
	    markersLayer = createDirectionsMarkersLayer();
	}
	
	return markersLayer;
}


/**
 * Removes all markers from the direction markers
 * layer.
 * 
 * @return
 */
function clearDirectionsMarkersLayer(){
	var markersLayer = map.getLayersByName("Markers")[0]; //get markers layer
	
	if (markersLayer != null) {
	    markersLayer.removeFeatures(markersLayer.features); 
	}
}


/**
 * create vector layer
 */
function createDirectionsVectorLayer(){

	var vectorLayer = map.getLayersByName("Directions")[0]; //get markers layer
	
	if (vectorLayer == null) {
	    vectorLayer = new OpenLayers.Layer.Vector("Directions");
	    vectorLayer.displayInLayerSwitcher = false;
	} else {
	    vectorLayer.destroyFeatures(vectorLayer.features);
	}

	
	return vectorLayer;
}

 
/**
 * Draws a default map. Zooms to extent of Australia and gives notification message
 */
function setDefaultMap() {
	ajaxCallInProgress = false;
	$("#loading").css({display:"none"});
  
    // Set a default map - centre of Australia
    map.setCenter(new OpenLayers.LonLat(138.0,-27.0).transform(proj_4326, map.getProjectionObject()));
    map.zoomTo(3);
    
    alert("We were unable to provide a route between these addresses. "+"\n"+
	"This could be because they are on different land masses "+ "\n" + 
	"or unavailability of the service at present. ");
}
