/****************************************************************************
 ****************************************************************************
 *                      MAP ROUTING VARIABLES
 ****************************************************************************
 ****************************************************************************/
/*
 *  Route Information Layers 
 */
var routeDefinitionLayer = null; //Layer for route selection pins
var routePushpinsLayer = null; //Layer for route information pins

var currentMouseEvent = null; //save the mouse event to know the location

var startPoint = null; //route start point
var endPoint = null; //route end point
var intermediatePoints = new Array(); //intermediate route points

var selectedPointType = null; //current selected point type - start, end, intermediate
var currentPlaces = null; //places near the user selection

var currentCalculatedRoute = null;
var hasNearRouteEventsLayersFilled = false;

var mapPopUp; //pop up for map erros and disambiguation functionalities
var disambiguationSuccessful = false; // marks a disambiguation as a success

//Route Layers
//we'll add special layers for the route
var routeLayersNames = new Array();

jQuery(document).ready(function(){
  if(typeof(layersNames) != 'undefined') {
    jQuery.map(layersNames, function(el){routeLayersNames.push('route_'+el);});
    
     for(var i = routeLayersNames.length-1; i>-1; --i){
     	var currLayer = new VEShapeLayer();
     	currLayer.SetTitle(routeLayersNames[i]);
        map.AddShapeLayer(currLayer);
     	layers[routeLayersNames[i]] = map.GetShapeLayerCount()-1;
     }
 }
 
 //we'll bind the dropdown to the checkbox and vice versa
 var nearRouteCheckbox = jQuery('#nearEvents');
 if(nearRouteCheckbox.length) {
 	jQuery('#nearEvents').click(function(e) {
 	  if(jQuery(this).attr('checked')){
 	    jQuery('#routeOptionFilter').val('NearEvents').change();
 	  }
 	  else {
 	     jQuery('#routeOptionFilter').val('AllEvents').change();
         jQuery(this).removeAttr('checked').blur();
 	  }
 	});
 	
	jQuery('#routeOptionFilter').change(function(e){
	  if(jQuery(this).val() == 'NearEvents'){ 
	  	jQuery('#nearEvents').attr('checked',true);
	  }
	  else {     
	  	jQuery('#nearEvents').removeAttr('checked');
	  }
	  onRouteOptionFilterChange();
	});
 }

  //we'll redefine the hooks that are binded to the filter's checkboxes
   
  changeOccurrencesVisualizations = function(objId){
      //the id passed is equivalent to the custom attribute in the Liferay configuration
      var obj = document.getElementById(objId);
      personalization.visibleLayers[objId] = obj.checked;

      var hasNearRouteEventsChoosed = (jQuery("#routeOptionFilter").val() == "NearEvents");
      var layers = layerNameMapping[objId].split("|");
      for(var i = 0; i < layers.length; ++i) {
          var layerName = hasNearRouteEventsChoosed ? 'route_'+layers[i] : layers[i];
          
          if(obj.checked) showLayer(layerName);
          else            hideLayer(layerName);
      }
      jQuery('#nearEvents').attr('checked', hasNearRouteEventsChoosed ? true : ''); 
  }
  changeCamerasVisualization = function(objId) {
      var hasNearRouteEventsChoosed = (jQuery("#routeOptionFilter").val() == "NearEvents");
      
      var obj = document.getElementById(objId);
      personalization.visibleLayers[objId] = obj.checked;
      for(var i = 0; i < cameraLayers.length; ++i){
        var layerName = hasNearRouteEventsChoosed ? 'route_'+cameraLayers[i] : cameraLayers[i];
  	    if(obj.checked) showLayer(layerName);
  	    else            hideLayer(layerName);
      }
  }
 
 
 
});


/****************************************************************************
 ****************************************************************************
 *                      MAP ROUTING MENU FUNCTIONALITIES
 ****************************************************************************
 ****************************************************************************/

/*
 * Shows the context menu for selecting start and end route points
 * @Param mapEvent the VEmap Bing Event
 */
function showContextMenu(mapEvent) {
    currentMouseEvent = mapEvent;
    var menu = jQuery("#mapContextMenu");
    if (mapEvent.rightMouseButton) {
        //if rightMouseButton pressed, show context menu
        menu.css({display: "block"});
        menu.css({top: mapEvent.clientY - jQuery("#estradasPortugalMap").offset().top + "px"});
        menu.css({left: mapEvent.clientX - jQuery("#estradasPortugalMap").offset().left + "px"});
    } else {
        closeContextMenu();
    }
}

function signalRouteCalculationStart() {
    jQuery("#calcRoute").attr("disabled", true);
    jQuery(".calculating-route").show();
    jQuery("#route-error-message").hide();
}

function expandRoutePanel() {
    jQuery("#currentRoute").show();
    jQuery("#currentRoute a").click();//toggles accordion    
}

function signalRouteCalculationEnd(success) {
    jQuery('#calcRoute').removeAttr("disabled");
    jQuery(".calculating-route").hide();
    selectedPointType = null;
    if (success) {
        jQuery('#menu').css('height', '504px');
        expandRoutePanel();
    }
}


/*
 * Closes the context menu
 */
function closeContextMenu() {
    jQuery("#mapContextMenu").css({display: "none"});
}


/****************************************************************************
 ****************************************************************************
 *                      MAP ROUTING FUNCTIONALITIES
 ****************************************************************************
 ****************************************************************************/

