var map;
var styleMap;
var loadingLayers = 0;

var municipalityLayer;
var overlayLayer;
var selectControl;
var selectedFeature;
var timer = {};
var config;

/*GOTCHAS:
* 
* $.getScript(config_file);//safari 2 and earlier can't handle this synchronously
* WORKAROUND: when loading one script after the other set a timer to allow the first one to load completely before the second call
* 
* selectControl.unselect(selectedFeature); removes the whole feature off the map => not good
* 
*/

function init() {
    //if (navigator.appName == "Microsoft Internet Explorer" && navigator.appVersion.indexOf("MSIE 8.0") > -1)
    //    alert("There are known compatibility issues with this website and Internet Explorer 8.\n\nWe are working hard to fix them, but in the mean time please enable 'Compatibility View'.");
    if ($.browser.msie) { $('#footer').empty(); } // hide footer in IE for now. TODO: fix
    if (!config || !config.organisation) {
        if (timer.config) {
            clearTimeout(timer.config);
            clearInterval(timer.config);
        }
        timer.config = window.setTimeout(init, 200);
        return;
    }

    if (typeof (customInit) == "function") {
        customInit();
    }

    initPresentation();
    initSelectionLists();
    initMap();
}

function initConfig() {
    if (!OpenLayers) {
        if (timer.openlayers) {
            clearTimeout(timer.openlayers);
            clearInterval(timer.openlayers);
        }
        timer.openlayers = window.setTimeout(initConfig, 200);
        return;
    }

    var app = basename(dirname(window.location + ''));
	
    var council = location.hostname.split(".")[0];
    //this is for testing on localhost 
    //if (council.substr(0, 9).toLowerCase() == 'localhost') {
        //council = 'corangamite';
    //    council = 'mitchell';
    //}
    if (council == 'test' || council == 'dev' || location.hostname.length < 18 || location.hostname.substr(location.hostname.length - 18, 18) != "sweepingplains.com") {
        $('#selectconfig').css('display', 'block');
        openConfigPopup();
        return;
    }

    loadConfig(council);
}

function loadConfig(council) {
    $('#selectconfig').css('display', 'none');
    
    config_file = "config/config.js";

    //the files need to be downloaded sequentially
    $.getScript(config_file, function() {
        config_file = "config/config-" + council.toLowerCase() + ".js";
        $.getScript(config_file, function() { init(); });
    });
}

function setTargetConfig(configName) {
    loadConfig(configName);
}

function openConfigPopup() {
    var w = window.open('selectconfig.html', 'config_popup', 'width=200,height=200,scrollbars=0,menubar=0,location=0,resizable=0,status=0');
    //w.focus();
}

function initPresentation() {
    $('#council_name').html(config.organisation.name);
    $('#subtitle').html(config.organisation.tagline + "<br /><br />map powered by<br /><a href='http://www.sweepingplains.com'>Sweeping Plains</a>");
    document.title = config.organisation.pagetitle;
    $('#banner img').attr('src', config.organisation.bannerfile)

    /*
    if (config.organisation.bannerfile & config.organisation.bannerfile != '') {    
    $('#banner img').attr('src', config.organisation.bannerfile);
    }
    else {
    $('#banner').empty();
    }
    */
}

function initSelectionLists() {
    fillSelectionList("basemap_select", config.basemap.layers);
    fillSelectionList("overlay_select", config.overlay.layers);
    fillSelectionList("goto_select", config.select["goto"]);
}

function setSelection(listName, value) {
    if ($('#' + listName).children().index($('option[value=' + value + ']')) != -1) {
        $('#' + listName).attr('value', value); //if the value exists in the list select it
    }
}

function fillSelectionList(listName, config) {
    for (var name in config) {
        // if the value is a string: use it, otherwise use the name as the value
        var value = (typeof (config[name]) == "string") ? config[name] : name;

        $('#' + listName).append("<option value='" + value + "'>" + name + "</option>");
        //TODO: based on SWEEP-5 I think that the above 'name' should be 'value', but config.select["goto"].value='144.968,-37.4835,'
    }
}

