var LEGEND_ICON_SIZE = 15;

/**
 * Legend class encapsulates the html associated with a legend for a single layer
 * Each layer has a legend and each legend has several rules
 *
 * @param {FreebaseLayer} the layer object that this legend belongs to. Having a reference in the
 *                        child object to the parent is probably a bad idea.
 */
function Legend(layerObj) {
   this.layer       = layerObj;                 // The FreebaseLayer object (parent to this object)
   this.name        = layerObj.display_name;
   this.rules       = layerObj.layer.rules;
   this.element     = null;                     // The element(Ext.form.FieldSet) containing the
   if (layerObj.local_options !== undefined){
     this.units = layerObj.local_options.units;// layer units 
   }
                                                // legend
   this.legendclass = [];                       // An array of LegendClass objects

   var theLPApplyButton = Ext.getCmp('lp-apply');
   var theLPResetButton = Ext.getCmp('lp-reset');
                
    this.resetButton = new Ext.Button({
      scope: this
      ,handler: function() {
         this.layer.reset();
         var menu = Ext.getCmp('main-menu');
         menu.hide(true);
         Ext.ComponentMgr.unregister(menu);
      }
      ,disabled: !this.layer.params.DIRTY
      ,text: 'Reset Layer' 
   });

   this.applyButton = new Ext.Button({          // The apply button for the legend
      scope: this
      ,handler: function() {
         this.apply(); // Copy local changes into the JSON
         this.layer.applyStyle();
         var menu = Ext.getCmp('main-menu');
         menu.hide(true);
         this.resetButton.enable();             // This should be done from applyStle success fn
         theLPResetButton.enable();
         Ext.ComponentMgr.unregister(menu);
      }
      ,disabled: true
      ,text: 'Apply'
   });
   
   this.loadPanel = new Ext.Panel({
      title: ''
      ,border: false
      ,html: '<table><tr><td>' +
             '<img src="' + Drupal.settings.freebase_path + '/assets/images/large-loading.gif"/>' +
             '</td><td><span style="font-weight:bold; font-size:small">Drawing Layer...</span>' +
             '</td></tr></table>'
      ,hidden: true
   });

   this.getElement = function() {
      if (this.element == null) {
         this.element = this.buildLegend();
      }
      return this.element
   }

   /**
    * Apply any styling changes that were made to the layer json in preparation for sending
    * to the server.
    */
   this.apply = function() {
      for (var i=0; i<this.legendclass.length; i++) {
         this.legendclass[i].apply();
      }
      this.layer.params.STYLE_CHG = true;
   }

   /**
    * Reset the checkboxes and colors for this legend to their default states
    */
   this.clean = function(theJson) {
      var theObj = eval('(' + theJson + ')');
           
      for (var i=0; i<this.legendclass.length; i++) {
         // use last symbolizer in legendclass
         var theSymbolizer = theObj.rules[i].symbolizers[theObj.rules[i].symbolizers.length - 1];
         this.legendclass[i].clean(theSymbolizer);
         this.applyButton.disable();
         this.resetButton.disable();
         theLPApplyButton.disable();
         theLPResetButton.disable();
      }
   }

   /**
    * Hide the legend and put a loading indicator in instead
    */
   this.isLoading = false;
   this.setLoadMask = function() {
      this.isLoading = true;
      for (var i=0; i<this.legendclass.length; i++) {
         this.legendclass[i].hide();
      }
      this.loadPanel.show();
   }

   /**
    * Hide the loading indicator and restore the legend
    */
   this.clearLoadMask = function() {
      this.isLoading = false;
      this.loadPanel.hide();
      for (var i=0; i<this.legendclass.length; i++) {
         this.legendclass[i].show();
      }
   }
   /**
    * Synchronize the checkboxes and colors for this legend to match what's in
    * the layer json. This is called if the user makes styling changes and dismisses
    * the legend without applying them.
    */
   this.synchronize = function() {
      for (var i=0; i<this.legendclass.length; i++) {
         this.legendclass[i].synchronize();
      }
      this.applyButton.disable();
      theLPApplyButton.disable();
   }

   this.hideButtons = function() {
      this.applyButton.hide();
      this.resetButton.hide();
   }

   this.showButtons = function() {
      this.applyButton.show();
      this.resetButton.show();
   }

   this.show = function() {
      this.getElement().show();
   }

   this.hide = function() {
      this.getElement().hide();
   }

   /**
    * Build the legend
    */
   this.buildLegend = function() {

      if (this.units !== undefined){
        var title = this.name +' ('+ this.units +')';
      } else {
        var title = this.name
      }

      var fieldset = new Ext.form.FieldSet({
         title: title,
         autoHeight: true,
         cls: 'legendfieldset',
         buttonAlign: 'center',
         buttons: [this.applyButton, this.resetButton]
      });        

      // Create an array of rule names. We'll use this array to detect duplicate rule names.
      // This is needed in the case of rules with maxscale/minscale, where the same rule
      // name is used in more than one rule.
      var ruleNames = [];
      for (var i = 0; i < this.rules.length; i++) {
         
         // See if we've already used this rule
         var found = false;
         for (var j = 0; j < ruleNames.length; j++) {
            if (ruleNames[j] == this.rules[i].name) {
               found = true;
            } 
         }
         if (!found) {
            this.legendclass[i] = new LegendClass(this.rules[i], this.applyButton, this.layer);
            this.legendclass[i].addListener(
               'checkchange', 
               function() { 
                  // alert('listener fired');
               }
            );
            // If the this layer is being loaded we need to hide the legend classes
            // until the layer is done loading, at which point clearLoadMask will un-hide the
            // legend classes and hide the loading indicator.
            if (this.isLoading) {
               this.legendclass[i].hide();
            }

            fieldset.add(this.legendclass[i]);
            ruleNames.push(this.rules[i].name);
         }
      }
         
      fieldset.add(this.loadPanel);
      return fieldset;
   }

   this.buildPrintView = function() {

      if (this.element == null) {
         this.element = this.buildLegend();
      }

      var html = "";
      for (var i=0; i<this.legendclass.length; i++) {
         html += this.legendclass[i].getPrintHtml();
      }
      return html;
   }
}

