﻿/** 
* Global variable: viskort
* (<VisKort.Application>) Holds the application 
*/
var viskort;

VisKort = {};

/** 
* Class: VisKort.Application
*
* The application holds the map, all the layers and all the components.
* viskort is the main entry to all javascript in the solution.
*
* Only one instance of this class should be created. All access to underlaying 
* objects should be through this instance.
*
* The constructor of the class initializes the map.
*
*/
VisKort.Application = OpenLayers.Class({

    /** 
    * Property: map
    * {<VisStedet.Map>} The OpenLayers/VisStedet map.
    */
    map: null,

    /** 
    * Property: infoBox
    * {<OpenLayers.Control.InfoBox>} The reference to the infobox to show popup balloon information
    */
    infoBox: null,

    /** 
    * Property: active_theme
    * {<String>} Name of the active layout theme (ASP.NET)
    */
    active_theme: null,

    /**
    * Property: zoomlevels
    * {<Number>} Number of zoom levels available on the map
    */
    zoomlevels: 12,

    /**
    * Property: resolutions
    * {<Array of numbers>} Resolutions of the zoom levels
    */
    resolutions: new Array(0.8, 1.6, 3.2, 6.4, 12.8, 25.6, 51.2, 102.4, 204.8, 409.6, 819.2, 1638.4),

    /**
    * Property: resolutions
    * {OpenLayers.Bounds} Bounds for max extend
    */
    maxExt: new OpenLayers.Bounds(120000, 5661139.2, 958860.8, 6500000),
    // bottom: 6500000 - 2*(6919430,4-5241708,8)/4 = 5661139.2
    // right: 120000 + 2*(1378291,2-(-299430,4))/4 = 958860.8
    //
    // TODO: Gør det konfigurerbart om Tile-kort skal anvendes.

    /**
    * Property: resolutions
    * {OpenLayers.Bounds} Bounds for start extend
    */
    startExt: new OpenLayers.Bounds(420000, 6025000, 905000, 6450000),

    /**
    * Property: components
    * {Object} Hashtable of available components <VisKort.Component>. 
    */
    components: {},

    /**
    * Property: activeComponentName
    * {String} The key of the active component in the components-hashtable. 
    */
    activeComponentName: null,

    /**
    * Property: pagemode
    * {String} Can either be "iframe", "popup" or "print". 
    */
    pagemode: "popup",

    /**
    * Property: clientLayersKml
    * {String} Holds original Kml strings sent from a parent window. 
    */
    clientLayersKml: "popup",

    /**
    * Property: baseQuerystring
    * (String) constant parameters
    */
    baseQuerystring: null,

    /**
    * Property: internalprojection
    * (String) holds the projection code of the map
    */
    internalprojection: "EPSG:25832",

    /**
    * Constructor: VisKort.Application
    * Initializes the map, the map controls, and the layers
    *
    * Parameters:
    * mapDivId - <String> The id of the HTML DIV tag to hold the map
    * activeTheme - <String>
    * mapConfig - <Object> The relevant subset of the MapConfig.xml file content (all layers needed in the current request). JSON formatted.
    * pagemode - <String>
    * baseQuerystring - <String> contains the querystring
    */
    initialize: function(mapDivId, activeTheme, mapConfig, pagemode, baseQuerystring, parentMapDivHeight, parentMapDivWidth, searchResultLayerId, infoBoxSettings) {
        if (pagemode)
            this.pagemode = pagemode;

        // Setup proj4j projections
        Proj4js.defs["EPSG:25832"] = "+proj=utm +zone=32 +ellps=GRS80 +units=m +no_defs ";
        Proj4js.defs["EPSG:4326"] = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ";

        // Setup proxy location and active theme
        OpenLayers.ProxyHost = "Proxy.ashx?url=";

        OpenLayers.ImgPath = '/VisKort/App_Themes/' + activeTheme + '/Images/OpenLayers/';
        this.active_theme = activeTheme;

        this.baseQuerystring = baseQuerystring;

        // Set InfoBox area (covers Denmark) 
        var infoBoxRing = new OpenLayers.Geometry.LinearRing();
        infoBoxRing.addComponent(new OpenLayers.Geometry.Point(440000, 6040000)); // SW
        infoBoxRing.addComponent(new OpenLayers.Geometry.Point(350000, 6330000)); // W
        infoBoxRing.addComponent(new OpenLayers.Geometry.Point(610000, 6430000)); // N
        infoBoxRing.addComponent(new OpenLayers.Geometry.Point(750000, 6220000)); // NE 1
        infoBoxRing.addComponent(new OpenLayers.Geometry.Point(750000, 6140000)); // NE 2
        infoBoxRing.addComponent(new OpenLayers.Geometry.Point(900000, 6160000)); // E
        infoBoxRing.addComponent(new OpenLayers.Geometry.Point(900000, 6100000)); // SE
        infoBoxRing.addComponent(new OpenLayers.Geometry.Point(710000, 6040000)); // S
        var infoBoxArea = new OpenLayers.Geometry.Polygon(infoBoxRing);

        // Initialize controls
        var layerSwitcher = new OpenLayers.Control.StyledLayerSwitcher(mapConfig);
        var scaleLineOptions = { bottomOutUnits: "", bottomInUnits: "" };
        var scaleLine = new OpenLayers.Control.ScaleLineBugFixed(scaleLineOptions);
        var navigation = new OpenLayers.Control.ModifiedNavigator();
        var panZoomBar = new OpenLayers.Control.PanZoomBarBugFix();
        var shortcuts = new OpenLayers.Control.ModifiedKeyboard();
        var overviewMapOptions = { projection: this.internalprojection, units: 'm', maxExtent: this.maxExt };
        var overviewMap = new OpenLayers.Control.StyledOverviewMap({ minRatio: 32, maxRatio: 64, mapOptions: overviewMapOptions });
        var locationsearch = new OpenLayers.Control.LocationSearch();
        var searchlist = new OpenLayers.Control.SearchList();
        var infoBoxLinkGenerators = null;
        var mousecontrol = new OpenLayers.Control.MousePosition();

        if (pagemode == "popup")
            infoBoxLinkGenerators = [new VisKort.Utils.LinkGenerator.Route(false),
                                     new VisKort.Utils.LinkGenerator.Rejseplanen("TwoLinks", false)];
        else
            infoBoxLinkGenerators = [new VisKort.Utils.LinkGenerator.Rejseplanen("TwoLinks", false)];

        infoBox = new OpenLayers.Control.InfoBox({}, infoBoxArea, infoBoxLinkGenerators, infoBoxSettings);

        // Initialize mapcontrols
        var activateInfoBox = true;
        if (this.pagemode == "reportingpage") {
            mapcontrols = [layerSwitcher, scaleLine, panZoomBar, navigation, infoBox, shortcuts,overviewMap, locationsearch, searchlist ];
        }
        else {
            mapcontrols = [layerSwitcher, scaleLine, panZoomBar, navigation, overviewMap, infoBox, shortcuts];
        }

        if (this.pagemode == "print") {
            mapcontrols = [scaleLine, infoBox, panZoomBar, navigation, shortcuts];
        }

        var maxresolution = null;

        if (this.pagemode == "print") {
            var printmapDiv = document.getElementById(mapDivId);
            try {
                if (parentMapDivHeight >= printmapDiv.clientHeight || parentMapDivWidth >= printmapDiv.clientWidth) {
                    maxresolution = (this.maxExt.top - this.maxExt.bottom) / 164;
                } else {
                    maxresolution = (this.maxExt.top - this.maxExt.bottom) / 256;
                }
            } catch (err) {
                maxresolution = (this.maxExt.top - this.maxExt.bottom) / 164;
            }
        } else {
            maxresolution = (this.maxExt.top - this.maxExt.bottom) / 256;
        }

        var options = {
            controls: mapcontrols,
            projection: "EPSG:25832",
            resolutions: this.resolutions,
            units: "m",
            maxResolution: maxresolution,
            maxExtent: this.maxExt
        }

        var viskortMapPositionChanged_parentMethodExists = false;
        try {
            if (window.parent)
                viskortMapPositionChanged_parentMethodExists = window.parent.viskortMapPositionChanged;
        } catch (nullPointerOrCrossDomainErr) { }
        if (viskortMapPositionChanged_parentMethodExists) {
            options.eventListeners = { "moveend": this.mapMoveZoomEvent.bind(this), "zoomend": this.mapMoveZoomEvent.bind(this) };
        }

        this.map = new VisStedet.Map(mapDivId, options);


        // Add KML layers from clientside scripting (except searchLayerKml)
        var loadData_parentMethodExists = false;
        try {
            if (window.parent)
                loadData_parentMethodExists = window.parent.loadData;
        } catch (nullPointerOrCrossDomainErr) { }

        if (loadData_parentMethodExists) {
            var clientLayers = window.parent.loadData();
            var clientLayerLoadEndCallback = function() {
                if (this.formatObject && this.formatObject.kmlDocumentName) {
                    this.name = this.formatObject.kmlDocumentName;
                    this.displayInLayerSwitcher = true;
                }
            }

            if (clientLayers.editKml) {
                var editLayer = new OpenLayers.Layer.KML("editKml", null,
                        			    { isBaseLayer: false,
                        			        formatOptions: { extractStyles: true, extractAttributes: true },
                        			        projection: "EPSG:4326",
                        			        displayInLayerSwitcher: false
                        			    },
                        			    this.internalprojection, clientLayers.editKml);
                this.map.addLayer(editLayer);
            }

            if (clientLayers.viewKml) {
                var currentViewLayer;
                for (var i = 0; i < clientLayers.viewKml.length; i++) {
                    currentViewLayer = new OpenLayers.Layer.KML("viewKml_" + i, null,
                        			    { isBaseLayer: false,
                        			        formatOptions: { extractStyles: true, extractAttributes: true },
                        			        projection: "EPSG:4326",
                        			        displayInLayerSwitcher: false
                        			    }, this.internalprojection, clientLayers.viewKml[i]);
                    this.map.addLayer(currentViewLayer);
                    currentViewLayer.events.register('loadend', currentViewLayer, clientLayerLoadEndCallback);
                }
            }

            this.clientLayersKml = clientLayers;
        }

        // Add layers
        var layers = mapConfig.mapconfig.layerlist.layer;
        if (layers == null)
            alert("Kortlag mangler at blive inkluderet");
        else if (layers[0] == null)
            this.addLayer(layers);
        else {
            for (var i = 0; i < layers.length; i++) {
                if (layers[i]["@id"] == searchResultLayerId) {
                    if (clientLayers)
                        this.addLayer(layers[i], clientLayers.searchLayerKml);
                    else
                        this.addLayer(layers[i]);
                }
                else
                    this.addLayer(layers[i]);
            }
        }

        // Add hover control for to be used for KML layers
        var kmlLayers = [];
        for (var i = 0; i < this.map.layers.length; i++) {
            if (this.map.layers[i].CLASS_NAME == "OpenLayers.Layer.Vector" && this.map.layers[i].protocol != null &&
        		this.map.layers[i].protocol.CLASS_NAME == "OpenLayers.Protocol.HTTP" && this.map.layers[i].protocol.format.CLASS_NAME == "OpenLayers.Format.KML") {

                kmlLayers.push(this.map.layers[i]);
            }
        }
        this.setupHoverControl(kmlLayers);

        // Zoom to all of denmark
        this.map.fractionalZoom = false;
        this.map.zoomToExtent(this.startExt, true);

        if (this.pagemode == "reportingpage") {
            locationsearch.activate();
            locationsearch.active_locationSearch();
            searchlist.activate();
            searchlist.active_SearchList();
        }

        // Activate infobox
        if (activateInfoBox) {
            infoBox.activate();
        }
        this.infoBox = infoBox;
    },

    /**
    * Method: addLayer
    * Method to add a layer. Invoked by the constructor during the initialization
    *
    * Parameters:
    * layerConfig - <Object> Subset of the MapConfig.xml file content corresponding to the layer. JSON formatted
    */
    addLayer: function(layerConfig, clientKml) {
        var layer, selectControl;
        if (layerConfig["@type"] == "WMS") {
            layer = new OpenLayers.Layer.WMS(layerConfig.name, layerConfig.url, layerConfig.params, layerConfig.options);
            layer.strategies = new OpenLayers.Strategy.BBOX();
        }

        if (layerConfig["@type"] == "WMS.Untiled") {
            layer = new OpenLayers.Layer.WMS.Untiled(layerConfig.name, layerConfig.url, layerConfig.params, layerConfig.options);
        }
        else if (layerConfig["@type"] == "WFS") {
            layer = new OpenLayers.Layer.WFS(layerConfig.name, layerConfig.url, layerConfig.params, layerConfig.options);
        }
        else if (layerConfig["@type"] == "KML") {
            if (clientKml)
                layer = new OpenLayers.Layer.KML(layerConfig.name, null, layerConfig.options, this.internalprojection, clientKml);
            else
                layer = new OpenLayers.Layer.KML(layerConfig.name, layerConfig.url, layerConfig.options, this.internalprojection);
        }
        else if (layerConfig["@type"] == "WMTS") {

            layerConfig.options.tileSize = new OpenLayers.Size(256, 256);
            layerConfig.options.tileOrigin = new OpenLayers.LonLat(this.maxExt.left, this.maxExt.top);
            layerConfig.options.tileFullExtent = this.maxExt.clone();
            layer = new OpenLayers.Layer.WMTS(layerConfig.name, layerConfig.url, layerConfig.params, layerConfig.options);
        }
        else if (layerConfig["@type"] == "BBOXKML") {
            layer = new OpenLayers.Layer.Vector(layerConfig.name, OpenLayers.Util.extend(layerConfig.options, {
                projection: this.internalProjection,
                strategies: [new OpenLayers.Strategy.BBOX()],
                protocol: new OpenLayers.Protocol.HTTP({
                    url: layerConfig.url,
                    format: new OpenLayers.Format.KML()
                }),
                styleMap: new OpenLayers.StyleMap({
                    "default": new OpenLayers.Style({
                        externalGraphic: layerConfig.options.extra.legend.src,
                        graphicWidth: 12,
                        graphicHeight: 12
                    })
                })
            }));
        }

        layer.addOptions({ mapConfigId: layerConfig["@id"] });

        this.map.addLayer(layer);

        if (layer.isBaseLayer && layerConfig.options && layerConfig.options.visibility) {
            this.map.setBaseLayer(layer);
        }
    },

    /**
    * Method: addComponent
    * Method to add components (<VisKort.Component> sub classes). Must be called after component initialization.
    *
    * Parameters:
    * name - <String> name (key) of the component
    * component - <VisKort.Component>
    */
    addComponent: function(name, component) {
        component.setContext(this, this.map, this.pagemode);
        this.components[name] = component;

        component.startup();
    },



    /**
    * Method: activateComponent
    * Method to activate a component. Must be invoked when a component gets focus.
    * Only one component can be active at a time.
    *
    * Parameters:
    * name - <String> name (key) of the component
    */
    activateComponent: function(name) {
        if (this.activeComponentName != name) {
            if (this.activeComponentName && this.components[this.activeComponentName])
                this.components[this.activeComponentName].deActivate();

            if (this.components[name])
                this.components[name].activate();

            this.activeComponentName = name;
        }
    },

    /**
    * Method: getActiveComponent
    * Method to return the active compoent <VisKort.Component>.
    */
    getActiveComponent: function() {
        if (this.activeComponentName)
            return this.components[this.activeComponentName]
        else
            return null;
    },

    /**
    * Method: getLayerById
    * Method to get the layer <OpenLayers.Layer> by a layer id from the MapConfig.xml file
    *
    * Parameters:
    * id - <String> 
    */
    getLayerById: function(id) {
        var layerList = this.map.layers;
        for (var i = 0; i < layerList.length; i++) {
            if (layerList[i].mapConfigId == id)
                return layerList[i];
        }
        return null;
    },

    /**
    * Method: openPopup
    * Method to open a "popup" map from the current "iframe" map - transfer the current state of the map
    *
    * Parameters:
    * baseQuerystring - <string> part of the original querystring that is readonly (not affected by user interaction)
    */
    openPopup: function() {
        var currentstate = "PanToEasting=" + this.map.getCenter().lon + "&PanToNorthing=" + this.map.getCenter().lat + "&ZoomLevel=" + Math.round(this.map.getZoom());

        var activeLayers = "";
        var includeComma = false;
        for (var q = 0; q < this.map.layers.length; q++) {
            if (this.map.layers[q].visibility && this.map.layers[q].options.mapConfigId != null) {
                if (includeComma)
                    activeLayers += ",";
                else
                    includeComma = true;

                activeLayers += this.map.layers[q].options.mapConfigId;
            }
        }

        var qs = currentstate;
        if (qs.length > 0 && activeLayers.length > 0)
            qs += "&";
        qs += "DefaultOn=" + activeLayers;
        if (qs.length > 0 && this.baseQuerystring.length > 0)
            qs += "&";
        qs += this.baseQuerystring;

        window.open('PopupMap.aspx?' + qs, '', 'location=no,status=no,width=850,height=550,resize=yes', false);
    },

    /**
    * Method: openAboutPage
    * Method to open the "about" page
    */
    openAboutPage: function() {
        window.open('About.aspx?CallingApp=' + this.active_theme, '', 'status=no,width=800,height=800,resize=yes', false);
    },

    setupHoverControl: function(kmlLayers) {

        var hover = new OpenLayers.Control.GetFeatures(kmlLayers, { clickTolerance: new OpenLayers.Size(12, 12) });
        hover.events.on({ "featuresretrieved": this.hoverInfo, "scope": this });
        this.map.addControl(hover);
        hover.activate();
    },

    hoverInfo: function(evt) {

        infoBox.showCombinedInfo(evt);
        OpenLayers.Event.stop(evt);
    },

    mapMoveZoomEvent: function(evt) {
        var currentExtent = this.map.getExtent();
        var currentExtentWGS84 = currentExtent.transform(new OpenLayers.Projection(this.internalprojection), new OpenLayers.Projection("EPSG:4326"));
        window.parent.viskortMapPositionChanged(currentExtent.toArray(), currentExtentWGS84.toArray());
    },

    //	hoverInfo: function(evt) {
    //		var lonlat = evt.lonlat;
    //		var features = evt.features;
    //		if (features) lonlat = features[0].geometry.bounds.getCenterLonLat();

    //		var html = "";
    //		for (var i = 0; i < features.length; i++) {
    //			html += "<p class='" + this.CLASS_NAME.replace(/\./g, "") + "Item" + "'><b>" + features[i].layer.extra.infoBoxTitle + "</b><br /><br />" + features[i].data.name + "<br />" + features[i].data.description + "</p>";
    //			if (i != features.length - 1) html += "<hr>";
    //		}

    //		infoBox.showCombinedInfo(lonlat, html);

    //		OpenLayers.Event.stop(evt);
    //	},

    CLASS_NAME: "VisKort.Application"
});