function addAStop(placeName) {

    if (jQuery("#intermediateStops tr td").length != 0) {
        var value = jQuery("#intermediateStops tr td")[jQuery("#intermediateStops tr td").length - 1].childNodes[0].value;
        if (value == "" && placeName == undefined) {
            return;
        } else if (value == "") {
            jQuery("#intermediateStops tr td")[jQuery("#intermediateStops tr td").length - 1].childNodes[0].value = placeName;
            return;
        }
    }

    var row = document.createElement("tr");
    var td = document.createElement("td");
    var titleRow = row;

    td.innerHTML = 'Paragem ' + (jQuery('#intermediateStops tr td input').length + 1);
    td.className = "label";

    row.appendChild(td);
    jQuery("#intermediateStops")[0].appendChild(row);

    row = document.createElement("tr");
    td = document.createElement("td");

    //input
    var input = document.createElement("input");
    input.type = "text";
    input.className = "inputIntermediate input";
    if (placeName != undefined) {
        input.value = placeName;
    }

    //icon
    var img = document.createElement("img");
    img.src = "/ep-theme/images/route/intermedio.png";
    img.width = "15";

    var closeButton = document.createElement("span");
    closeButton.innerHTML = "x"
    closeButton.className = "closeButton"

    td.appendChild(img);
    td.appendChild(input);
    td.appendChild(closeButton);

    row.appendChild(td);

    jQuery("#intermediateStops")[0].appendChild(row);
    jQuery(closeButton).click(function() {
        jQuery(titleRow).remove();
        jQuery(row).remove();
    });

    //hide ads
    jQuery('#rightColumn').hide();
    jQuery('#menu').css('height', '504px');
}
/*
 *  Changes the direction of an itinerary
 *
 *  @Returns
 *      - inversedRoute - Array of VELatLong
 */
function inverseRoute() {
    //TODO: To be implemented :)
    var auxPoint = endPoint;
    endPoint = startPoint;
    startPoint = auxPoint;

    intermediatePoints.reverse();
    //TODO:UI needed actions
}

/*
 *  Stores the information about the route points given by the user.
 *  @Param pointType type of the point: "start", "end", "intermediate"
 */
function selectRoutePoint(pointType) {
    if (intermediatePoints.length > 23) {
        alert("J&aacute; tem o m&aacute;ximo de pontos interm&eacute;dios seleccionados.");
    } else {
        var point = map.PixelToLatLong(new VEPixel(currentMouseEvent.mapX, currentMouseEvent.mapY));
        selectedPointType = pointType;
    }
    if (point != null) {
        map.FindLocations(point, locationFindCallback);
    }
    closeContextMenu();
}

/*
 *  Saves the route disambiguated point
 *
 *  @Param point map point to be saved
 */
function saveRoutePoint(point) {
    switch (selectedPointType) {
        case "start":
            startPoint = point.LatLong;
            break;
        case "end":
            endPoint = point.LatLong;
            break;
        case "intermediate":
            intermediatePoints.push([point.LatLong, point.Name]);
            break;
        default:
            //do nothing, problaby a user hack
            break;
    }

    if (startPoint != null && endPoint != null) calculateRoute();
}

/*
 *  Callback function to FindLocations method
 *  @Params nearbyPlaces VEPlace[] with the places near the user selection
 */
function locationFindCallback(nearbyPlaces) {
    if (nearbyPlaces == null) {
        //TODO: show pop up box
        alert("Escolheu um local inválido, por favor tente novamente.");
        return;
    }
    currentPlaces = nearbyPlaces;
    if (nearbyPlaces.length == 1) {
        disambiguationSelection(0, nearbyPlaces[0].Name);
    } else {
        var html = "<div class=\"location-suggestion-title\">Sugestões para local interm&eacute;dio<span style='font-size: 9px;'>&nbsp;&nbsp;(veja no mapa as localizações respectivas)</span></div><div class=\"location-suggestions\">";

        var allLocations = new Array();
        disambiguationPins = new Array();
        for (var index = 0; index < nearbyPlaces.length; ++index) {
            html += "<div class=\"location-suggestion\" onclick='javascript:removeDisambiguationPins();disambiguationSelection(this.id, \"" + nearbyPlaces[index].Name + "\");mapPopUp.dialog(\"close\");' id='" + index + "' style='cursor:pointer;'>" + (index+1) + " - " + nearbyPlaces[index].Name + "</div>";

            var pin = new VEShape(VEShapeType.Pushpin, vePlaces[index].LatLong);
            pin.SetTitle(vePlaces[index].Name);

            if(index >= 9)
                pin.SetCustomIcon("<div class='disambiguation-icon-two-digits'>" + (index + 1) + "</div><img src='/ep-theme/images/map/markers/disambiguation.png'/>");
            else
                pin.SetCustomIcon("<div class='disambiguation-icon'>" + (index + 1) + "</div><img src='/ep-theme/images/map/markers/disambiguation.png'/>");

            map.AddShape(pin);
            disambiguationPins[index] = pin;

            allLocations[index] = vePlaces[index].LatLong;
        }
        html += "</div>";

        map.SetMapView(allLocations);

        mapDisambiguationPopUp(html, "Foi encontrado mais do que um local similar ao indicado.");
    }
}

/*
 *  Selection of a place when various are available
 *  @Params index index of the place received by callback
 */
function disambiguationSelection(index, placeName) {
    saveRoutePoint(currentPlaces[index]);
    loadRouteShapes();

    if (placeName != null) {
        if (selectedPointType == "start") {
            jQuery("#startPointTextDefinition").val(placeName);
        }

        if (selectedPointType == "end") {
            jQuery("#endPointTextDefinition").val(placeName);
        }

        if (selectedPointType == "intermediate") {
            addAStop(placeName);
        }
    }

    disambiguationSuccessful = true;
}

/*
 *  Loads the shapes of the route.
 *  The shapes loaded are the start and end points and all the intermediate.
 */