function initMap() {

    Proj4js.libPath = "javascript/openlayers-addins/proj4js/lib/";

    OpenLayers.ProxyHost = "cgi/proxy.py?url="; // for XHR
    OpenLayers.DOTS_PER_INCH = 96;

    // avoid pink tiles
    OpenLayers.IMAGE_RELOAD_ATTEMPTS = 3;
    OpenLayers.Util.onImageLoadErrorColor = "transparent";

    var epsg4326 = new OpenLayers.Projection("EPSG:4326");
    var epsg900913 = new OpenLayers.Projection("EPSG:900913");

    // Extent to which zoomworld icon zooms to
    var maxExtent = OpenLayers.Bounds.fromString(config.extent.initial).transform(epsg4326, epsg900913);

    map = new OpenLayers.Map('map', {
        projection: epsg900913,
        //projection: epsg4326,
        displayProjection: epsg4326,
        units: "m",
        numZoomLevels: 25,
        //maxResolution: 9783.93961875,
        maxResolution: 156543.0339,
        //maxResolution: 0.703125,
        //numZoomLevels: 20,
        maxExtent: maxExtent
    });

    // Add base and overlay layers
    initBaseLayers();
    initOverlayBaseLayers();
    initOverlayLayers();

    //map.addControl(new OpenLayers.Control.KeyboardDefaults());
    //map.addControl(new OpenLayers.Control.LayerSwitcher());
    //map.addControl(new OpenLayers.Control.Permalink());
    map.addControl(new OpenLayers.Control.MousePosition());
    //map.addControl(new OpenLayers.Control.PanZoomBar());
    scalebar = new OpenLayers.Control.ScaleBar();
    map.addControl(scalebar);



    var clickControl = new OpenLayers.Control.Click();
    map.addControl(clickControl);
    clickControl.activate();

    // Zoom to initial extent, unless permalink is used
    if (!map.getCenter()) {
        browseTo(config.extent.initial);
    } else {
        //console.info("permalink");
    }

    initInfoPanel();
}

function initBaseLayers() {
    var defaultBaseLayer;
    for (var name in config.basemap.layers) {
        if (config.basemap.layers[name]["default"]) {// only load the layers as they are needed
            doSelect('basemap', name);
            setSelection('basemap_select', name);
        }
    }

    // Set the default baseLayer unless permalink is used
    /*if (defaultBaseLayer && !map.getCenter()) {
    map.setBaseLayer(defaultBaseLayer);
    }*/
}

/*
* Returns the layer with the given name.
* If such a layer does not exist it loads it. 
*/
function getBaseLayer(name) {
    var layer = map.getLayersByName(name)[0];
    if (!layer) {
        var basemap = config.basemap.layers[name];

        switch (basemap.type) {
            case OpenLayers.Layer.WMS:
                layer = new basemap.type(name, basemap.url, basemap.params, basemap.options);
                break;
            case OpenLayers.Layer.OSM.Osmarender:
                layer = new basemap.type(name);
                break;
            case OpenLayers.Layer.TMS:
                layer = new basemap.type(name, basemap.url, basemap.options);
                break;
            default:
                layer = new basemap.type(name, basemap.options);
        }
        layer.events.register('loadstart', layer, increaseLoadingLayers);
        layer.events.register('loadend', layer, decreaseLoadingLayers);
        map.addLayer(layer);
    }
    return layer;
}

function initOverlayBaseLayers() {
    for (var name in config.overlay.baselayers) {
        var overlay = config.overlay.baselayers[name];
        var layer = new OpenLayers.Layer.GML(name, overlay.url, config.overlay.options);
        layer.events.register('loadstart', layer, increaseLoadingLayers);
        layer.events.register('loadend', layer, decreaseLoadingLayers);
        map.addLayer(layer);
    }
}

