/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
 * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
 * full text of the license. */

/** 
 * @requires OpenLayers/Control/LayerSwitcher.js
 */

/**
 * Class: OpenLayers.Control.LayerSwitcher
 * The LayerSwitcher control displays a table of contents for the map. This 
 * allows the user interface to switch between BaseLasyers and to show or hide
 * Overlays. By default the switcher is shown minimized on the right edge of 
 * the map, the user may expand it by clicking on the handle.
 *
 * To create the LayerSwitcher outside of the map, pass the Id of a html div 
 * as the first argument to the constructor.
 * 
 * Inherits from:
 *  - <OpenLayers.Control.LayerSwitcher>
 */
OpenLayers.Control.LayerSwitcher = 
  OpenLayers.Class(OpenLayers.Control.LayerSwitcher, {
  // DOM Elements
  
    /**
     * Property: routeLayersDiv
     * {DOMElement} 
     */
    routeLayersDiv: null,

    /** 
     * Property: routeLayers
     * {Array(<OpenLayers.Layer>)}
     */
    routeLayers: [],

    /** 
     * Property: routeHierarchy
     * {Object()}
     */
    routeHierarchy: null,
    
    /** 
     * Method: redraw
     * Goes through and takes the current state of the Map and rebuilds the
     *     control to display that state. Groups base layers into a 
     *     radio-button group and lists each data layer with a checkbox.
     *
     * Returns: 
     * {DOMElement} A reference to the DIV DOMElement containing the control
     */  
    redraw: function() {
        //if the state hasn't changed since last redraw, no need 
        // to do anything. Just return the existing div.
        if (!this.checkRedraw()) { 
            return this.div; 
        } 

        //clear out previous layers 
        this.clearLayersArray("base");
        this.clearLayersArray("data");
        this.clearLayersArray("route");
        
        var containsOverlays = false;
        var containsBaseLayers = false;
        
        // Save state -- for checking layer if the map state changed.
        // We save this before redrawing, because in the process of redrawing
        // we will trigger more visibility changes, and we want to not redraw
        // and enter an infinite loop.
        var len = this.map.layers.length;
        this.layerStates = new Array(len);
        for (var i = 0; i < len; i++) {
            var layer = this.map.layers[i];
            this.layerStates[i] = {
                'name': layer.name, 
                'visibility': layer.visibility,
                'inRange': layer.inRange,
                'id': layer.id
            };
        }    

        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];
            var baseLayer = layer.isBaseLayer;

            if (layer.displayInLayerSwitcher) {

                if (baseLayer) {
                    containsBaseLayers = true;
                } else {
                    containsOverlays = true;
                }    

                // only check a baselayer if it is *the* baselayer, check data
                //  layers if they are visible
                var checked = (baseLayer) ? (layer == this.map.baseLayer)
                                          : layer.getVisibility();
    
                // create input element
                var inputElem = document.createElement("input");
                inputElem.id = this.id + "_input_" + layer.name;
                inputElem.name = (baseLayer) ? "baseLayers" : layer.name;
                inputElem.type = (baseLayer) ? "radio" : "checkbox";
                inputElem.value = layer.name;
                inputElem.checked = checked;
                inputElem.defaultChecked = checked;

                if (!baseLayer && !layer.inRange) {
                    inputElem.disabled = true;
                }
                var context = {
                    'inputElem': inputElem,
                    'layer': layer,
                    'layerSwitcher': this
                };
                OpenLayers.Event.observe(inputElem, "mouseup", 
                    OpenLayers.Function.bindAsEventListener(this.onInputClick,
                                                            context)
                );
                
                // create span
                var labelSpan = document.createElement("span");
                if (!baseLayer && !layer.inRange) {
                    labelSpan.style.color = "gray";
                }
                labelSpan.innerHTML = layer.name;
                labelSpan.style.verticalAlign = (baseLayer) ? "bottom" 
                                                            : "baseline";
                OpenLayers.Event.observe(labelSpan, "click", 
                    OpenLayers.Function.bindAsEventListener(this.onInputClick,
                                                            context)
                );
                // create line break
                var br = document.createElement("br");
    
                
                var groupArray = (baseLayer) ? this.baseLayers
                                             : this.dataLayers;
                groupArray.push({
                    'layer': layer,
                    'inputElem': inputElem,
                    'labelSpan': labelSpan
                });
                                                     
    
                var groupDiv = (baseLayer) ? this.baseLayersDiv
                                           : this.dataLayersDiv;
                groupDiv.appendChild(inputElem);
                groupDiv.appendChild(labelSpan);
                groupDiv.appendChild(br);
            }
        }

        if (this.routeHierarchy != null) {
            this.redrawTraces(this.routeHierarchy, this.routeLayersDiv);
        }

        // if no overlays, dont display the overlay label
        this.dataLbl.style.display = (containsOverlays) ? "" : "none";        
        
        // if no baselayers, dont display the baselayer label
        this.baseLbl.style.display = (containsBaseLayers) ? "" : "none";        

        return this.div;
    },

    /** 
     * Method: updateMap
     * Cycles through the loaded data and base layer input arrays and makes
     *     the necessary calls to the Map object such that that the map's 
     *     visual state corresponds to what the user has selected in 
     *     the control.
     */
    updateMap: function() {
        // set the newly selected base layer        
        for(var i=0, len=this.baseLayers.length; i<len; i++) {
            var layerEntry = this.baseLayers[i];
            if (layerEntry.inputElem.checked) {
                this.map.setBaseLayer(layerEntry.layer, false);
            }
        }

        // set the correct visibilities for the overlays
        for(var i=0, len=this.dataLayers.length; i<len; i++) {
            var layerEntry = this.dataLayers[i];   
            layerEntry.layer.setVisibility(layerEntry.inputElem.checked);
        }

        // set the correct visibilities for the route layers
        for(var i=0, len=this.routeLayers.length; i<len; i++) {
            var layerEntry = this.routeLayers[i];   
            layerEntry.layer.setVisibility(layerEntry.inputElem.checked);
        }
    },

	loadTraces: function(routeHierarchy) {
		this._loadTraces(routeHierarchy);
		this.routeHierarchy = routeHierarchy;
		this.layerStates = []; // Force redraw
		this.redraw();
	},

	_loadTraces: function(group) {
		for (var i = 0; i < group.traces.length; i++) {
			var trace = group.traces[i];
			if (trace.layer === undefined) 
			{
				trace.layer = new OpenLayers.Layer.GML(trace.name, "trace.php?trace_id=" + trace.id, 
				{
					format: OpenLayers.Format.GPX,
					style: {strokeColor: "red", strokeWidth: 3, strokeOpacity: 0.7},
					projection: new OpenLayers.Projection("EPSG:4326"),
					visibility: trace.visible,
					displayInLayerSwitcher: false
				});
				trace.bbox.transform(projWGS84, projMerc);
				map.addLayer(trace.layer);
			}
		}
		for (var i = 0; i < group.children.length; i++) {
			var child = group.children[i];
			this._loadTraces(child);
		}
	},

	redrawTraces: function(group, div) {
		var groupLbl = document.createElement("div");
		groupLbl.innerHTML = group.name;

		var collapsedIndicator = document.createElement("span");
		collapsedIndicator.className = group.collapsed ? "" : "hidden";
		collapsedIndicator.innerHTML = "...";
		collapsedIndicator.title = "Klik om groep uit te klappen";
		groupLbl.appendChild(collapsedIndicator);

		var groupDiv = document.createElement("div");
		groupDiv.style.paddingLeft = "15px";
		groupDiv.className = group.collapsed ? "hidden" : "";

		var context = {
			'groupElem': groupDiv,
			'groupIndicator': collapsedIndicator,
			'group': group
		};
		OpenLayers.Event.observe(groupLbl, "mouseup", 
			OpenLayers.Function.bindAsEventListener(this.onGroupClick, context)
		);

		for (var i = 0; i < group.traces.length; i++) {
			var trace = group.traces[i];
			var layer = trace.layer;
			var checked = layer.getVisibility();

			// create input element
			var inputElem = document.createElement("input");
			inputElem.id = this.id + "_input_" + layer.name;
			inputElem.name = layer.name;
			inputElem.type = "checkbox";
			inputElem.value = layer.name;
			inputElem.checked = checked;
			inputElem.defaultChecked = checked;

			if (!layer.inRange) {
				inputElem.disabled = true;
			}
			var context = {
				'inputElem': inputElem,
				'layer': layer,
				'layerSwitcher': this
			};
			OpenLayers.Event.observe(inputElem, "mouseup", 
				OpenLayers.Function.bindAsEventListener(this.onInputClick, context)
			);
			
			// create span
			var labelSpan = document.createElement("span");
			if (!layer.inRange) {
				labelSpan.style.color = "gray";
			}
			labelSpan.innerHTML = layer.name;
			labelSpan.title = trace.title;
			labelSpan.style.verticalAlign = "baseline";
			OpenLayers.Event.observe(labelSpan, "click", 
				OpenLayers.Function.bindAsEventListener(this.onInputClick, context)
			);

			// create zoom image
			var zoomImage = document.createElement("img");
			zoomImage.src = "zoom.png";
			zoomImage.title = "Zoom naar deze route";
			var context = {
				'bbox': trace.bbox
			};
			OpenLayers.Event.observe(zoomImage, "mouseup", 
				OpenLayers.Function.bindAsEventListener(this.onClickZoom, context)
			);

			// create download link
			var downloadLink = document.createElement("a");
			downloadLink.href = "trace.php?trace_id=" + trace.id;
			downloadLink.title = "Download GPX bestand";
			var downloadImage = document.createElement("img");
			downloadImage.src = "download.png";
			downloadLink.appendChild(downloadImage);
			var context = {
				'url': downloadLink.href
			};
			OpenLayers.Event.observe(downloadLink, "mouseup", 
				OpenLayers.Function.bindAsEventListener(this.onClickDownload, context)
			);

			// create line break
			var br = document.createElement("br");

			this.routeLayers.push({
				'layer': layer,
				'inputElem': inputElem,
				'labelSpan': labelSpan
			});

			groupDiv.appendChild(inputElem);
			groupDiv.appendChild(labelSpan);
			groupDiv.appendChild(document.createTextNode(" "));
			groupDiv.appendChild(zoomImage);
			groupDiv.appendChild(document.createTextNode(" "));
			groupDiv.appendChild(downloadLink);
			groupDiv.appendChild(br);
		}

		for (var i = 0; i < group.children.length; i++) {
			var child = group.children[i];
			this.redrawTraces(child, groupDiv);
		}

		div.appendChild(groupLbl);
		div.appendChild(groupDiv);
	},

    /** 
     * Method: loadContents
     * Set up the labels and divs for the control
     */
    loadContents: function() {

        //configure main div
        this.div.style.position = "absolute";
        this.div.style.top = "25px";
        this.div.style.right = "0px";
        this.div.style.left = "";
        this.div.style.bottom = "33px";
        this.div.style.fontFamily = "sans-serif";
        this.div.style.fontWeight = "bold";
        this.div.style.marginTop = "3px";
        this.div.style.marginLeft = "3px";
        this.div.style.marginBottom = "3px";
        this.div.style.fontSize = "smaller";   
        this.div.style.color = "white";   
        this.div.style.backgroundColor = "transparent";
    
        OpenLayers.Event.observe(this.div, "mouseup", 
            OpenLayers.Function.bindAsEventListener(this.mouseUp, this));
        OpenLayers.Event.observe(this.div, "click",
                      this.ignoreEvent);
        OpenLayers.Event.observe(this.div, "mousedown",
            OpenLayers.Function.bindAsEventListener(this.mouseDown, this));
        OpenLayers.Event.observe(this.div, "dblclick", this.ignoreEvent);


        // layers list div        
        this.layersDiv = document.createElement("div");
        this.layersDiv.id = this.id + "_layersDiv";
        this.layersDiv.style.paddingTop = "5px";
        this.layersDiv.style.paddingLeft = "10px";
        this.layersDiv.style.paddingBottom = "5px";
        this.layersDiv.style.paddingRight = "0px";
        this.layersDiv.style.backgroundColor = this.activeColor;

        // had to set width/height to get transparency in IE to work.
        // thanks -- http://jszen.blogspot.com/2005/04/ie6-opacity-filter-caveat.html
        //
        //this.layersDiv.style.width = "100%";
        this.layersDiv.style.height = "100%";
        this.layersDiv.style.overflow = "auto";

        this.baseLbl = document.createElement("div");
        this.baseLbl.innerHTML = OpenLayers.i18n("baseLayer");
        this.baseLbl.style.marginTop = "3px";
        this.baseLbl.style.marginLeft = "3px";
        this.baseLbl.style.marginBottom = "3px";
        
        this.baseLayersDiv = document.createElement("div");
        this.baseLayersDiv.style.paddingLeft = "10px";
        /*OpenLayers.Event.observe(this.baseLayersDiv, "click", 
            OpenLayers.Function.bindAsEventListener(this.onLayerClick, this));
        */

        this.dataLbl = document.createElement("div");
        this.dataLbl.innerHTML = OpenLayers.i18n("overlays");
        this.dataLbl.style.marginTop = "3px";
        this.dataLbl.style.marginLeft = "3px";
        this.dataLbl.style.marginBottom = "3px";
        
        this.dataLayersDiv = document.createElement("div");
        this.dataLayersDiv.style.paddingLeft = "10px";

        this.routeLayersDiv = document.createElement("div");

        if (this.ascending) {
            this.layersDiv.appendChild(this.baseLbl);
            this.layersDiv.appendChild(this.baseLayersDiv);
            this.layersDiv.appendChild(this.dataLbl);
            this.layersDiv.appendChild(this.dataLayersDiv);
            this.layersDiv.appendChild(this.routeLayersDiv);
        } else {
            this.layersDiv.appendChild(this.routeLayersDiv);
            this.layersDiv.appendChild(this.dataLbl);
            this.layersDiv.appendChild(this.dataLayersDiv);
            this.layersDiv.appendChild(this.baseLbl);
            this.layersDiv.appendChild(this.baseLayersDiv);
        }    
 
        this.div.appendChild(this.layersDiv);

        OpenLayers.Rico.Corner.round(this.div, {corners: "tl bl",
                                        bgColor: "transparent",
                                        color: this.activeColor,
                                        blend: false});

        OpenLayers.Rico.Corner.changeOpacity(this.layersDiv, 0.75);

        var imgLocation = OpenLayers.Util.getImagesLocation();
        var sz = new OpenLayers.Size(18,18);        

        // maximize button div
        var img = imgLocation + 'layer-switcher-maximize.png';
        this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
                                    "OpenLayers_Control_MaximizeDiv", 
                                    null, 
                                    sz, 
                                    img, 
                                    "absolute");
        this.maximizeDiv.style.top = "5px";
        this.maximizeDiv.style.right = "0px";
        this.maximizeDiv.style.left = "";
        this.maximizeDiv.style.display = "none";
        OpenLayers.Event.observe(this.maximizeDiv, "click", 
            OpenLayers.Function.bindAsEventListener(this.maximizeControl, this)
        );
        
        this.div.appendChild(this.maximizeDiv);

        // minimize button div
        var img = imgLocation + 'layer-switcher-minimize.png';
        var sz = new OpenLayers.Size(18,18);        
        this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
                                    "OpenLayers_Control_MinimizeDiv", 
                                    null, 
                                    sz, 
                                    img, 
                                    "absolute");
        this.minimizeDiv.style.top = "5px";
        this.minimizeDiv.style.right = "0px";
        this.minimizeDiv.style.left = "";
        this.minimizeDiv.style.display = "none";
        OpenLayers.Event.observe(this.minimizeDiv, "click", 
            OpenLayers.Function.bindAsEventListener(this.minimizeControl, this)
        );

        this.div.appendChild(this.minimizeDiv);
    },

    /** 
     * Method: maximizeControl
     * Set up the labels and divs for the control
     * 
     * Parameters:
     * e - {Event} 
     */
    maximizeControl: function(e) {

        //HACK HACK HACK - find a way to auto-size this layerswitcher
        this.div.style.width = "23em";
        this.div.style.height = "";

        this.showControls(false);

        if (e != null) {
            OpenLayers.Event.stop(e);                                            
        }
    },

    /** 
     * Method:
     * A group has been clicked, (un)collapse it
     * 
     * Parameters:
     * e - {Event} 
     *
     * Context:  
     *  - {DOMElement} groupElem
     *  - {DOMElement} groupIndicator
     *  - {Object} group
     */
    onGroupClick: function(e) {
		this.group.collapsed = !this.group.collapsed;
		if (this.group.collapsed) {
			this.groupElem.className = "hidden";
			this.groupIndicator.className = "";
		} else {
			this.groupElem.className = "";
			this.groupIndicator.className = "hidden";
		}
        OpenLayers.Event.stop(e);
    },

    /** 
     * Method:
     * A download should be initiated
     * 
     * Parameters:
     * e - {Event} 
     *
     * Context:  
     *  - {String} url
     */
    onClickDownload: function(e) {
		window.location = this.url;
		OpenLayers.Event.stop(e);
    },

    /** 
     * Method:
     * A zoom should be initiated
     * 
     * Parameters:
     * e - {Event} 
     *
     * Context:  
     *  - {OpenLayers.Bounds} bbox
     */
    onClickZoom: function(e) {
		map.zoomToExtent(this.bbox);
    },


    CLASS_NAME: "OpenLayers.Control.LayerSwitcher"
});