function loadRouteShapes() {
    var shape;
    try {
        if (intermediatePoints.length != 0) {
            for (var i = 0; i < intermediatePoints.length; ++i) {
                shape = new VEShape(VEShapeType.Pushpin, intermediatePoints[i][0]);
                shape.SetCustomIcon("/ep-theme/images/route/intermedio.png");
                routeDefinitionLayer.AddShape(shape);
            }
        }
        if (startPoint != null) {
            removeRouteStartShape();

            shape = new VEShape(VEShapeType.Pushpin, startPoint);
            shape.SetCustomIcon("/ep-theme/images/route/inicio.png");
            routeDefinitionLayer.AddShape(shape);
        }
        if (endPoint != null) {
            removeRouteEndShape();

            shape = new VEShape(VEShapeType.Pushpin, endPoint);
            shape.SetCustomIcon("/ep-theme/images/route/chegada.png");
            routeDefinitionLayer.AddShape(shape);
        }
    } catch(ex) {
        //no routeDefinitionLayer defined :)
    }
}

function removeRouteStartShape() {
    for (i = 0; i < routeDefinitionLayer.GetShapeCount(); i++) {
        currentShape = routeDefinitionLayer.GetShapeByIndex(i);

        if(Liferay.Util.endsWith(currentShape.GetCustomIcon().Image, "inicio.png")) {
            routeDefinitionLayer.DeleteShape(currentShape);

            return;
        }
    }
}

function removeRouteEndShape() {
    for (i = 0; i < routeDefinitionLayer.GetShapeCount(); i++) {
        currentShape = routeDefinitionLayer.GetShapeByIndex(i);

        if(Liferay.Util.endsWith(currentShape.GetCustomIcon().Image, "chegada.png")) {
            routeDefinitionLayer.DeleteShape(currentShape);

            return;
        }
    }
}

/*
 *  Fires the pop up with the needed content
 */
function mapDisambiguationPopUp(html, title) {
    disambiguationSuccessful = false;

    mapPopUp = Liferay.Popup({
        title: title != null ? title : "Escolha o local que pretende",
        message: html,
        modal: true,
        resizable: false,
        width: 370,
        onClose: function() {
            if (!disambiguationSuccessful) {
                signalRouteCalculationEnd(false);
            }
        }
    });

    jQuery(".location-suggestions div:nth-child(odd)").addClass("location-suggestion-odd");
}

/*
 * Calculates the route previously selected
 */
function calculateRoute(routeOptimization) {
    jQuery('#my-routes tr').removeClass('selected'); //un-select routes

    currentCalculatedRoute = null;
    hasNearRouteEventsLayersFilled = false;
    //clear old shapes       
    for(var i = 0; i < routeLayersNames.length; ++i){
       map.GetShapeLayerByName(routeLayersNames[i]).DeleteAllShapes();
    }    
    
    signalRouteCalculationStart();

    var routeOptions = buildRouteOptions(routeOptimization);
    var directions = new Array();

    directions.push(startPoint);
    for (var index = 0; index < intermediatePoints.length; ++index) {
        directions.push(intermediatePoints[index][0]);
    }
    directions.push(endPoint);

    map.GetDirections(directions, routeOptions);

    try {
        var startRouteOptions = buildRouteOptions(routeOptimization);
        startRouteOptions.RouteCallback = startCalculateRouteCallback;
        startMap.GetDirections(directions, startRouteOptions);

        var endRouteOptions = buildRouteOptions(routeOptimization);
        endRouteOptions.RouteCallback = endCalculateRouteCallback;
        endMap.GetDirections(directions, endRouteOptions);
    } catch(ex) {
        //no print maps
    }
    try {
        routeDefinitionLayer.DeleteAllShapes();
        routePushpinsLayer.DeleteAllShapes();
    } catch(ex) {
        //no layer to clear
    }
}

function startCalculateRouteCallback(route) { calculateRouteCallback(route, startMap); }
function endCalculateRouteCallback(route) { calculateRouteCallback(route, endMap); }

/*
 *  Builds a route options object.
 *  @Returns a VERouteOptions object - see the documentation
 */
function buildRouteOptions(routeOptimization) {
    var routeOptions = new VERouteOptions();
    routeOptions.DistanceUnit = VERouteDistanceUnit.Kilometer;
    routeOptions.DrawRoute = true; //default
    routeOptions.RouteCallback = calculateRouteCallback;
    routeOptions.RouteMode = VERouteMode.Driving; //default

    var select = jQuery("#routeOption");
    var value = "MinimizeTime";
    if (routeOptimization == undefined) {
        if (select.length != 0) {
            value = jQuery("table td select")[0].value;
        }
    } else {
        value = routeOptimization;
    }

    if (value == "MinimizeTime") {
        routeOptions.RouteOptimize = VERouteOptimize.MinimizeTime; //default
    } else {
        routeOptions.RouteOptimize = VERouteOptimize.MinimizeDistance;
    }

    routeOptions.SetBestMapView = true //default;
    routeOptions.ShowDisambiguation = false; //default:true - TODO: translate the disambiguation dialog if needed
    routeOptions.ShowErrorMessages = false; //default:true - TODO: translate error messages?

    return routeOptions;
}

/*
 * Print callback function place holder
 */
var printCallback = function() {
};

/*
 *  Callback funtion for CalculateRoute map function.
 *
 *  @Params
 *      - route - The VERoute returned with all the route information for the user
 *                selection.
 */
