/* (C) GeoBasis-DE/LGB
 * Copyright by Landesvermessung und Geobasisinformation Brandenburg (LGB)
 *
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU GPL (>=v3)
 * and comes with ABSOLUTELY NO WARRANTY! Check out the
 * LICENSE for details.
 */

/**
 * This controller is used to parse and evaluate URL parameters.
 * The parameters currently known are:
            'projection',
            'layers',
            'center',
 *
 * - kml: The URL to a kml document.
 * - georss: The URL to a georss document.
 * - csv: The URL to a csv document.
 * - bbox: Comma separated list of coordinates (left,bottom,right,top).
 * - ort, strasse, plz: Equivalent to the gazeteer search. Opens the dialog if
 *   multiple locations are found.
 * - x, y, zoom: Set the center to x, y and zoom to the specified zoom level.
 * - center: Pass the x and y coordinates as list of coordinates (x,y)
 * - bglayer: Specify the background layer by ID
 * - layers: Specify the layers (ID) to be displayed
 * - projection: The EPSG-Code for the projection
 *
 * To use parameters in the base url add the controller to the controllers
 * array in your app.
 *
    Ext.application({
        classicControllers: [
            'Parameter',
            ...
        ],
        ...
    });
 *
 * To parse the parameters call
 * {@link LGB.ext4map.controller.Parameter#evaluateParams}.
  LGB.ext4map.app.getController('LGB.ext4map.controller.Parameter').evaluateParams();
 */
