   //////////////////
  //    GLOBAL    //
 //   VARIABLES  //
//////////////////

var map;                //OL map-object
var hotLocality;        //header HL

var markersCoordinates; 
var markersAddresses;
var mapLabel;
var rightClickPopUp;    //Popup to set start and end of route

//variables used by the directory functionality.
var directoryLat;       //search position: latitude
var directoryLon;       //search position: longitude
var direcotryAddress;   //search position: address
var directoryParams = {
        dir: "PSMA",
        max: "99",
        name: null
      };    //search parameters
var geoRSSlayer;        //layer displaying search results
var rssMetadata  = new OpenLayers.Format.MBMetaData();

var proj_4326 = new OpenLayers.Projection("EPSG:4326");


/*
 * This construct is used to determine whether a (address) popup
 * is to be destroyed or not.
 * 
 * It should be destroyed when neither the popup nor the feature
 * got the focus.
 */
var popupFocus = {feature: null,    //reference to the feature 
                  popup: null,      //reference to the popup
                  fFocus: false,    //feature got focus
                  pFocus: false,    //popup got focus
                  
                  /**
                   * Function to destroy popup.
                   * 
                   * The destruction can be delayed, which avoids flickering
                   * of the popup under some circumstances.
                   * 
                   * delayed: boolean   Is destruction delayed? true|false
                   */
                  destroy: function(delayed) {
                    if (popupFocus.popup && !popupFocus.fFocus && !popupFocus.pFocus && !delayed) {
                        setTimeout("popupFocus.destroy(true);", 100);
                    } else if (popupFocus.popup && !popupFocus.fFocus && !popupFocus.pFocus && delayed) {
                       map.removePopup(popupFocus.popup);
                       popupFocus.popup.destroy();
                       popupFocus.popup = null;
                       popupFocus.feature.popup = null;
                       popupFocus.feature = null;
                    }
                  }
};


   //////////////////
  //    GLOBAL    //
 //   FUNCTIONS  //
//////////////////

/**
 * Initialises the map.
 */
function init_map()
{

    var nav =  new OpenLayers.Control.Navigation({zoomWheelEnabled:true,handleRightClicks:true});
    /**
     * If you want to limited how far the map
     * can zoom out, increase the reduceZoomLevelsBy
     */
    map = new OpenLayers.Map(
            'openlayers_map',
            {   
                projection: new OpenLayers.Projection("EPSG:900913"),
                displayProjection: proj_4326,
                units: "m",
                maxResolution: 78271.51695, //156543.0339, //0.703125,
                maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34), 
                numZoomLevels: 18,     
                controls: [
                           new OpenLayers.Control.Scale(),
                           new OpenLayers.Control.PanZoomBar(),
                           nav
                           ],
                           buffer: 1
            }
    );
    //when you go to production, make getdevmap getmap
    var wmsc = [
                "http://wmsc1.terrapages.net/getpngmap?",
                "http://wmsc2.terrapages.net/getpngmap?",
                "http://wmsc3.terrapages.net/getpngmap?",
                "http://wmsc4.terrapages.net/getpngmap?",
                 
                "http://wmsc5.terrapages.net/getpngmap?",
                "http://wmsc6.terrapages.net/getpngmap?",
                "http://wmsc7.terrapages.net/getpngmap?"

                ];
    
    var tpstreetAttribution = "<a href=\"http://terrapages.com\" target=\"_blank\"><img src=\"images/poweredbytp.png\" alt=\"Powered by Terrapages & PSMA\" title=\"Powered by Terrapages & PSMA\" /></a><br />\n";
    var terrapagesStreetLayer = new OpenLayers.Layer.WMS( "TerraPages Street Map",wmsc, {layers: 'StreetMercator', format: 'image/png' }, 
            {sphericalMercator: true, wrapDateLine: true, attribution: tpstreetAttribution, transitionEffect: 'resize'});//{buffer: 0, isBaseLayer: true,wrapDateLine: true} );
    map.addLayer(terrapagesStreetLayer);
 
    var osmAttribution  = "<a href=\"http://openstreetmap.org\" target=\"_blank\"><img src=\"images/osm_logo.png\" alt=\"Powered by OpenStreetMap\" title=\"Powered by OpenStreetMap\" /></a><br />\n";
        //osmAttribution += "Powered by <a href=\"http://openstreetmap.org\" target=\"_blank\">OpenStreetMap</a>\n";
        
    osmLayer = new OpenLayers.Layer.WMS( "Open Street Map", wmsc, {layers: 'osm', format: 'image/png' }, {sphericalMercator: true, wrapDateLine: true, attribution: osmAttribution, transitionEffect: 'resize'});
    map.addLayer(osmLayer);
    
    var lswitch = new OpenLayers.Control.LayerSwitcher();
    map.addControl(lswitch);
    lswitch.div.style.top = "50px";

    //add the keyhole overview map
    tpsl = terrapagesStreetLayer.clone();
    tpsl.transitionEffect = null;
    osml = osmLayer.clone();
    osml.transitionEffect = null;
    var overview = new OpenLayers.Control.OverviewMap(
            {
                layers: [tpsl, osml],
                mapOptions: {maxResolution: 156543.0339} //0.703125,}
            }
    );
	
	//switching between the base layers in the keyhole-map.
    map.events.register("changebaselayer", overview, function(evt) {
                                                        if (this.layers.length > 0) {
                                                            if (OpenLayers.Util.indexOf(this.layers, this.ovmap.baseLayer) === 0) {
                                                                this.ovmap.setBaseLayer(this.layers[1]); 
                                                            } else {
                                                                this.ovmap.setBaseLayer(this.layers[0]);
                                                            }
                                                            this.update();
                                                        }});
    map.addControl(overview);
    
    // Remove the default Attribution Control, so we can recreate it
    oldAttributionControl = map.getControlsByClass("OpenLayers.Control.Attribution")[0];
    if (oldAttributionControl) {
        map.removeControl(oldAttributionControl);
    }

    attributionControl = new OpenLayers.Control.Attribution({displayClass: "customControlAttribution"});
    map.addControl(attributionControl);
    
    map.events.register("movestart",null,hideBuiltURL); //hides url frame when map changes
    overview.maximizeControl();
    $("#openlayers_map").bind("contextmenu",mapRightClickEvent);

    OpenLayers.ProxyHost = "proxy.jsp?url=";  
    
	//creating overlay-layers
    var georss = getGeoRSSLayer();
    georss.visibility = true;
    map.addLayer(georss);
    
    var markers = createMarkersLayer();
    map.addLayer(markers);
    
    var dirMarker = getDirectionsMarkersLayer();
    map.addLayer(dirMarker);
    
    var dirVector = createDirectionsVectorLayer();
    map.addLayer(dirVector);
    
    var layers = new Array(georss, dirMarker, dirVector, markers);
    
    selectControl = new OpenLayers.Control.HoverClickSelect(layers,
                    {onSelectHover: onFeatureSelectHover, onUnselect: onFeatureUnselect, onSelectClick: onFeatureSelectClick}); 
    map.addControl(selectControl); 
    selectControl.activate(); 

    return map;
}