function initOverlayLayers() {
    for (var name in config.overlay.layers) {
        var overlay = config.overlay.layers[name];
        var options = OpenLayers.Util.extend({ visibility: false }, config.overlay.options);
        if (overlay.options) {
            OpenLayers.Util.extend(options, overlay.options);
        }
        var additionaloptions = overlay.additionaloptions;
        var layer;
        switch (overlay.datatype) {
            case 'WMS':
                layer = new OpenLayers.Layer.WMS(name, overlay.url, options, additionaloptions);
                layer.visibility = false;
                addSelectFeatureControl(layer);
                break;
            case 'WFS':
                layer = new OpenLayers.Layer.WFS(name, overlay.url, options);
                addSelectFeatureControl(layer);
                break;
            case 'GeoJSON':
                options = OpenLayers.Util.extend({ format: OpenLayers.Format.GeoJSON }, options);
                layer = new OpenLayers.Layer.Vector(name, options);
                addSelectFeatureControl(layer);
                break;
            case 'MapGuide':
                var sessionId = createMapGuideSession(overlay.sessionUrl);
                var params = OpenLayers.Util.extend({ session: sessionId }, overlay.params);
                layer = new OpenLayers.Layer.MapGuide(name, overlay.url, params, options);
                layer.getFeatureFromEvent = function(evt) {
                    return null;
                }
                break;
            default: //KML
                layer = new OpenLayers.Layer.GML(name, overlay.url, options);
                addSelectFeatureControl(layer);
                break;
        }
        activateLayer(layer);
    }
}

function createMapGuideSession(url) {
    //create mapguide session
    var xmlHttp;
    try {
        xmlHttp = new XMLHttpRequest();
    }
    catch (e) {
        try {
            xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
        }
        catch (e) {
            alert('MapGuide layer session creation failed.  Your browser does not support XMLHttp.');
            return null;
        }
    }
    xmlHttp.open("GET", OpenLayers.ProxyHost + encodeURIComponent(url), false);
    xmlHttp.send(null);
    var sessionId;
    //sessionId= xmlHttp.responseText;  //get session id text

    //workaround - find session id text in web page.
    var t = xmlHttp.responseText
    var pos = t.indexOf('sessionId=\'') + 11;
    sessionId = t.substring(pos, t.indexOf('\'', pos));

    if (sessionId == null) {
        alert("MapGuide layer session creation failed.");
        return null;
    }
    return sessionId;
}

function activateLayer(layer) {
    layer.events.register('loadstart', layer, increaseLoadingLayers);
    layer.events.register('loadend', layer, decreaseLoadingLayers);
    map.addLayer(layer);
}

function addSelectFeatureControl(layer) {
    selectControl = new OpenLayers.Control.SelectFeature(layer, {
        onSelect: onFeatureSelect,
        onUnselect: onFeatureUnselect
    });

    selectControl.handlers.feature.stopDown = false; // fix problem where map dragging gets disabled
    selectControl.handlers.feature.stopUp = false; // fix problem where map dragging gets disabled

    map.addControl(selectControl);
    selectControl.activate();
}


OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, {
    defaultHandlerOptions: {
        'single': true,
        'double': false,
        'pixelTolerance': 0,
        'stopSingle': false,
        'stopDouble': false
    },

    initialize: function(options) {
        this.handlerOptions = OpenLayers.Util.extend({}, this.defaultHandlerOptions);
        OpenLayers.Control.prototype.initialize.apply(this, arguments);
        this.handler = new OpenLayers.Handler.Click(this, { 'click': this.trigger }, this.handlerOptions);
    },

    trigger: function(e) {
        for (var name in config.overlay.layers) {
            var layer = map.getLayersByName(name)[0];
            if (layer && layer.visibility) {
                if (layer.clickTrigger)
                    layer.clickTrigger(e);
            }
        }
    }
});


function selectOverlayLayer(selectName) {
    // remove any old layers first
    for (var name in config.overlay.layers) {
        var layer = map.getLayersByName(name)[0];
        if (layer) {
            layer.setVisibility(name == selectName);
            moveLayerToTop(layer); // make features selectable
            if (name == selectName)
                currentLayer = layer;
        }
    }
    return true;
}