function calculateRouteCallback(route, routeParent) {
    var currMap;
    if (routeParent == undefined) {
        currMap = map;
    } else {
        currMap = routeParent;
    }

    if (route == null || route.Distance == 0) {
        noRoutePossibleError();
        signalRouteCalculationEnd(false);
        return;
    }

    currentCalculatedRoute = route;

    var container = null;
    var containerList = jQuery("#routeSteps");
    if (containerList.length != 0) {
        container = jQuery("#routeSteps")[0];
        jQuery("#routeSteps")[0].innerHTML = "";
        jQuery(".column.right").hide();
        jQuery("#routingSideMenu .header").show();
        jQuery("#routingSideMenu .routeContent").show();
        jQuery("#routeStepsHeader").show();
    }
    /*
     *  Remove all of the auto-generated route pushpins
     *  Iterate through each VERouteLeg, adding pushpins at each item in the VEItinerary array
     */
    currMap.DeleteShapeLayer(currMap.GetShapeLayerByIndex(currMap.GetShapeLayerCount() - 1));

    if (routeParent == undefined) {
        if (routeDefinitionLayer != null) routeDefinitionLayer.DeleteAllShapes();
        routePushpinsLayer.DeleteAllShapes();
    }

    expandRoutePanel();

    //Unroll all route legs
    var legs = route.RouteLegs;
    var numTurns = -1;

    // Leg is a VERouteLeg object
    var leg = null;
    //the shape to be created
    var shape = null;

    var shapeArray = new Array();
    var startShape = null;
    var endShape = null;
    // Get intermediate legs
    for (var i = 0; i < legs.length; i++)
    {
        // Get this leg so we don't have to derefernce multiple times
        leg = legs[i];

        for (var j = 0; j < leg.Itinerary.Items.length; j ++)
        {
            numTurns++;
            if (container != null) {
                jQuery("#routeDistance")[0].innerHTML = truncateDist(route.Distance);
                jQuery("#routeTime")[0].innerHTML = convertTime(route.Time);

                var div = document.createElement("div");
                div.className = "routeStep";

                var table = document.createElement("table");
                table.cellpadding = "0";
                table.cellspacing = "0";
                table.style.width = "100%";
                var row = document.createElement("tr");

                var number = document.createElement("td");
                number.className = "routeStepNumber";

                if (j == 0 && i == 0) {
                    var customIcon = new VECustomIconSpecification();
                    customIcon.CustomHTML = "<img src='/ep-theme/images/route/inicio.png'/>";
                    var shape = new VEShape(VEShapeType.Pushpin, leg.Itinerary.Items[j].LatLong);
                    shape.SetCustomIcon(customIcon);
                    shape.SetDescription(leg.Itinerary.Items[j].Text);
                    //routePushpinsLayer.AddShape(shape);
                    startShape = shape;
                    number.innerHTML = '<img src="/ep-theme/images/route/inicio.png" width="18px" />';
                } 
                else if (j == leg.Itinerary.Items.length - 1 && i == legs.length - 1) {
                    var customIcon = new VECustomIconSpecification();
                    customIcon.CustomHTML = "<img src='/ep-theme/images/route/chegada.png'/>";
                    var shape = new VEShape(VEShapeType.Pushpin, leg.Itinerary.Items[j].LatLong);
                    shape.SetCustomIcon(customIcon);
                    shape.SetDescription(leg.Itinerary.Items[j].Text);
                    //routePushpinsLayer.AddShape(shape);
                    endShape = shape;
                    number.innerHTML = '<img src="/ep-theme/images/route/chegada.png" width="18px" />';
                } 
                else if (j == leg.Itinerary.Items.length - 1) {
                    var customIcon = new VECustomIconSpecification();
                    customIcon.CustomHTML = "<img src='/ep-theme/images/route/intermedio.png'/>";
                    var shape = new VEShape(VEShapeType.Pushpin, leg.Itinerary.Items[j].LatLong);
                    shape.SetCustomIcon(customIcon);
                    shape.SetDescription(leg.Itinerary.Items[j].Text);
                    //routePushpinsLayer.AddShape(shape);
                    shapeArray.push(shape);

                    number.innerHTML = '<img src="/ep-theme/images/route/intermedio.png" width="18px" />';
                }
                else {
                    number.innerHTML = numTurns;
                    var customIcon = new VECustomIconSpecification();
                    customIcon.CustomHTML = '<div style="position: relative;"><div style="position: absolute; font-weight: bold; font-size: 9pt; margin-top:0px;"><table style="height: 28px; width: 28px;"><tbody><tr><td style="text-align: center;">' + numTurns + '</td></tr></tbody></table></div><img src="/ep-theme/images/route/intermedio.png"/></div>';
                    var shape = new VEShape(VEShapeType.Pushpin, leg.Itinerary.Items[j].LatLong);
                    shape.SetCustomIcon(customIcon);
                    shape.SetDescription(leg.Itinerary.Items[j].Text);
                    //routePushpinsLayer.AddShape(shape);
                    shapeArray.push(shape);
                }

                var description = document.createElement("td");
                description.className = "routeStepDescription";

                if (j == leg.Itinerary.Items.length - 1 && i < legs.length - 1)
                    description.innerHTML = leg.Itinerary.Items[j].Text + " <span class=\"routeStepComment\">" + Encoder.htmlEncode(intermediatePoints[i][1]) + "</span>";
                else
                    description.innerHTML = leg.Itinerary.Items[j].Text;

                var distance = document.createElement("td");
                distance.className = "routeStepDistance";
                //calculate distance
                var dist = leg.Itinerary.Items[j].Distance;
                dist = truncateDist(dist);

                if (dist != "0 m") distance.innerHTML = dist;

                row.appendChild(number);
                row.appendChild(description);
                row.appendChild(distance);

                table.appendChild(row);
                div.appendChild(table);
                div.innerHTML = "<div onclick='try { centerMapAndZoomCamera(" + leg.Itinerary.Items[j].LatLong.Latitude + "," + leg.Itinerary.Items[j].LatLong.Longitude + ") } catch(ex) {}'>" + div.innerHTML + "</div>";
                container.appendChild(div);
            }
        }
    }

    if (routeParent == undefined) {
        routePushpinsLayer.AddShape(shapeArray);
        routePushpinsLayer.AddShape(startShape);
        routePushpinsLayer.AddShape(endShape);
    } else {
        var currentRoutePushpinsLayer = new VEShapeLayer();
        currMap.AddShapeLayer(currentRoutePushpinsLayer);

        currentRoutePushpinsLayer.AddShape(shapeArray);
        currentRoutePushpinsLayer.AddShape(startShape);
        currentRoutePushpinsLayer.AddShape(endShape);
    }

    setTimeout("printCallback()", 1000);

    loadRouteShapes();
    try {
        onRouteOptionFilterChange();
    } catch(ex) {
        //ignore
    }

    signalRouteCalculationEnd(true);
}