/**
 * Sets the map label.
 * 
 * I.e. displays the given string in the top right
 * corner of the map.
 */
function setMaplabel(labelString, onClickFunc)
{
    mapLabel = labelString;
    if(mapLabel && mapLabel!="")
    {
        $("#mapLabelFrame").html(mapLabel).css({visibility: "visible"}).click(onClickFunc);
    }
    else
    {
        $("#mapLabelFrame").css({visibility: "hidden"});
    }

}

/**
 * Functions triggers when the mouse is right licked over the map
 * Creates a popup for making directions requests directly to the map
 *
 * @param evt
 * @return
 */
function mapRightClickEvent(evt)
{
    evt.preventDefault();

    if(ajaxCallInProgress)
    {
        alert("Freemaps is currently finding your directions. Please wait before requesting more directions");
    }
    else
    {

        var coord = getLonLat($(this),evt.pageX,evt.pageY);
        var coordT = getLonLat($(this),evt.pageX,evt.pageY).transform(map.getProjectionObject(), proj_4326);
        var popUpHtml = "<b>Directions Options:</b><br/><hr/>"
            +"<a href='#' onclick='javascript:setStartCoord("+ coordT.lon +"," + coordT.lat + ")'>From here</a>"
            +"<a href='#' onclick='javascript:setEndCoord("+ coordT.lon +"," + coordT.lat + ")'>To here</a>";

        /*
		it is possible to hit this event again before the "mouse leave" event triggers,
		so before another popup is created, remove and destroy the all popups.
         */
        for(index=map.popups.length-1;index>=0;index--)
        {
            popup = map.popups[index]
                               map.removePopup(popup);
            popup.destroy();
        }

        rightClickPopUp = new OpenLayers.Popup.FramedCloud("rightClickPopup",coord,new OpenLayers.Size(150,75),popUpHtml);
        rightClickPopUp.setBorder(1);
        rightClickPopUp.autoSize = false;

        map.addPopup(rightClickPopUp);

        $("#rightClickPopup").bind("mouseleave",function (evt) {

            rightClickPopUp.hide();
            rightClickPopUp.destroy();

            return false;
        });
    }

    return false;
}

/**
 * Calculates geographic position of the given target. 
 * 
 * @param target
 * @param pageX
 * @param pageY
 * @return
 */