function doSelect(type, value) {
    switch (type) {
        case "basemap":
            switchBaseLayer(value);
            break;
        case "overlay":
            selectOverlayLayer(value);
            setOverlayDescription();
            break;
        case "goto":
            browseTo(value);

            // Check if selected feature is still visible. If not: unselect
            /* Commented out since it is broken
            var feature = overlayLayer ? overlayLayer.selectedFeatures[0] : null;
            if (feature && !feature.onScreen()) {
            selectControl.unselect(feature);  // unselect feature 
            setInfoDiv(); // clear info div as well
            break;
            }
            */
            break;
        default:
    }
}

function setOverlayDescription() {
    var value = $('#overlay_select').val();

    if (!config.overlay.layers[value]) {
        InfoPanel.revertToLevel(0);
        return;
    }

    //    if (config.overlay.layers[value].descriptionurl) {
    //        $('#results').empty().load(config.overlay.layers[value].descriptionurl, {}, function() {
    //            $('#results').prepend('<p/><strong>' + config.overlay.layers[value].featuredescription + '</strong><p/>');
    //        });
    //    }
    //    else {
    //        $('#results').prepend('<p/><strong>' + config.overlay.layers[value].featuredescription + '</strong><p/>');
    //    }

    var feature;
    var layer = map.getLayersByName($('#overlay_select').val())[0];
    if (layer.selectedFeatures && layer.selectedFeatures.length > 0) {
        feature = layer.selectedFeatures[0];
    }
    
    InfoPanel.addItem(new InfoPanelItem(
            1,
            config.overlay.layers[value].descriptionurl ? config.overlay.layers[value].descriptionurl : null,
            config.overlay.layers[value].featuredescription ? config.overlay.layers[value].featuredescription : value
            ));
            
    //if (selectedFeature != null && selectedFeature.onScreen(true) && selectedFeature.layer.name == value) {
    if (feature != null) {       
        onFeatureSelect(feature);
    }
}

function onFeatureSelect(feature) {
    selectedFeature = feature;
    //var featureName = feature.attributes.name;
    //var featureDescription = feature.attributes.description || feature.attributes.name;

    $('#clearFeature').attr('class', 'activelink');

    //setInfoDiv(featureDescription);
    //if (config.overlay.layers[$('#overlay_select').val()]) {
    //    $('#results').prepend('<p/><strong>' + config.overlay.layers[$('#overlay_select').val()].featuredescription + '</strong><p/>');
    //}

    var content;
    if (config.overlay.layers[$('#overlay_select').val()]) {
        content = '<strong>' + config.overlay.layers[$('#overlay_select').val()].featuredescription + '</strong><br />';
    } else {
        content = "<strong>" + feature.attributes.name + "</strong><br />"; //fallback where .featuredescription does not exists (should never happen)
    }
    content += feature.attributes.description;

    InfoPanel.addItem(new InfoPanelItem(2, null, content));
}

function onFeatureUnselect(feature) {
    InfoPanel.revertToLevel(1);
    selectedFeature = null;
    $('#clearFeature').attr('class', 'inactivelink');
}

function clearSelectedFeature() {
    if (selectedFeature) {
        $('#clearFeature').attr('class', 'inactivelink');

        map.getLayersByName($('#overlay_select').val())[0].drawFeature(selectedFeature, 'default');
        selectedFeature = null;
        InfoPanel.revertToLevel(1);     //setOverlayDescription();
        //map.getLayersByName($('#overlay_select').val())[0].redraw()
    }
}

