/*
 * Copyright (C) 2008 Camptocamp, OpenGeo
 *
 * This file is part of GeoExt
 *
 * GeoExt is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * GeoExt is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GeoExt.  If not, see <http://www.gnu.org/licenses/>.
 */

Ext.namespace('GeoExt', 'GeoExt.widgets');

/**
 * Class: GeoExt.widgets.LegendPanel
 *      LegendPanel is an Ext.Panel that displays a legend for all layers in a
 *      map. Currently the following layers are supported: 
 *      - {OpenLayers.Layer.WMS}
 *      In the future other layer types can be added.
 *
 * Inherits from:
 *  - {Ext.Panel}
 */

/**
 * Constructor: GeoExt.widgets.LegendPanel
 * Create an instance of GeoExt.widgets.LegendPanel
 *
 * Parameters:
 * config - {Object} A config object used to set the legend
 *     panel's properties.
 *
 * Returns:
 * {<GeoExt.widgets.LegendPanel>}
 */
GeoExt.widgets.LegendPanel = function(config){
    Ext.apply(this, config);
    GeoExt.widgets.LegendPanel.superclass.constructor.call(this);
}

GeoExt.widgets.LegendPanel.LEGENDURL = 1;
GeoExt.widgets.LegendPanel.GETLEGENDGRAPHIC = 0;