function getLonLat(target,pageX,pageY)
{
    var x_percent = (pageX - target.offset().left) / target.width();
    var y_percent = (pageY - target.offset().top)  / target.height();
    bbox = map.getExtent();
    var lon = bbox.getWidth() * x_percent + bbox.left;
    var lat = bbox.top - bbox.getHeight() * y_percent;
    return new OpenLayers.LonLat(lon, lat);
}

/**
 * Generates URL, which is used for allowing the user to link to this map.
 */
function buildURL(){
    var location = document.location.protocol + "//" + document.location.host + document.location.pathname + "?";
    var center = map.getCenter().transform(map.getProjectionObject(), proj_4326);
    var query = "lon=" + center.lon + "&lat=" + center.lat + "&zoom=" + map.getZoom();
	
    //add markers to the url
    if(markersCoordinates && markersCoordinates!=""){
        query += "&marker=" + markersCoordinates;
        if(markersAddresses && markersAddresses!="")
        {
            query += "&address=" + markersAddresses;
        }
    }
    if(mapLabel && mapLabel!="")
    {
        query += "&label=" + mapLabel
    }
    //add directions to the url
    if(startLat && startLon)
    {
        query += "&startLat=" + startLat + "&startLon=" + startLon; 
    }
    if(endLat && endLon)
    {
        query += "&endLat=" + endLat + "&endLon=" + endLon;
    }
    url = location + query;
    $("#getURLTabDiv").hide();
    $("#getUrlHref").attr({value: url});
    $("#getUrlFrame").animate({left:"0px"});
}

/**
 * Hides Link-URL.
 */
function hideBuiltURL()
{
    $("#getUrlFrame").animate({left:'-420px'},
            {
        complete:function(){
        $("#getURLTabDiv").show();
    },duration:500
            });
}

/**
 * Shows Link-URL-tab.
 */
function showGetUrlTab()
{
    $("#getURLTabDiv").stop().animate({left:"0px"},200);
}

/**
 * Hides Link-URL-tab.
 */
function hideGetUrlTab()
{
    $("#getURLTabDiv").stop().animate({left:"-19px"},200);
}

/**
* Checks that the coordinates are valid floats
* @param {Object} lat
* @param {Object} lon
*/
function validCoords(lat, lon) {
   if (isNaN(parseFloat(lat)) || lat == 0 ||
           isNaN(parseFloat(lon)) || lon == 0) {
       return false;
   }
   return true;
}


//CALLBACK FUNCTIONS

/**
 * Called when (directory-feature-)popup gets closed.
 * 
 * Unselects the feature.
 */
function onPopupClose(evt) {
    
    //selectControl.unselect(selectedFeature);
    this.hide();
    unhighlightFeature(this.feature);
    OpenLayers.Event.stop(evt);
}

function unhighlightFeature(feature) {
    if (feature.popup) {
        map.removePopup(feature.popup);
        feature.popup.destroy();
        feature.popup = null;
    }
    
    feature.style.pointRadius = feature.style.pointRadius - 5;
    feature.layer.drawFeature(feature, feature.style);
    if(selectControl.highLightedFeature == feature) {
        selectControl.highLightedFeature = null;
    }
}

/**
 * Executed when feature gets selected by clicking on it.
 * 
 * Has only an effect, if the feature is a directory feature.
 * In this case it creates the popup.
 * 
 * @param feature
 * @return
 */
function onFeatureSelectClick(feature) {
    selectedFeature = feature;
    
    if(this.highLightedFeature == feature) {
        this.highLightedFeature = null;
    }
    
    if(feature.type == 'directory') {
        
        var dirTitle = rssMetadata.directories[feature.data.dir].title;
        var dirImg = rssMetadata.directories[feature.data.dir].img;
        
        var popup = new OpenLayers.Popup.FramedCloud("chicken",
                feature.geometry.getBounds().getCenterLonLat(), null,
                "<div id='dirBubble'><table><tbody><tr><td><img src='"+feature.data.link+
                "'/></td><td style='color: #0066cc; font-weight: bold;'>"+ feature.data.title +
                "</td></tr><td><img src='"+ dirImg +"' title='"+dirTitle+"'/></td><td>"+ feature.data.description +
                "</td></tr></tbody></table></div>",
                null, true, onPopupClose);
        feature.popup = popup;
        popup.feature = feature;
        
        for (p in map.popups) {
            if (map.popups[p].feature) {
                unhighlightFeature(map.popups[p].feature);  
            }
        }
        
        map.addPopup(popup, true);
    } 
}


/**
 * Executed when feature gets selected by hovering over it.
 * 
 * Has only an effect, if the feature is a location feature.
 * In this case it creates the popup.
 * 
 * @param feature
 * @return
 */