function truncateDist(dist) {
    if (dist < 1) {
        //it's meters
        dist = Math.round(dist * 1000) + " m";
    } else {
        //it's kilometer'
        dist = Math.round(dist * 10) / 10 + " km";
    }
    return dist;
}

function convertTime(seconds) {
    var times = time(seconds * 1000);
    return times.substring(2, times.length - 4);
}

function two(x) {
    return ((x > 9) ? "" : "0") + x;
}
function three(x) {
    return ((x > 99) ? "" : "0") + ((x > 9) ? "" : "0") + x;
}

function time(ms) {
    var sec = Math.floor(ms / 1000);
    ms = ms % 1000;
    t = three(ms);

    var min = Math.floor(sec / 60);
    sec = sec % 60;
    t = two(sec) + ":" + t;

    var hr = Math.floor(min / 60);
    min = min % 60;
    t = two(min) + ":" + t;

    var day = Math.floor(hr / 60);
    hr = hr % 60;
    t = two(hr) + ":" + t;
    t = day + ":" + t;

    return t;
}

/*
 *  Clears the route and map
 */
function clearRouteAndMap() {
    startPoint = null;
    endPoint = null;
    currentCalculatedRoute = null;
    intermediatePoints = new Array();

    routePushpinsLayer.DeleteAllShapes();
    routeDefinitionLayer.DeleteAllShapes();

    try {
        map.GetDirections("");
    } catch(e) {
        // ignore
    }

    jQuery('#my-routes tr').removeClass('selected');

    jQuery("#routeSteps")[0].innerHTML = "";
    jQuery("#currentRoute").hide('fast');

    jQuery("#startPointTextDefinition").val("");
    jQuery("#endPointTextDefinition").val("");
    jQuery("#routeOption").val(null);

    jQuery("#routingSideMenu").css("height", "");
    jQuery("#intermediateStops")[0].innerHTML = "";
    jQuery("#searchRoute a").click();

    jQuery('#rightColumn').show();
    jQuery('#menu').css('height', '');
    
    onRouteOptionFilterChange();    
}

/*
 * Initialization of routing options  - On the specific liferay page
 */
jQuery(document).ready(function() {
    try {
        routeDefinitionLayer = new VEShapeLayer();
        routePushpinsLayer = new VEShapeLayer();
        routeDefinitionLayer.SetTitle('UserRouteDefinition');
        routePushpinsLayer.SetTitle('UserRoutePushpins');
        
        map.AddShapeLayer(routeDefinitionLayer);
        map.AddShapeLayer(routePushpinsLayer);
        map.AttachEvent("onclick", showContextMenu);
    } catch(ex) {
        //map not defined :)
    }
});

function verifyFieldCompletion() {
    if (jQuery("#startPointTextDefinition")[0].value != "" && jQuery("#endPointTextDefinition")[0].value != "") {
        startField = jQuery("#startPointTextDefinition")[0].value;
        endField = jQuery("#endPointTextDefinition")[0].value;

        return true;
    }
    else {
        alert("Por favor indique o local de origem e destino.");

        return false;
    }
}

var startField, endField;

function evaluateStartPoint() {
    if (verifyFieldCompletion()) {
        //fields filled
        intermediatePoints = new Array();
        signalRouteCalculationStart();
        prepareIntermediateArray();
        map.Find(null, startField, null, null, 0, 10, false, false, false, false, findStartCallback);
    }
}

var intermediateField;

function prepareIntermediateArray() {
    intermediateField = new Array();
    jQuery("#intermediateStops tr td input").each(function(i, selected) {
        var curr = jQuery(selected);
        if (curr.val().trim() != '')  intermediateField.push(curr.val());
    });

}

var locationResults = null;

function noRoutePossibleError() {
    jQuery("#route-error-message td").html("Erro: Não existe um caminho possível");
    jQuery("#route-error-message").show();

    jQuery("#routeSteps")[0].innerHTML = "";
    jQuery("#routingSideMenu .show").hide();
    jQuery("#routingSideMenu .routeContent").hide();
    jQuery("#routeStepsHeader").hide();

    signalRouteCalculationEnd(false);
}