function browseTo(lonLatString) {
    var lonLatArray = lonLatString.split(",");
    var startlon = parseFloat(lonLatArray[0]);
    var startlat = parseFloat(lonLatArray[1]);
    var endlon = parseFloat(lonLatArray[2]);
    var endlat = parseFloat(lonLatArray[3]);

    var zoomLevel = parseInt(lonLatArray[4]);

    if (startlon && startlat) {
        var bounds = new OpenLayers.Bounds(startlon, startlat, endlon, endlat);
        if (map.displayProjection && map.getProjectionObject()) {
            map.zoomToExtent(
                  bounds.transform(map.displayProjection, map.getProjectionObject())
                );
        }

        if (zoomLevel) {
            map.zoomTo(zoomLevel);
        }
    }
}

function setElementVisibility(element_id, visible) {
    visible ? $('#' + element_id).show() : $('#' + element_id).hide();
    return true;
}

function setControlsVisibility(visible) {
    setElementVisibility("controls", visible);
}

function setLegendVisibility(visible) {
    setElementVisibility("legend", visible);
}


function switchBaseLayer(layerName) {
    var epsg4326 = new OpenLayers.Projection("EPSG:4326");
    var epsg900913 = new OpenLayers.Projection("EPSG:900913");

    var layer = getBaseLayer(layerName);
    //var layer = map.getLayersByName(layerName)[0];
    if (layer) {
        var bounds;
        /*
        if (layerName == "Terrapages") {
        layer.projection = new OpenLayers.Projection('EPSG:4326');
        layer.maxExtent = new OpenLayers.Bounds(-180, -90, 180, 90);
        layer.units = 'degrees';
        //layer.maxResolution = 1.40625;
        layer.maxResolution = 0.703125;
        } else {
        layer.projection = new OpenLayers.Projection('EPSG:900913');
        //layer.displayProjection = new OpenLayers.Projection('EPSG:4326');
        layer.maxExtent = new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34);
        layer.units = 'm';
        layer.maxResolution = 156543.0339;
        }
        */
        map.setBaseLayer(layer);

        // Workaround a resize bug in Virtual Earth Layer. Needs a patched OpenLayers
        // that has this function in the VE layer.
        if (typeof (layer.onMapResize) == "function") {
            layer.onMapResize();
        }

        //var bounds = new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34);
        //map.zoomToExtent(bounds);
    }
}

// Move layer to the top, so its features can get selected
function moveLayerToTop(layer) {
    map.raiseLayer(layer, map.getNumLayers() - map.getLayerIndex(layer));
}

function searchAddress(addressString) {

    increaseLoadingLayers(); // start spinner

    $.getJSON("cgi/vmas.py", { address: addressString, ajax: 'true' }, function(featureCollection) {

        // Create Address Search layer if it doesn't exist yet
        layerName = "Address Search";
        var layer;
        if (map.getLayersByName("Address Search")[0]) {
            layer = map.getLayersByName("Address Search")[0];
            layer.destroyFeatures();
        } else {
            layer = new OpenLayers.Layer.Vector("Address Search", config.searchoverlay.options);
        }

        // Fill layer with address marker from VMAS service
        var format = new OpenLayers.Format.GeoJSON({
            externalProjection: new OpenLayers.Projection('EPSG:4326'), internalProjection: map.getProjectionObject()
        });
        var features = format.read(featureCollection);

        // When there are no features, present nice message that address has not been found
        // and exit the function
        if (!features) {
            //setInfoDiv("Sorry. Couldn't find that location.");
            InfoPanel.addItem(new InfoPanelItem(2, null, "Sorry. Couldn't find that location."));
            decreaseLoadingLayers(); // stop spinner
            return false;
        }

        // Add addressmarker to the Address Search layer
        layer.addFeatures(features);
        activateLayer(layer); // keep the select control to manually select the marker
        //moveLayerToTop(layer); // make features selectable

        var addressMarker = layer.features[0]; // there should be only one feature in this layer
        InfoPanel.addItem(new InfoPanelItem(2, null, "<strong>Search Successful</strong><br />" + addressMarker.attributes.description));
        
        //Paul Vella: Make search layer not selectable (by disabling following 3 lines)
        //addSelectFeatureControl(layer);
        //selectControl.unselectAll();   // deselect any previous address marker (needed for proper selecting)
        //selectControl.select(addressMarker); // now select the address marker

        // Zoom map to this zoom level (if not done already)
        if (map.zoom != 15) {
            map.zoomTo(15);
        }

        // Now pan map to (new) address. This results in a smooth scrolling of the 
        // map if the new address was already within visible viewport
        var lonLat = new OpenLayers.LonLat(addressMarker.geometry.x, addressMarker.geometry.y);
        map.panTo(lonLat);

        decreaseLoadingLayers(); // stop spinner
    })
}