function onFeatureSelectHover(feature) {
    selectedFeature = feature;
    
    if (feature.type == 'address') {
        popup = new OpenLayers.Popup.FramedCloud("chicken",
                feature.geometry.getBounds().getCenterLonLat(), null,
                feature.data.address,
                null, false, onPopupClose);
        
        popup.events.on({
            "mousedown": this.onmousedown,
            "mousemove": this.onmousemove,
            "mouseup": this.onmouseup,
            "click": this.onclick,
            "mouseout": function(evt) { 
                            popupFocus.pFocus = false;
                            popupFocus.destroy(false);
        },
            "mouseover": function(evt) {popupFocus.pFocus = true;},
            "dblclick": this.ondblclick,
            scope: this
        });
        
        feature.popup = popup;
        
        popupFocus.feature = feature;
        popupFocus.popup = popup;
        popupFocus.fFocus = true;
        
        for (p in map.popups) {
            if (map.popups[p].feature) {
                unhighlightFeature(map.popups[p].feature);  
            }
        }
        
        map.addPopup(popup, true);
    } else if (feature.type == 'directory' && feature.popup == null) {
        if(this.highLightedFeature) {
            this.highLightedFeature.style.pointRadius = feature.style.pointRadius - 5;
            this.highLightedFeature.layer.drawFeature(feature, feature.style);
            this.highLightedFeature = null;
        }
        feature.style.pointRadius = feature.style.pointRadius + 5;
        feature.layer.drawFeature(feature, feature.style);
        this.highLightedFeature = feature;
    }
}

/**
 * Executed when feature gets unselected.
 * Destroys the popup.
 * 
 * @param feature
 * @param hovering
 * @return
 */
function onFeatureUnselect(feature, hovering) {
    if (feature.popup) {
        if (hovering && feature.type == 'address' && !feature.popup.hasFocus) {
            popupFocus.fFocus = false;
            popupFocus.destroy(false);
           
        } else if (!hovering && feature.type == 'directory') {
            map.removePopup(feature.popup);
            feature.popup.destroy();
            feature.popup = null;
            
            feature.style.pointRadius = feature.style.pointRadius - 5;
            feature.layer.drawFeature(feature, feature.style);
            if(this.highLightedFeature == feature) {
                this.highLightedFeature = null;
            }
            
        }
    } else if (feature.type == 'directory') {
        feature.style.pointRadius = feature.style.pointRadius - 5;
        feature.layer.drawFeature(feature, feature.style);
        
        this.highLightedFeature = null;
    }
}

  ///////////////////////////////////////////////////////////
 ////     Functions dealing with the GeoRSSLayer        ////
///////////////////////////////////////////////////////////
/**
 * Creates the GeoRSS layer.
 */
function getGeoRSSLayer() {
        
    geoRSSlayer = new OpenLayers.Layer.GeoRSS("http://terrapages.net/AtomServices.php", directoryParams);
    geoRSSlayer.displayInLayerSwitcher = false;
   
    return geoRSSlayer;
    
}


/**
 * Callback function for the directory HL.
 * 
 * Sets global variables for coordinates.
 * 
 * @param lon
 * @param lat
 * @return
 */
function directoryHLCallback(lon,lat)
{
    directoryLat = lat;
    directoryLon = lon;
    directoryAddress = this.current;
}


/**
 * Executed when user starts directory search.
 * 
 * Resets the search parameters, enables GeoRSS
 * layer and switches to the home-tab.
 */
function directorySubmit() {
 
        var dir = "";
        
        var iconDiv = $("#directoryIconsImg")[0];
        var icons = iconDiv.getElementsByTagName("img");
                
        while(icons.length > 0) {
            iconDiv.removeChild(icons[0]);
        }
        
        for (var i in rssMetadata.directories) {
            var boxId ="#dirCheckBox"+rssMetadata.directories[i].id;
            var box = $(boxId)[0];
        
            if(box.checked) {
                if(dir != "") {
                    dir += ",";
                }
                dir += box.value;
                
                $("#directoryIconsImg").append('<img src="'+rssMetadata.directories[i].img+'" title="'+
                        rssMetadata.directories[i].title+'" />');
            }
        }
                
        if(dir != "") {
            directoryParams = { 
                                dir: dir,
                                max: 99,
                                name: null,
                                cat: null
                              };
            
            geoRSSlayer.params = directoryParams;
            geoRSSlayer.paramChange = true;
            geoRSSlayer.visibility = true; 
        }  else {
            directoryClear();
        }
        
        $("#tabs").show().tabs('select', 0);
        geoRSSlayer.moveTo();
}

/**
 * Called when user clicks on the directory-clear button.
 * 
 * Removes all search results from the layer and disables it.
 * 
 * @return
 */
function directoryClear() {
    geoRSSlayer.removeFeatures(geoRSSlayer.features); 
    geoRSSlayer.visibility = false;
}