Ext.extend(GeoExt.widgets.LegendPanel, Ext.Panel, {

    /**
     * APIProperty: wmsMode
     * {Integer} should the legend component use SLD WMS GetLegendGraphic
     *     requests or LegendURLs from the GetCapabilities reponse?
     *     One of: GeoExt.widgets.LegendPanel.GETLEGENDGRAPHIC or 
     *     GeoExt.widgets.LegendPanel.LEGENDURL. The LEGENDURL option requires
     *     the OpenLayers.Format.WMSCapabilities parser. GETLEGENDGRAPHIC is
     *     the default.
     */
    wmsMode: GeoExt.widgets.LegendPanel.GETLEGENDGRAPHIC,

    /**
     * APIProperty: map
     * {OpenLayers.Map}
     */
    map: null,

    /**
     * APIProperty: labelCls
     * {String} Additional css class to put on the legend labels
     */
    labelCls: null,

    /**
     * APIProperty: wmsLegendFormat
     * {String} the format to use in the GetLegendGraphic requests, defaults
     *     to image/png. The value should be a valid mime-type.
     */
    wmsLegendFormat: "image/png",

    /**
     * APIProperty: ascending
     * {Boolean} if true the layers in the tree will show the
     *     bottom OpenLayer layer on top and the top OpenLayer layer on the
     *     bottom. If false, this is inverted.
     */
    ascending: true,

    /**
     * APIProperty: static
     * {Boolean} if true the LegendPanel will not listen to the addlayer,
     *     changelayer and removelayer events. So it will load with the initial
     *     state of the map object and not change anymore.
     */
    static: false,

    /**
     * Property: idPrefix
     * {String} the prefix to use for the id attribute value of the sub panels
     *    (gxt for GeoExt and lp for LegendPanel)
    */
    idPrefix: 'gxt-lp-',

    /**
     * Method: initComponent
     *      Initialize this component. We register for the events here.
     */
    initComponent: function() {
        if (!this.static) {
            this.map.events.register("addlayer", this, this.addLayer);
            this.map.events.register("changelayer", this, this.changeLayer);
            this.map.events.register("removelayer", this, this.removeLayer);
        }
        GeoExt.widgets.LegendPanel.superclass.initComponent.call(this);
    },

    /**
     * Method: onRender
     *      This function is called when the component renders.
     */
    onRender: function(ct, position) {
        GeoExt.widgets.LegendPanel.superclass.onRender.call(this, ct, position);
        var layers = this.map.layers.slice();
        if (!this.ascending) {
            layers.reverse();
        }
        for (var i=0, len=layers.length; i<len; i++) {
            var layer = layers[i];
            this.createLegend(layer);
        }
    },

    /**
     * Method: onDestroy
     *      This function is called when the component destroys. We deregister
     *      the events here.
     */
    onDestroy: function() {
        if (!this.static) {
            this.map.events.unregister("addlayer", this, this.addLayer);
            this.map.events.unregister("changelayer", this, this.changeLayer);
            this.map.events.unregister("removelayer", this, this.removeLayer);
        }
        GeoExt.widgets.LegendPanel.superclass.onDestroy.call(this);
    },

    /**
     * Method: addLayer
     *      Internal function called on the addlayer event
     *
     * Parameters:
     * evt - {Object} The event object sent by OpenLayers
     */
    addLayer: function(evt) {
        this.createLegend(evt.layer);
    },

    /**
     * Method: removeLayer
     *      Internal function called on the removelayer event
     *
     * Parameters:
     * evt - {Object} The event object sent by OpenLayers
     */
    removeLayer: function(evt) {
        if (evt.layer && evt.layer instanceof OpenLayers.Layer.WMS) {
            this.remove(Ext.getCmp(this.generatePanelId(evt.layer)));
        }
    },

    /**
     * Method: changeLayer
     *      Internal function called on the changelayer event
     *
     * Parameters:
     * evt - {Object} The event object sent by OpenLayers
     */
    changeLayer: function(evt) {
        // TODO deal with property order if we want to reflect the order
        // in the legend
		  
		  //edited by Luc add "&& typeof evt.layer.type!='object'" to avoid google map to create legend
        if (evt && evt.layer && evt.property && typeof evt.layer.type!='object') {
            var panel = Ext.getCmp(this.generatePanelId(evt.layer));
            if (!panel) {
                panel = this.createLegend(evt.layer);
            }
            if (evt.property == 'visibility') {
                if (panel) {
                    panel.setVisible(evt.layer.getVisibility());
                }
            }
            // it is possible for an application to hide layers from the legend
            // by setting the hideInLegend property on the layer
            // when the hideInLegend property changes, the application is
            // responsible for triggering a changelayer event with a property
            // named legendvisibility.
            else if (evt.property == 'legendvisibility') {
                if (panel) {
                    panel.setVisible(!evt.layer.hideInLegend);
                }
            }
        }
    },

    /**
     * Method: generatePanelId
     *     Generate an id attribute value for the panel.
     *     It is assumed that the combination of layer.params.LAYER and
     *     layer.mame is unique.
     *
     * Parameters:
     * layer - {<OpenLayers.Layer.WMS>} the layer object
     *
     * Returns: 
     * {String}
     */
    generatePanelId: function(layer) {
        return this.idPrefix + layer.params.LAYERS + layer.name;
    },

    /**
     * Method: onImageLoadError
     *     When the image fails loading (e.g. when the server returns an XML
     *     exception) we need to set the src to a blank image otherwise IE
     *     will show the infamous red cross.
     */
    onImageLoadError: function() {
        this.src = Ext.BLANK_IMAGE_URL;
    },

    /**
     * Method: createLegendPanel
     *     Create a panel for every layer, it will contain a Label with the
     *     layer's name and for every possible sub layer an image
     *
     * Parameters:
     * id - {String} the unique id to use for the panel
     * title - {String} the title of the layer
     * legImg - {Object} the legend image object
     *
     * Returns:
     * {<Ext.Panel>}
     */
    createLegendPanel: function(id, title, legImg) {
        // TODO: we probably need the ability to change the css class
        // of the label
        var panel = new Ext.Panel({
            id: id,
            bodyStyle: this.bodyStyle,
			border: false,
            items: [
                new Ext.form.Label({
                    text: title,
                    cls: 'x-form-item x-form-item-label' + 
					(this.labelCls ? ' ' + this.labelCls : '')
                })
            ]
        });
        for (var i=0, len=legImg.length; i<len; i++) {
            panel.add(new Ext.BoxComponent({el: legImg[i]}));
        }
        return panel;
    },

    /**
     * Method: getLegendUrl
     *     Get the URL from which the legend image can be retrieved
     *
     * Parameters:
     * layer - {<OpenLayers.Layer.WMS>} the WMS layer
     * layerName - {String} one of the layers from the LAYERS parameter
     *
     * Returns:
     * {String}
     */
    getLegendUrl: function(layer, layerName) {
        if (this.wmsMode == GeoExt.widgets.LegendPanel.GETLEGENDGRAPHIC) {
           return layer.getFullRequestString({
                REQUEST: "GetLegendGraphic",
                WIDTH: null,
                HEIGHT: null,
                EXCEPTIONS: "application/vnd.ogc.se_xml",
                LAYER: layerName,
                LAYERS: null,
                SRS: null,
                FORMAT: this.wmsLegendFormat
            });
        }
    },

    /**
     * Method: createImage
     *     Create an image object for the legend image
     *
     * Parameters:
     * src - {String} the source of the image (url)
     * id - {String} the id (prefix) for the image object
     *
     * Returns:
     * {DOMElement}
     */
    createImage: function(src, id) {
        var legendImage = document.createElement("img");
        Ext.EventManager.addListener(legendImage, 'error', 
            this.onImageLoadError, legendImage);
        legendImage.src = src;
        legendImage.id = id+'_img';
        return legendImage;
    },

    /**
     * Method: createLegend
     *     Create the legend panel for a layer
     *
     * Parameters:
     * layer - {<OpenLayers.Layer>} the layer object
     */
    createLegend: function(layer) {
        // currently only OpenLayers.Layer.WMS is supported and
        // only for visible layers a legend is created.
        // If an application does not want a layer in the legend
        // they can set hideInLegend to true on the layer.
        var panel;
        if (layer instanceof OpenLayers.Layer.WMS && 
            layer.getVisibility() && !layer.hideInLegend) {
                // if LAYERS param is in the form of LAYERS=A,B,C we need to 
                //split them up, and show an image per layer
                var layers = layer.params.LAYERS.split(",");
                var legImg = [];
                for (var i=0, len=layers.length; i<len; i++) {
                    var layerName = layers[i];
                    legImg.push(this.createImage(
                        this.getLegendUrl(layer, layerName), 
                        this.generatePanelId(layer)+i));
                }
                panel = this.createLegendPanel(this.generatePanelId(layer),
                    layer.name, legImg);
                if (this.ascending) {
                    this.add(panel);
                } else {
                    var idx = (this.map.layers.length-1)-this.map.getLayerIndex(layer);
                    this.insert(idx, panel);
                }
                this.doLayout();
        }
        return panel;
    }

});

Ext.reg('gx_legend', GeoExt.widgets.LegendPanel);