function findStartCallback(veShapeLayer, veFindResults, vePlaces, more, errorMessage) {
    vePlaces = filterPlaces(vePlaces);
    if (vePlaces == null) {
        clearRouteAndMap();
        noRoutePossibleError();

        signalRouteCalculationEnd(false);
        return;
    }
    if (vePlaces.length > 1) {
        locationResults = vePlaces;
        var html = "<div class=\"location-suggestion-title\">Sugest&otilde;es para local de origem<span style='font-size: 9px;'>&nbsp;&nbsp;(veja no mapa as localizações respectivas)</span></div><div class=\"location-suggestions\">";

        var allLocations = new Array();
        disambiguationPins = new Array();
        for (var i = 0; i < vePlaces.length; ++i) {
            html += "<div class=\"location-suggestion\" onclick='javascript:removeDisambiguationPins();locationSelect(this.id, \"start\",\"" + vePlaces[i].Name + "\");mapPopUp.dialog(\"close\");' id='" + i + "' style='cursor:pointer;'>" + (i+1) + " - " + vePlaces[i].Name + "</div>";

            var pin = new VEShape(VEShapeType.Pushpin, vePlaces[i].LatLong);
            pin.SetTitle(vePlaces[i].Name);

            if(i >= 9)
                pin.SetCustomIcon("<div class='disambiguation-icon-two-digits'>" + (i + 1) + "</div><img src='/ep-theme/images/map/markers/disambiguation.png'/>");
            else
                pin.SetCustomIcon("<div class='disambiguation-icon'>" + (i + 1) + "</div><img src='/ep-theme/images/map/markers/disambiguation.png'/>");

            map.AddShape(pin);
            disambiguationPins[i] = pin;

            allLocations[i] = vePlaces[i].LatLong;
        }
        html += "</div>";

        map.SetMapView(allLocations);

        mapDisambiguationPopUp(html, "Foi encontrado mais do que um local similar ao indicado.");
    } else {
        startPoint = vePlaces[0].LatLong;
        if (intermediateField.length != 0) {
            intermediateCallbackCounter = 0;
            map.Find(null, intermediateField[0], null, null, 0, 10, false, false, false, false, findIntermediateCallback);
        } else {
            map.Find(null, endField, null, null, 0, 10, false, false, false, false, findEndCallback);
        }
    }
}

var intermediateCallbackCounter = 0;

function findIntermediateCallback(veShapeLayer, veFindResults, vePlaces, more, errorMessage) {
    vePlaces = filterPlaces(vePlaces);
    if (vePlaces == null) {
        clearRouteAndMap();
        noRoutePossibleError();

        signalRouteCalculationEnd(false);

        return;
    }
    if (vePlaces.length > 1) {
        locationResults = vePlaces;
        var html = "<div class=\"location-suggestion-title\">Sugest&otilde;es para o local intermédio " + (intermediateCallbackCounter + 1) + ":</div><div class=\"location-suggestions\">";

        var allLocations = new Array();
        disambiguationPins = new Array();
        for (var i = 0; i < vePlaces.length; ++i) {
            html += "<div class=\"location-suggestion\" onclick='javascript:removeDisambiguationPins();locationSelect(this.id, \"intermediate\",\"" + vePlaces[i].Name + "\");mapPopUp.dialog(\"close\");' id='" + i + "' style='cursor:pointer;'>" + (i+1) + " - " + vePlaces[i].Name + "</div>";

            var pin = new VEShape(VEShapeType.Pushpin, vePlaces[i].LatLong);
            pin.SetTitle(vePlaces[i].Name);

            if(i >= 9)
                pin.SetCustomIcon("<div class='disambiguation-icon-two-digits'>" + (i + 1) + "</div><img src='/ep-theme/images/map/markers/disambiguation.png'/>");
            else
                pin.SetCustomIcon("<div class='disambiguation-icon'>" + (i + 1) + "</div><img src='/ep-theme/images/map/markers/disambiguation.png'/>");

            map.AddShape(pin);
            disambiguationPins[i] = pin;

            allLocations[i] = vePlaces[i].LatLong;
        }
        html += "</div>";

        map.SetMapView(allLocations);

        mapDisambiguationPopUp(html, "Foi encontrado mais do que um local similar ao indicado.");
    } else {
        intermediatePoints.push([vePlaces[0].LatLong, vePlaces[0].Name]);
        ++intermediateCallbackCounter;
        if (intermediateCallbackCounter < intermediateField.length) {
            map.Find(null, intermediateField[intermediateCallbackCounter], null, null, 0, 10, false, false, false, false, findIntermediateCallback);
        } else {
            map.Find(null, endField, null, null, 0, 10, false, false, false, false, findEndCallback);
        }
    }
}

function locationSelect(selectionIndex, pointType, selectionName) {
    if (pointType == "start") {
        startPoint = locationResults[selectionIndex].LatLong;
        map.Find(null, endField, null, null, 0, 10, false, false, false, false, findEndCallback);

        jQuery("#startPointTextDefinition").val(selectionName);
    }
    if (pointType == "end") {
        endPoint = locationResults[selectionIndex].LatLong;
        jQuery("#endPointTextDefinition").val(selectionName);
        calculateRoute();
    }
    if (pointType == "intermediate") {
        intermediatePoints.push([locationResults[selectionIndex].LatLong, locationResults[selectionIndex].Name]);
        ++intermediateCallbackCounter;
        if (intermediateCallbackCounter < intermediateField.length) {
            map.Find(null, intermediateField[intermediateCallbackCounter], null, null, 0, 10, false, false, false, false, findIntermediateCallback);
        } else {
            map.Find(null, endField, null, null, 0, 10, false, false, false, false, findEndCallback);
        }
    }
}

function findEndCallback(veShapeLayer, veFindResults, vePlaces, more, errorMessage) {
    vePlaces = filterPlaces(vePlaces);
    if (vePlaces == null) {
        clearRouteAndMap();
        noRoutePossibleError();

        signalRouteCalculationEnd(false);

        return;
    }
    if (vePlaces.length > 1) {
        locationResults = vePlaces;
        var html = "<div class=\"location-suggestion-title\">Sugestões para local de destino<span style='font-size: 9px;'>&nbsp;&nbsp;(veja no mapa as localizações respectivas)</span></div><div class=\"location-suggestions\">";

        var allLocations = new Array();
        disambiguationPins = new Array();
        for (var i = 0; i < vePlaces.length; ++i) {
            html += "<div class=\"location-suggestion\" onclick='javascript:removeDisambiguationPins();locationSelect(this.id, \"end\", \"" + vePlaces[i].Name + "\");mapPopUp.dialog(\"close\");' id='" + i + "' style='cursor:pointer;'>" + (i+1) + " - " + vePlaces[i].Name + "</div>";

            var pin = new VEShape(VEShapeType.Pushpin, vePlaces[i].LatLong);
            pin.SetTitle(vePlaces[i].Name);

            if(i >= 9)
                pin.SetCustomIcon("<div class='disambiguation-icon-two-digits'>" + (i + 1) + "</div><img src='/ep-theme/images/map/markers/disambiguation.png'/>");
            else
                pin.SetCustomIcon("<div class='disambiguation-icon'>" + (i + 1) + "</div><img src='/ep-theme/images/map/markers/disambiguation.png'/>");

            map.AddShape(pin);
            disambiguationPins[i] = pin;

            allLocations[i] = vePlaces[i].LatLong;
        }
        html += "</div>";

        map.SetMapView(allLocations);

        mapDisambiguationPopUp(html, "Foi encontrado mais do que um local similar ao indicado.");
    } else {
        endPoint = vePlaces[0].LatLong;
        calculateRoute();
    }
}