function increaseLoadingLayers() {
    loadingLayers++;
    showSpinner();
}

function decreaseLoadingLayers() {
    loadingLayers--;
    if (loadingLayers < 1) {
        hideSpinner();
    }
}
function showSpinner() {
    $('#spinner').show();
    $('#status').html("loading " + loadingLayers + " layer" + ((loadingLayers == 1) ? '' : 's') + "...");
}

function hideSpinner() {
    $('#spinner').hide();
    $('#status').html('loaded');
}

//function setInfoDiv(message) {
//    if (!message) {
//        message = '<p>Click on a map symbol to view details</p>';
//    }
//    $("#results").html(message);
//}

function basename(path) {
    return path.replace(/.*\//, '');
}

function dirname(path) {
    return path.replace(/\/[^\/]*$/, '');
}

function spit(msg) {
    //console.log(msg); 
    $('#results').append(msg);
}


//info panel
function initInfoPanel() {
    InfoPanel.addItem(new InfoPanelItem(
        0,
        config.organisation.welcomepage,
        "Pick a map overlay from the Display list, then click on a map symbol to view details"
        ));
}

function InfoPanelItem(level, url, altText) {
    this.level = level;
    if (url != null)
        this.url = url;
    if (altText != null)
        this.text = altText;
}

var InfoPanel = new InfoPanelController();

function InfoPanelController() {
    var items = new Array();

    this.pushItem = function(item) {
        var currentTop = null;
        while (items.length > 0) {
            currentTop = items.pop()
            if (currentTop.level < item.level) {
                items.push(currentTop);
                break;
            }
            if (currentTop.level == item.level) {
                break;
            }
        }
        items.push(item);
        debugStack();
    };

    this.addItem = function(item) {
        this.pushItem(item);
        displayItem(item);
    };

    this.revertToLevel = function(level) {
        var item = items.pop();
        while (true) {
            if (item.level == level) {
                items.push(item);
                displayItem(item);
                debugStack();
                return;
            }
            if (items.length == 0) {
                displayItem(new InfoPanelItem(0, null, "Error - could not find info panel item to revert to with level " + level));
                debugStack();
                return;
            }
            item = items.pop();
        }
    };

    function displayItem(item) {
        if (item.url) {
            //$('#results').empty().load(item.url);
            ajaxRequest = $.ajax({
                async: false,
                type: "GET",
                url: item.url,
                success: function(data, msg) {
                    $('#results').empty().prepend(data);
                },
                error: function(XMLHttpRequest, textStatus, errorThrown) {
                    $('#results').empty();
                    //if (item.text)
                    //    $('#results').empty().prepend('<p>' + item.text + '</p>');
                    //else
                    //    $('#results').empty().prepend('<p style="color: red">Failed getting url: \'' + item.url + '\'<br /><br /><strong>' + textStatus + '</strong></p>');
                }
            });
        } else if (item.text) {
            $('#results').empty().prepend('<p>' + item.text + '</p>');
        }

        //        if (item.level < 2)
        //            $("#clearFeature").hide();
        //        else
        //            $("#clearFeature").show();
    };

    function debugStack() {
        if (window.loadFirebugConsole) {
            window.loadFirebugConsole();

            var msg = "";
            for (var i = 0; i < items.length; i++) {
                msg += i + ":" + items[i].level + "  ";
            }
            window.console.log("InfoPanel stack: " + msg);
        }
    }
}