Ext.define('LGB.ext4map.controller.Parameter', {
    extend: 'Ext.app.Controller',
    requires: 'LGB.ext4map.store.LocationResult',
    stores: ['LocationResult'],

    /**
     * The OpenLayers map object.
     */
    map: null,

    /**
     * @private
     * The parsed parameters.
     */
    params: [],

    /**
     * @private
     * The Layer to draw markers.
     */
    markerLayer: null,

    /**
     * @private
     * Layer loader instance
     */
    layerLoader: null,

    /**
     * Set the OpenLayers map object and trigger parsing the URL parameters.
     *
     * @param {OpenLayers.Map} map The map object.
     */
    setMap: function(map) {
        this.map = map.map;
        //this.markerLayer = new OpenLayers.Layer.Markers('marker');
        //this.markerLayer.displayInLayerSwitcher = false;
        //this.map.addLayer(this.markerLayer);
        this.parseParams();
    },

    /**
     * Parse the URL and extract the parameters.
     */
    parseParams: function() {
        var url = window.location;
        var knownParams = ['kml',
            'georss',
            'csv',
            'projection',
            'layers',
            'bglayer',
            'center',
            'bbox',
            'ort',
            'x',
            'y',
            'strasse',
            'plz',
            'zoom',
            'geomaerker'];
        var query = url.search.substring(1);
        var qparams = query.split('&');
        for (var i = qparams.length - 1; i >= 0; i--) {
            var pair = qparams[i].split('=');
            var key = decodeURIComponent(pair[0]);
            for (var j = knownParams.length - 1; j >= 0; j--) {
                if (knownParams[j] === key) {
                    this.params[key] = pair[1];
                }
            }
        }
    },

    /**
     * Evaluate the parsed parameters.
     */
    evaluateParams: function() {
        var me = this;
        // var displayLayers = [];
        // if (this.params.layers) {
            // displayLayers = this.params.layers.split(',');
            // console.log(displayLayers);
        // }
        if (this.params.zoom) {
            this.map.getView().setZoom(parseInt(this.params.zoom));
        }
        if (this.params.layers !== undefined || this.params.bglayer){
            var firstBaseLayer; // get the first baselayer as default base layer
            var baseLayerDisplayed; // if there is already an baselayer defined
            var layerlist = this.map.getLayers().getArray();
            var displayLayers = this.params.layers ? this.params.layers.split(','): [];
            for (var i = 0; i < layerlist.length; i ++ ) {
                var isBaseLayer = false;
                if (layerlist[i].get('options')) {

                    var lyrID = layerlist[i].get('options').permalinkID;
                    isBaseLayer = layerlist[i].get('options').isBaseLayer;
                    var visibleLayer = false;
                    if (isBaseLayer && !firstBaseLayer) {
                        firstBaseLayer = layerlist[i];
                    }
                    if (this.params.bglayer && this.params.bglayer == lyrID &&
                        isBaseLayer) {
                        visibleLayer = true;
                        baseLayerDisplayed = true;
                    }
                    if ( lyrID && displayLayers.indexOf(lyrID.toString()) != -1) {
                        if (isBaseLayer && !baseLayerDisplayed) {
                            visibleLayer = true;
                            baseLayerDisplayed = true;
                        } else if (!isBaseLayer) {
                            visibleLayer = true;
                        }
                    }
                    layerlist[i].setVisible(visibleLayer);
                    this.toggleLayerTree(layerlist[i], visibleLayer)
                    LGB.ext4map.app.fireEvent('layerstatechanged', layerlist[i]);
                }
            }
            if (!baseLayerDisplayed) {
                firstBaseLayer.setVisible(true);
                this.toggleLayerTree(firstBaseLayer, true)
                LGB.ext4map.app.fireEvent('layerstatechanged', firstBaseLayer);
            }
        }
        if (this.params.projection) {
            var success = this.switchProjection();
            if (!success) {
                return;
            }
        }
        if (this.params.kml) {
            this.createKMLLayer(this.params.kml, this.params.projection, 'KML');
        if (this.params.georss) {
        }
            this.createGeoRSSLayer(this.params.georss, this.params.projection, 'GeoRSS');
        }
        if (this.params.csv) {
            this.createCSVLayer(this.params.csv, this.params.projection, 'CSV');
        }
        if (this.params.bbox) {
            var coords = this.params.bbox.split(',');
            if (coords.length !== 4) {
                Ext.window.MessageBox('Fehler', 'Eine BBOX enthält vier Koordinaten!');
            }
            else {
                var numCoords = [];
                for (var i = 0; i < 4; i++) {
                    numCoords.push(parseFloat(coords[i]));
                }
                this.map.getView().fit(
                    numCoords,
                    this.map.getSize());
            }
        }
        if (this.params.x && this.params.y) {
            if (!this.params.center) {
                this.map.getView().setCenter([parseFloat(this.params.x), parseFloat(this.params.y)]);
            }
            var style = new ol.style.Style({
                image: new ol.style.Icon({
                    src: window.bbviewerlib + 'img/markers/marker.png'
                })
            });
            var source = new ol.source.Vector({
                loader: function(extent, resolution, projection) {
                    var coordinate = ol.proj.transform([me.params.x, me.params.y], this.get('dataProjection'), projection);
                    var geom = new ol.geom.Point(coordinate);
                    var feature = new ol.Feature({
                        geometry: geom
                    });
                    this.addFeatures([feature]);
                }
            });
            source.set('dataProjection', this.map.getView().getProjection());
            var markerLayer = new ol.layer.Vector({
                source: source,
                style: style
            });
            this.map.addLayer(markerLayer);
        }
        if (this.params.center) {
            var coords = this.params.center.split(',');
            if (coords.length !== 2) {
                Ext.window.MessageBox('Fehler', 'Kartenmitte kann nicht festgestellt werden!');
            }
            else {
                var numCoords = [];
                for (var i = 0; i < 2; i++) {
                    numCoords.push(parseFloat(coords[i]));
                }
                this.map.getView().setCenter(numCoords);
            }
        }
        if (this.params.ort || this.params.strasse || this.params. plz) {
            var text = this.params.strasse ? this.params.strasse + ' ' : '';
            text += this.params.ort ? this.params.ort + ' ' : '';
            text += this.params.plz ? this.params.plz : '';
            var store = this.getLocationResultStore();
            var epsg = this.map.getView().getProjection().getCode();
            store.load({
                params: {
                    Filter: text,
                    Typename: '0',
                    srsName: epsg,
                    maxFeatures: '50'
                },
                callback: this.loadLocations,
                scope: this
            });
        }
        if (this.params.geomaerker
            && this.params.geomaerker === 'true'
            && me.getController('Geomaerker')
            && me.application.appParams.app.tools.geomaerker) {
                var btn = Ext.ComponentQuery
                        .query('toolbarview[id=top_toolbar] button[action=geomaerker]');
                me.getController('Toolbar').geomaerker(btn[0]);
                me.getController('Geomaerker').toggle(true, btn[0].lastBox.x);
        }
        LGB.ext4map.app.getController('LayerTree').disableLayers();
    },

    /**
     * Manages the layertree entry for the @Param layer.
     * @Param true if the layer is set to be checked in the layerTree
     */
    toggleLayerTree: function(layer, toggleOn){
        if (LGB.ext4map.app.controllerExists('LayerTree') && layer){
            var view = Ext.ComponentQuery.query('layertree')[0];
            var root = view.getRootNode();
            var node = root.findChildBy(function(item) {
                if (item.data.layerID == layer.get('layerID')) {
                    return true;
                }
                return false;
            }, this, true);

            //If no node is found, the layer is probably a sublayer;
            if (!node) {
                return;
            }
            if (toggleOn) {
                node.set('checked', true);
                this.getController('LayerTree').onItemCheckChange(node);
                var parentNode = node.parentNode;
                while (parentNode) {
                    parentNode.expand();
                    parentNode = parentNode.parentNode;
                }
            } else {
                if (node){
                    node.set('checked', false);
                }
            }
            view.store.sync();
        }
    },

    switchProjection: function() {
        if (!LGB.ext4map.app.controllerExists('ProjectionSwitcher')) {
            return false;
        }
        var projection = '';
        var configs = this.application.appParams.map;
        var opts;
        for (var i = 0; i < configs.length; i++) {
            if (configs[i].switcherValue == this.params.projection) {
                projection = ol.proj.get(this.params.projection);
                opts = configs[i];
            }
        }
        if (projection === '') {
            return false;
        }

        if (Ext.getCmp('projectionswitcher')){
            Ext.getCmp('projectionswitcher').select(this.params.projection);
        }
        var projection = ol.proj.get(this.params.projection);
        var oldProjection = this.map.getView().getProjection();
        var centerCoord = this.map.getView().getCenter();
        var center = ol.proj.transform(centerCoord, oldProjection, projection);
        var toolbox = Ext.create('LGB.ext4map.util.Toolbox');
        var viewOpts = {
            projection: projection,
            center: center,
            zoom: this.map.getView().getZoom()
        };
        if (opts.resolutions !== undefined) {
            viewOpts.resolutions = opts.resolutions;
        }
        else if (opts.scales !== undefined) {
            var resolutions = [];
            for (var i = 0; i < opts.scales.length; i++) {
                resolutions.push(
                    toolbox.getResolutionFromScale(opts.scales[i],
                        projection.getUnits()));
            }
            viewOpts.resolutions = resolutions;
        }
        else if (opts.maxZoom !== undefined) {
            viewOpts.maxZoom = opts.maxZoom;
            if (opts.minZoom !== undefined) {
                viewOpts.minZoom = opts.minZoom;
            }
            else {
                viewOpts.minZoom = 0;
            }
        }
        var view = new ol.View(viewOpts);        
        // var draw = this.application.getController('Draw');
        // var features = draw.drawLayer.getSource().getFeatures();
        this.map.setView(view);
        var mapPanel = Ext.getCmp('mapId');
        var ovMap = mapPanel.overview;
        if (ovMap) {
            this.map.removeControl(ovMap);
            mapPanel.overview = null;

            this.map.updateSize();
            var overview = new ol.control.OverviewMap({
                className: 'ol-overviewmap ol-custom-overviewmap',
                view: new ol.View({
                    projection: this.map.getView().getProjection()
                }),
                layers: [this.map.getLayers().getArray()[0]],
                collapsed: true,
                collapsible: true
            });
            mapPanel.overview = overview;
            this.map.addControl(overview);
        }
        //features = draw.drawLayer.getSource().getFeatures().slice(0);
        var layers = this.map.getLayers().getArray();
        for (i = 0; i < layers.length; i++) {
            if (layers[i] instanceof ol.layer.Vector &&
                layers[i].getSource().getFormat() instanceof ol.format.WFS) {
                layers[i].getSource().getProjection() == projection;
                layers[i].getSource().clear();
                layers[i].getSource().changed();
            }
            else if(layers[i] instanceof ol.layer.Vector){
                var fs = layers[i].getSource().getFeatures();
                for (var j = 0; j < fs.length; j++) {
                    fs[j].getGeometry().transform(oldProjection, projection);
                }
            }
            else if (layers[i] instanceof ol.layer.Tile) {
                 gridOpts = {
                    resolutions: viewOpts.resolutions,
                    extent: opts.maxExtent
                };
                var params = layers[i].getSource().getParams();
                var url = layers[i].getSource().getUrls()[0];
                layers[i].setSource(new ol.source.TileWMS({
                    url: url,
                    params: params,
                    wrapX: false,
                    noWrap: true,
                    tileGrid: new ol.tilegrid.TileGrid(gridOpts)
                }));
                layers[i].getSource().on('tileloadstart', this.layerLoader.loadStart, this.layerLoader);
                layers[i].getSource().on('tileloadend', this.layerLoader.loadEnd, this.layerLoader);
                layers[i].getSource().changed();
            }
        }
        //for (var i = 0; i < features.length; i++) {
        //    features[i].getGeometry().transform(oldProjection, projection);
        //}

        //draw.drawLayer.getSource().addFeatures(features);

        // Change the digits of the mousePosition according to the Projection
        var projSwitcher = Ext.ComponentQuery.query('projectionswitcher');
        var digit = 2;
        if (projSwitcher && projSwitcher.length > 0) {
            var proj = projSwitcher[0].value;
            if (proj === 'EPSG:4326') {
                digit = 5;
            }
            else {
                digit = 2;
            }
        }

        var mousePositions = Ext.ComponentQuery.query('mouseposition');
        if (mousePositions.length > 0){
            mousePositions[0].digits = digit;
        }
        this.map.updateSize();
        return true;
    },

    /**
     * Create a Vector layer and add the features found in the kml document.
     *
     * @param {String} url The URL.
     * @param {String} title The layer title.
     */
    createKMLLayer: function(url, proj, title) {
        var format = new ol.format.KML({
            extractStyles: true
        });
        if (proj) {
            format.defaultDataProjection = ol.proj.get(proj);
        }
        else {
            format.defaultDataProjection =
                ol.proj.get(this.map.getView().getProjection().getCode());
        }
        var source = new ol.source.Vector({
            format: format,
            url: url
        });
        //TODO: Only use one format attribute
        var layer = new ol.layer.Vector({
            name: title,
            source: source,
            type: 'kml',
            options: {
                format: 'kml',
                featureInfo: true,
                hover: true
            }
        });
        this.map.addLayer(layer);
        //Wait for the kml layer to be ready, then zoom to the extent
        var readyListener = function() {
            if (layer.getSource().getState() === 'ready' &&
                    layer.getSource().getExtent()[0] != Infinity) {
                this.map.getView().fit(layer.getSource().getExtent(), this.map.getSize());
                if (this.params['zoom']) {
                    this.map.getView().setZoom(parseInt(this.params.zoom));
                }
                layer.un('change', readyListener, this);
            }
        }
        layer.on('change', readyListener, this);
    },

    /**
     * Create a Vector layer and add the features found in the georss document.
     *
     * @param {String} url The Url
     * @param {String} title The layer title
     */
    createGeoRSSLayer: function(url, proj, title) {
        // TODO Implement a GeoRSS parser or wait for OL 3...
    },

    /**
     * Create a Vector layer and add the features found in the csv document.
     *
     * @param {String} url The URL.
     * @param {String} title The layer title.
     */
    createCSVLayer: function(url, proj, title) {
        var me = this;
        Ext.Ajax.request({
            url: url,
            success: function(response) {
                var gj = csv2geojson.csv2geojson(response.responseText, {
                    latfield: 'y',
                    lonfield: 'x',
                    delimiter: ','
                }, function(err, data) {
                    if (err) {
                        Ext.Msg.alert('Fehler!',
                            'Die angegebene CSV-Datei konnte nicht verarbeitet werden.');
                        return;
                    }
                    var source = new ol.source.Vector({
                        features: (new ol.format.GeoJSON()).readFeatures(data)
                    });
                    var layer = new ol.layer.Vector({
                        name: title,
                        source: source
                    });
                    me.map.addLayer(layer);
                    if (proj !== 'EPSG:4236') {
                        var features = layer.getSource().getFeatures();
                        for (var i = 0; i < features.length; i++) {
                            features[i].getGeometry().transform('EPSG:4326', proj);
                        }
                    }
                });
            },
            failure: function(response) {
                Ext.Msg.alert('Fehler!',
                    'Die angegebene CSV-Datei konnte nicht geladen werden.');
            }
        });
    },

    /**
     * Zoom to the specified bbox.
     *
     * @param {OpenLayers.Bounds} bbox The bounds to zoom to.
     */
    zoomTo: function(bbox) {
        this.map.getView().fit(bbox, this.map.getSize());
    },

    /**
     * @private
     * Load the locations similar to the gazetteer search.
     */
    loadLocations: function(records, operation, success) {
        if (records.length > 1) {
            this.showSelector();
        }
        else {
            Ext.Ajax.request({
                scope: this,
                url: './ows/fassade.php?url=' + records[0].get('url'),
                // url: records[0].get('url'),
                success: this.readResponse
            });
        }
    },

    /**
     * @private
     * Open the gazetteer search window.
     */
    showSelector: function() {
        var view = Ext.create('LGB.ext4map.view.Gazetteer', {
            features: {
                searchAddress: true
            },
            closeAction: 'destroy'
        });
        view.show();
        var layers = this.map.getLayers().getArray();
        var hl, cl;
        for (var i = 0; i < layers.length; i++) {
            if (layers[i].get('name') === 'Ortsuche') {
                hl = layers[i];
            }
            if (layers[i].get('name') === 'OrtsucheFix') {
                cl = layers[i];
            }
        }
        view.down('gazetteerpanel').updateResults(hl, cl, null, null, 'LocationResult');
    },

    /**
     * Read the features from search response and display the result on the map
     * or open the gazetteer search window.
     */
    readResponse: function(response) {
        var gml = new ol.format.WFS({
            gmlFormat: new ol.format.ADVGML3()
        });
        features = gml.readFeatures(response.responseXML); //Array(OpenLayers.Feature.Vector)
        if (features.length > 1) {
            this.showSelector();
        }
        else {
            var feat = features[0];
            var layers = this.map.getLayers().getArray();
            var cl;
            for (var i = 0; i < layers.length; i++) {
                if (layers[i].get('name') === 'OrtsucheFix') {
                    cl = layers[i];
                }
            }
            cl.getSource().clear();
            var geom = feat.getGeometry().clone();
            var feature = new ol.Feature({
                geometry: geom
            });
            cl.getSource().addFeatures([feature]);
            cl.setVisible(true);
            this.zoomToExtent(geom.getExtent());
        }
    }
});