/**
 * Creates a VELatLong object from a js object
 * that contains the VELatLong properties
 * @returns a VELatLong object
 */
function copyVELatLong(LatLongObj) {
    var ret = new VELatLong();
    jQuery.each(LatLongObj, function(key, value) {
        ret[key] = value;
    });
    return ret;
}

/**
 * Creates a VELatLong array from a js object
 * that contains an array with objects with
 * VELatLong properties
 * @returns a VELatLong array
 */
function copyVELatLongArray(LatLongArray) {
    var returnArray = new Array();
    jQuery.each(LatLongArray, function(key, value) {
        var aux = new Object();
        aux[0] = copyVELatLong(value[0]);
        aux[1] = value[1];
        returnArray.push(aux);
    });
    return returnArray;
}
/**
 * Copies VELatLongArray, discarding text data.
 */
function copyVELatLongArrayDiscardText(LatLongArray) {
    var returnArray = new Array();
    jQuery.each(LatLongArray, function(key, value) {
		if(value instanceof Array) returnArray.push(copyVELatLong(value[0]));
		else                       returnArray.push(copyVELatLong(value));
    });
    return returnArray;
}

//callback for route option change evt
function onRouteOptionFilterChange() {
    if (jQuery("#routeOptionFilter").val() == "NoEvents")  {
    	showAllLayers(false);
    	showAllRouteLayers(false);
    }
    else if (jQuery("#routeOptionFilter").val() == "AllEvents") {
    	showAllLayers(true);
    	showAllRouteLayers(false);
    }
    else if (jQuery("#routeOptionFilter").val() == "NearEvents") {
        showAllLayers(false);                
        showAllRouteLayers(true);
        jQuery("#filtersOptions input").attr("checked", true);
        if(!hasNearRouteEventsLayersFilled) showNearRouteEvents();
    }
}

/**
 * Toggles all route layers.
 * @param allLAyers true to show all route layers, false otherwise 
 */ 
function showAllRouteLayers(allLayers) {
    for(var i = 0; i < routeLayersNames.length; ++i){
        if(allLayers == true)  showLayer(routeLayersNames[i]);
        else                   hideLayer(routeLayersNames[i]);
    }
}

/**
 * Shows all shapes.
 * @param val a boolean indicating if the shapes are to show or to hide
 */
function showAllShapes(val) {
      for(var i=0; i < map.GetShapeLayerCount(); i++) {
    	   var shapeLayer = map.GetShapeLayerByIndex(i);
           for (var k = 0; k < shapeLayer.GetShapeCount(); k++) {
              if(val == true) shapeLayer.GetShapeByIndex(k).Show();
              else            shapeLayer.GetShapeByIndex(k).Hide();
           }
       }
}

//shows all events near the current route
function showNearRouteEvents() {
       if (currentCalculatedRoute == null) return;   
       signalMapWorkingStart('A calcular os pontos próximos do percurso...');    
       
       var MinimumDistance = 500; //500m
       var MinimumRouteLeg = 1000;

       var last = currentCalculatedRoute.ShapePoints.length;
       var lastProcessedShapePoint = null;
       var def = new jQuery.deferred(); 
       
       var allLayers = jQuery.map(layersNames, function(name,i){
          var l = map.GetShapeLayerByName(layersNames[i]);
          if(l != null && l.GetShapeCount() > 0) return l;
       });  
       
       def.loop({begin:0, end:currentCalculatedRoute.ShapePoints.length, step:1}, function(i) {
			try {
	           if(i >= last) {signalMapWorkingStop();}
	           
	           var currentShapePoint = currentCalculatedRoute.ShapePoints[i];
	           // only process shape points in every 1000 meters
	           if (lastProcessedShapePoint != null) {
	               if (distanceBetweenPoints(currentShapePoint.Latitude, currentShapePoint.Longitude, lastProcessedShapePoint.Latitude, lastProcessedShapePoint.Longitude) < MinimumRouteLeg)
	               return;//continue //continue is if we use the for() cycle
	
	               for (var j = 0; j < allLayers.length; j++) {
	                   var shapeLayer = allLayers[j];
	
	                   for (var k = 0; k < shapeLayer.GetShapeCount(); k++) {
	                       var currentShape = shapeLayer.GetShapeByIndex(k);
	                       var shapeLocation = currentShape.GetPoints()[0];
	                       var dist = distanceBetweenLineAndPoint(lastProcessedShapePoint.Latitude, lastProcessedShapePoint.Longitude, currentShapePoint.Latitude, currentShapePoint.Longitude, shapeLocation.Latitude, shapeLocation.Longitude);
	                       if (dist < MinimumDistance) {                           
	                           var copy = new VEShape(currentShape.GetType(), currentShape.GetPoints());
	                           copy.SetCustomIcon(currentShape.GetCustomIcon());
	                           copy.SetTitle(currentShape.GetTitle());
	                           copy.SetDescription(currentShape.GetDescription());
	                           
	                           var routeLayer = map.GetShapeLayerByName('route_'+currentShape.GetShapeLayer().GetTitle());
	                           routeLayer.AddShape(copy);
	                       }     
	                   }
	               }           
	           }
	
	           lastProcessedShapePoint = currentShapePoint;
   			}catch(ex) {
   				signalMapWorkingStop();
   				def.cancel();
			}
       });
       def.call();
       hasNearRouteEventsLayersFilled = true;      
}