// Apply and reset buttons on the LegendPanel are now enabled and disabled like 
// the ones on the context menu legend -- done via direct enable/disable calls 
// to dom elements via the button ID -- could also be done via event
// handlers and listeners 

/**
 * An Ext.Panel which contains the checkbox, the color, and the class label
 */
LegendClass = function(_rule, _button, _layer) {
   this.rule=_rule;
   this.layer=_layer;
   this.applyButton = _button;
   
   // used to track color changes
   this.defaultColor;
   this.mostRecentColor;
   this.proposedNewColor;
   this.unappliedColorChg = false;

   var theColor;
   var symbolizer = this.rule.symbolizers[this.rule.symbolizers.length -1]; //This uses the last symbolizer
   var legend_graphic = "";
   var size = 0;
   var graphic = symbolizer.graphic;
   (graphic.size <= LEGEND_ICON_SIZE-1) ? size = graphic.size : size = LEGEND_ICON_SIZE-1;
         
   if (symbolizer.type == "Point") {
        theColor = graphic.fill;
   } else if (symbolizer.type == "Polygon") {
        theColor = symbolizer.fill;
   } else if (symbolizer.type == "Line") {
        theColor = symbolizer.strokes[0].color;
   }
   
   if (!this.defaultColor) {
        this.defaultColor = theColor;
        this.mostRecentColor = theColor;
   }
   
   legend_graphic = this.getLegendGraphic(symbolizer.type, size, theColor, graphic);
   
   // Determine whether this rule is hidden, and mark the checkbox appropriately
   var checked = true;
   if (this.rule.hidden != undefined && this.rule.hidden == "true") {
      checked = false;
   } else {
      checked = true;
   }

   this.checkbox = new Ext.form.Checkbox({
      hideLabel: true,
      boxLabel: '',
      checked: checked
      ,listeners: {
         'check': {
            fn: function() {
                //var theLPApplyButton = Ext.getCmp('lp-apply');
                Ext.getCmp('lp-apply').enable();
                this.applyButton.enable();
            },
            scope: this,
            delay: 0
         }
      }
   });

    // assign unique Id to icon and icon image in order to dynamically change 
    // ID follows pattern LayerName_RuleName_Icon/IconImg
    var iconId = this.layer.name + "_" + this.rule.name.split(" ").join('') + '_Icon';
    var iconImgId = this.layer.name + "_" + this.rule.name.split(" ").join('') + '_IconImg';
    this.icon = {
      id: iconId
      ,xtype: 'panel'
      ,width: LEGEND_ICON_SIZE
      ,height: LEGEND_ICON_SIZE
      ,html: '<img id="'+iconImgId+'" src="'+legend_graphic+'" border="0" />'
      ,bodyStyle: 'padding:2px;'
      ,ownerCt: this
      ,listeners: {
         'render': {
            fn: function() {
               Ext.fly(this.el).on('mouseover', function(e, t) {
                    if ((t.tagName.toLowerCase() == 'img') && (graphic.external_graphic_url == undefined)) {
                        document.body.style.cursor='pointer';
                    }
               }),
               Ext.fly(this.el).on('mouseout', function(e, t) {
                    if ((t.tagName.toLowerCase() == 'img') && (graphic.external_graphic_url == undefined)) {
                        document.body.style.cursor='default';
                    }
               }),
               Ext.fly(this.el).on('click', function(e, t) {
                // create color picker panel
				// should only show if user clicks on the legend graphic and
				// should not show for points with external graphic urls
				if ((t.tagName.toLowerCase() == 'img') && (graphic.external_graphic_url == undefined)) {
				    var menuColor = new Ext.menu.ColorMenu({
						id:'color-menu'
						,allowOtherMenus: true
						,ignoreParentClicks: true
						,defaultAlign: 'tr'
						,shadow: 'drop'
						,ownerCt: this
					});
					
					var cancelItem = new Ext.menu.Item({
				        text: 'Cancel'
				    });
        		     
        		    menuColor.add(cancelItem); 

     				// ext bug when defining ColorMenu select event in listeners object
					// http://extjs.com/forum/showthread.php?t=19749&highlight=colormenu
					// so use on method instead
					menuColor.on("select", function(thePalette,theColor){
					    // keep track of proposed color and most recently applied
					    // color so we can revert back if user dismisses without clicking apply
					    var theLegendClass = Ext.ComponentMgr.get(iconId).ownerCt;
					    theLegendClass.proposedNewColor = theColor;
				        theLegendClass.unappliedColorChg = true;
				        		        
				        // immediately change color in legend
				        legend_graphic = theLegendClass.getLegendGraphic(symbolizer.type, size, theColor, graphic);
				        t.src = legend_graphic;
				        
				        // change color for map -- doesn't actually
				        // get changed until user clicks apply
				        theLegendClass.applyColorChange(symbolizer, theColor, graphic);

                        // enable apply buttons on legend panel and individual
                        // legend class (for legend context menu)
                        Ext.ComponentMgr.get('lp-apply').enable();
                        theLegendClass.applyButton.enable();
					     
					});
					
					menuColor.show(t,"tr");
					Ext.ComponentMgr.register(menuColor);
				}
	          });
            },
            scope: this,
            delay: 0
         }
      }
   };

   LegendClass.superclass.constructor.call(this, {
            layout: 'table',
            border: false,
            baseCls: 'legends',
            width: 160,
            defaults: {
               border: false
            },
            layoutConfig: {
               columns: 3
            },
            autoHeight: true,
            items: [
               this.checkbox
               ,this.icon
               ,{
                  html: '<span style="font-family:sans-serif; font-size:11px;">'+
                     this.rule.name+'</span>'
                  ,bodyStyle: 'margin-left:4px'
            }]
   });
   
   // Generate an html version of the legend for this class for use on the print view
   this.printhtml = '<div style="width: 175px;text-align:left; margin-top:10px;"><img src="'+
     legend_graphic+'"/><span style="font-family:sans-serif; font-size:11px; margin-left:5px;">'+ 
     this.rule.name+'</span></div>';
        
}
Ext.extend(LegendClass, Ext.Panel, {
    rule: null,
    layer: null,
    checkbox: null,
    printhtml: null,
   
    /**
     * Resets the checkbox for this legend class to checked and the legend
     * color to the default color. 
     *
     * TODO: look into a solution that simply redefines the symbolizer 
     *       color for the individual LegendClass and then rerenders
     *       the legend class panel (seems like it would be more of an ext
     *       way of doing things) -- problem is that its not clear exactly
     *       how to rerender a Panel that has already been rendered --
     *       right now, we're swapping out the image contained within the
     *       legendclass panel which may be fine too
     */
    clean: function(symbolizer) {
        this.rule.hidden = "false";
        this.checkbox.setValue(true);
        this.unappliedColorChg = false;
      
        // get default legend class color from passed in symbolizer
        var theDefaultColor;
        switch (symbolizer.type) {
            case "Point":
                if (symbolizer.graphic.external_graphic_url == undefined) { 
                      theDefaultColor = symbolizer.graphic.fill;
                }
                break;
            
            case "Polygon":
                theDefaultColor = symbolizer.fill;
                break;
                
            case "Line":
                theDefaultColor = symbolizer.strokes[0].color;
                break;
                  
            default:
                alert("Unsupported Symbolizer type " + symbolizer.type + " encountered (Legend.js)");
                break;
      }  
      
      // set most recent color to default color      
      this.mostRecentColor = theDefaultColor;
      
      // change color in legend
      var legend_graphic = "";
      var size = 0;
      var graphic = symbolizer.graphic;
      (graphic.size <= LEGEND_ICON_SIZE-1) ? size = graphic.size : size = LEGEND_ICON_SIZE-1;
      
      var theLegendGraphic = this.getLegendGraphic(symbolizer.type, size, theDefaultColor, graphic);
      var iconImg = this.layer.name + "_" + this.rule.name.split(' ').join('') + '_IconImg';
      document.getElementById(iconImg).src = theLegendGraphic;
    
	  // apply color change to legend class symbolizer
	  var legendClassSymbolizer = this.rule.symbolizers[this.rule.symbolizers.length -1]; //This uses the last symbolizer
	  var legendClassGraphic = legendClassSymbolizer.graphic;
	  this.applyColorChange(legendClassSymbolizer, theDefaultColor, legendClassGraphic);
   },
   
   /**
    * Called when the user pushes apply. Updates the 'hidden' flag
    * for this rule, and sets the STYLE_CHG flag for this layer. Changes are not submitted
    * until the apply button is pressed.
    */
   apply: function() {
      // modify the layer JSON for this rule
      if (this.items.items[0].checked == false) {
         this.rule.hidden = "true";
      } else {
         this.rule.hidden = "false";
      }
      // track color changes
      this.mostRecentColor = this.proposedNewColor;
      this.unappliedColorChg = false;
    },

   /**
    * Reset the checkbox and color to the state defined in the layer JSON.
    */
    synchronize: function() {
        if (this.rule.hidden == "true") {
            this.checkbox.setValue(false);
        } else {
            this.checkbox.setValue(true);
        }
      
        // set icon color back to most recent color if necessary
        if (this.unappliedColorChg) {
            // change color in legend
            var symbolizer = this.rule.symbolizers[this.rule.symbolizers.length -1]; //This uses the last symbolizer
            var legend_graphic = "";
            var size = 0;
            var graphic = symbolizer.graphic; //This uses the last symbolizer
            (graphic.size <= LEGEND_ICON_SIZE-1) ? size = graphic.size : size = LEGEND_ICON_SIZE-1;
          
            var theLegendGraphic = this.getLegendGraphic(symbolizer.type, size, this.mostRecentColor, graphic);
            var iconImg = this.layer.name + "_" + this.rule.name.split(' ').join('') + '_IconImg';
          
            document.getElementById(iconImg).src = theLegendGraphic;
        
            // change color in symbolizer also 
            this.applyColorChange(symbolizer, this.mostRecentColor, graphic);

            this.unappliedColorChg = false;
        }
    },

   /**
    * Return the html for the printed legend that was generated in buildLegend
    */
   getPrintHtml: function() {
      if (this.rule.hidden == "true") {
         return "";
      } else {
         return this.printhtml;
      }
   },
   
   /**
    * Changes color in symbolizer -- used when map redrawn
    */
    applyColorChange: function(symbolizer, color, graphic) {
        switch (symbolizer.type) {
              case "Point":
                  if (graphic.external_graphic_url == undefined) { 
                      graphic.fill = color;
                  }
                  break;
            
              case "Polygon":
                  symbolizer.fill = color;
                  break;
                
              case "Line":
                  symbolizer.strokes[0].color = color;
                  break;
                  
              default:
			      alert("Unsupported Symbolizer type " + symbolizer.type + " encountered (Legend.js)");
                  break;
          }  
   
    },
   
   /**
    * Returns the graphic image (really a url) to be placed in the legend
    */
   getLegendGraphic: function(symbolizerType, size, color, graphic) {
        var theLegendGraphic;
    
        switch (symbolizerType) {
            case "Point":
                // If this is a point symbolizer with no external graphic defined
                if (graphic.external_graphic_url == undefined) {
                    //draw the symbol that will be used in the legend
                    theLegendGraphic = Drupal.settings.freebase_path + "/assets/php/icon.php?" +
				       "shape=" + graphic.graphic_mark.toLowerCase()+
				       "&s=" + size+
				       "&w=" + LEGEND_ICON_SIZE+
        			   "&h=" + LEGEND_ICON_SIZE+
        			   "&r=" + HexToR(color)+
				       "&g=" + HexToG(color)+
        			   "&b=" + HexToB(color);
                } else {
                    // if there is an external graphic defined
                    theLegendGraphic = graphic.external_graphic_url;
                }
                break;
            
            case "Polygon":
                theLegendGraphic = Drupal.settings.freebase_path + "/assets/php/icon.php?" +
                           "shape=square" +
                           "&w=" + size +
                           "&h=" + size +
                           "&r=" + HexToR(color)+
                           "&g=" + HexToG(color)+
                           "&b=" + HexToB(color);
                break;
                
             case "Line":
                 theLegendGraphic = Drupal.settings.freebase_path + "/assets/php/icon.php?" +
                       "shape=line" +
                       "&w=" + size +
                       "&h=" + size +
                       "&r=" + HexToR(color)+
                       "&g=" + HexToG(color)+
                       "&b=" + HexToB(color);
                 break;
                 
             default:
			     alert("Unsupported Symbolizer type " + symbolizerType + " encountered (Legend.js)");
			     break;
        }
        
        return theLegendGraphic;
   }      
});