function distanceBetweenPoints2(lat1, lon1, lat2, lon2) {
        var R = 6371; // km
        var dLat = deg2rad(lat2 - lat1);
        var dLon = deg2rad(lon2 - lon1);
        var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
                Math.sin(dLon / 2) * Math.sin(dLon / 2);
        var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return R * c;
}

function bearing(lat1, lon1, lat2, lon2) {
    lat1 = deg2rad(lat1);
    lat2 = deg2rad(lat2);
    var dLon = deg2rad(lon2 - lon1);

    var y = Math.sin(dLon) * Math.cos(lat2);
    var x = Math.cos(lat1) * Math.sin(lat2) -
            Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);

    return toBrng(Math.atan2(y, x));
}

function toBrng(value) {
    return (rad2deg(value)+360) % 360;
}

function distanceBetweenLineAndPoint2(lat1, lon1, lat2, lon2, lat3, lon3) {
    var R = 6371; // km

    var d13 = distanceBetweenPoints2(lat1, lon1, lat3, lon3);
    var brng12 = bearing(lat1, lon1, lat2, lon2);
    var brng13 = bearing(lat1, lon1, lat3, lon3);
    var dXt = Math.asin(Math.sin(d13 / R) * Math.sin(brng13 - brng12)) * R;

    return Math.abs(dXt);
}

//haversine distance
function distanceBetweenPoints(lat1, lon1, lat2, lon2) {
       var theta = lon1 - lon2;

       var dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta));
       dist = Math.acos(dist);
       dist = rad2deg(dist);

       return dist * 60 * 1.1515 * 1000;
}

//calculates a distance between a line and a point
function distanceBetweenLineAndPoint(lat1, lon1, lat2, lon2, lat3, lon3) {
       return Math.abs(distanceBetweenPoints(lat1, lon1, lat2, lon2) - distanceBetweenPoints(lat1, lon1, lat3, lon3) - distanceBetweenPoints(lat2, lon2, lat3, lon3));
}

//converts degrees to radians
function deg2rad(deg) {
       return (deg * Math.PI / 180);
}

//converts radians to degrees  
function rad2deg(rad) {
       return (rad * 180 / Math.PI);
}

// makeClass - By John Resig (MIT Licensed)
// http://ejohn.org/blog/simple-class-instantiation/
function makeClass() {
    return function(args) {
        if (this instanceof arguments.callee) {
            if (typeof this.init == "function") {
                this.init.apply(this, args.callee ? args : arguments);
            }
        } else {
            return new arguments.callee(arguments);
        }
    };
}


/****************************************************************************
 ****************************************************************************
 *                      ROUTE CLASS
 ****************************************************************************
 ****************************************************************************/

//Class definition
var Route = makeClass();

/**
 * Creates a new route.
 *
 * @param startName the start name OR a JSON Object
 * @param endName the end name
 * @param intermediateNames the intermediate names (array)
 * @param startCoord the start point (latlong)
 * @param endCoord the end point (latlong)
 * @param intermediateCoord the intermediate points (latlong array)
 * @param option the route option.
 * @param routeName the route name
 * @return a new Route
 */
Route.prototype.init = function(route) {
    //building from JSON object   
    this.routeId = route.routeId || '';
    this.routeName = route.routeName || '';
    this.startName = route.startName;
    this.endName = route.endName;
    this.intermediateNames = route.intermediateNames || new Array();
    this.startPoint = copyVELatLong(route.startCoord || route.startPoint);
    this.endPoint = copyVELatLong(route.endCoord || route.endPoint);
    this.intermediatePoints = copyVELatLongArrayDiscardText(route.intermediateCoords || route.intermediatePoints);
    this.routeOption = route.option;

    this.isAlertSMS = route.isAlertSMS || false;
    this.isAlertEmail = route.isAlertEmail || false;
    this.alertTime = route.alertTime || [];
}

//toJSON decl
Route.prototype.toJSON = function() {
    return jQuery.toJSON(this);
}

//writes to field pages
Route.prototype.writeToDocument = function() {
    jQuery('#startPointTextDefinition').val(this.startName);
    jQuery('#endPointTextDefinition').val(this.endName);
    jQuery('#routeOption').val(this.routeOption);
    window.startPoint = this.startPoint;
    window.endPoint = this.endPoint;

    jQuery('#intermediateStops tr').remove();
	window.intermediatePoints = new Array();
	for(var i=0; i < this.intermediatePoints.length; i++) {
		window.intermediatePoints.push([this.intermediatePoints[i], this.intermediateNames[i]]);
	}
	
    //adds the inputs for the intermediate stops
    while (jQuery('.inputIntermediate').length < this.intermediateNames.length) {
        addAStop();
    }

    for (var i = 0; i < this.intermediateNames.length; i++) {
        jQuery('.inputIntermediate')[i].value = this.intermediateNames[i];
    }
}

/**
 * Returns a new route based on current document params.
 * @return a new route
 */
var currentRoute = function() {
	var route = {
            startName: jQuery('#startPointTextDefinition').val(),
            endName: jQuery('#endPointTextDefinition').val(),
            intermediateNames: jQuery('.inputIntermediate').map(function() { return jQuery(this).val()}).get(),
            startCoord: window.startPoint,
            endCoord: window.endPoint,
            intermediateCoords: window.intermediatePoints,
            option: jQuery('#routeOption').val()
	};
    return new Route(route);
} 
