/**********************************************************************************************
 * JAFFA - Java Application Framework For All - Copyright (C) 2008 JAFFA Development Group
 *
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License (version 2.1 any later).
 *
 * See http://jaffa.sourceforge.net/site/legal.html for more details.
 *********************************************************************************************/

/** Based on Original Work found at http://extjs.com/forum/showthread.php?p=203828#post203828
 *
 * @author chander, PaulE
 * @TODO - Don't allow a field to be grouped by, if it is not sortable in the column model!!! 
 */
Ext.namespace("Ext.ux.grid");

Ext.ux.grid.MultiGroupingGrid = function(config) {
    config = config||{};
    
    // Cache the orignal column model, before state is applied
    if(config.cm)
      this.origColModel = Ext.ux.clone(config.cm.config);
    else if(config.colModel)
      this.origColModel = Ext.ux.clone(config.colModel.config);
      
    config.tbar = [{
       xtype:'tbtext'
      ,text:this.emptyToolbarText
    },{
       xtype:'tbfill'
      ,noDelete:true
    },{
       xtype:'tbbutton'
      ,text:'Options'
      ,noDelete:true
      ,menu:{
         items: [{
           text:'Restore Default Columns',
           scope: this,
           disabled: !this.origColModel,
           handler: function() {
             this.getColumnModel().setConfig(this.origColModel,false);
             this.saveState();
             return true;
           }
         },{
           text:'Show Group Columns'
          ,checked: !config.view.hideGroupedColumn
          ,scope:this
          ,checkHandler: function (item, checked) {
             this.view.hideGroupedColumn = !checked;
             this.view.refresh(true);
           }
         },{
           text: 'Clear Filters' // Labels.get('label.jaffa.jaffaRIA.jaffa.finder.grid.deactivateFilters')
          ,scope: this
          ,handler: function () {
             //TODO use the clearFilters() method!
             this.plugins.filters.each(function(flt) {
               flt.setActive(false); 
             });
           }  
         }]     
       }
    }];
    Ext.ux.grid.MultiGroupingGrid.superclass.constructor.call(this, config);
    //console.debug("Create MultiGroupingGrid",config);
};
Ext.extend(Ext.ux.grid.MultiGroupingGrid, Ext.grid.GridPanel, {

   initComponent : function(){
     //console.debug("MultiGroupingGrid.initComponent",this);
     Ext.ux.grid.MultiGroupingGrid.superclass.initComponent.call(this);
     
     // Initialise DragZone
     this.on("render", this.setUpDragging, this);
   }
   
   /** @cfg emptyToolbarText String to display on tool bar when there are no groups
    */   
  ,emptyToolbarText : "Drop Columns Here To Group" 
   /** Extend basic version so the Grouping Columns State is remebered
    */   
  ,getState : function(){
     var s = Ext.ux.grid.MultiGroupingGrid.superclass.getState.call(this);
     s.groupFields = this.store.getGroupState();
     return s;
   }
   
   /** Extend basic version so the Grouping Columns State is applied
    */   
  ,applyState : function(state){
     Ext.ux.grid.MultiGroupingGrid.superclass.applyState.call(this,state);
     if(state.groupFields) {
        this.store.groupBy(state.groupFields,true);
        //console.debug("Grid.applyState: Groups=",state.groupFields);
     }
   }  
    
  ,setUpDragging: function() {
     //console.debug("SetUpDragging", this);
     this.dragZone = new Ext.dd.DragZone(this.getTopToolbar().getEl(), {
       ddGroup:"grid-body" + this.getGridEl().id //FIXME - does this need to be unique to support multiple independant panels on the same page
      ,panel:this 
      ,scroll:false
       // @todo - docs
      ,onInitDrag : function(e) {
         // alert('init');
         var clone = this.dragData.ddel;
         clone.id = Ext.id('ven'); //FIXME??
         // clone.class='x-btn button';
         this.proxy.update(clone);
         return true;
       }

       // @todo - docs
      ,getDragData: function(e) {
         var target = Ext.get(e.getTarget().id);
         //console.debug("DragZone: ",e,target);
         if(!target || target.hasClass('x-toolbar x-small-editor')) {
           return false;
         }
           
         d = e.getTarget().cloneNode(true);
         d.id = Ext.id();
         //console.debug("getDragData",this, target);
           
         this.dragData = {
           repairXY: Ext.fly(target).getXY(),
           ddel: d,
           btn:e.getTarget()
         };
         return this.dragData;
       }

       //Provide coordinates for the proxy to slide back to on failed drag.
       //This is the original XY coordinates of the draggable element.
      ,getRepairXY: function() {
         return this.dragData.repairXY;
       }
     });
     
     // This is the target when columns are dropped onto the toolbar (ie added to the group)
     this.dropTarget2s = new Ext.dd.DropTarget(this.getTopToolbar().getEl(), {
       ddGroup: "gridHeader" + this.getGridEl().id
      ,panel:this 
      ,notifyDrop: function(dd, e, data) {
         if (this.panel.getColumnModel().config[this.panel.getView().getCellIndex(data.header)].groupable) {
             //console.debug("Adding Filter", data);
             var btname = this.panel.getColumnModel().getDataIndex(this.panel.getView().getCellIndex(data.header));
             this.panel.store.groupBy(btname);
             return true;
         }
         else {
             return false;
         }
       }
       ,notifyOver: function(dd,e,data){
         if (this.panel.getColumnModel().config[this.panel.getView().getCellIndex(data.header)].groupable) {
             return this.dropAllowed;
         }
         else {
             return this.dropNotAllowed;
         }
       }
     });

     // This is the target when columns are dropped onto the grid (ie removed from the group)
     this.dropTarget22s = new Ext.dd.DropTarget(this.getView().el.dom.childNodes[0].childNodes[1], {
       ddGroup: "grid-body" + this.getGridEl().id  //FIXME - does this need to be unique to support multiple independant panels on the same page
      ,panel:this 
      ,notifyDrop: function(dd, e, data) {
         var txt = Ext.get(data.btn).dom.innerHTML;
         var tb = this.panel.getTopToolbar();
         //console.debug("Removing Filter", txt);
         var bidx = tb.items.findIndexBy(function(b) {
           //console.debug("Match button ",b.text);
           return b.text==txt;
         },this);
         //console.debug("Found matching button", bidx);
         if(bidx<0) return; // Error!
         var fld = tb.items.get(bidx).fieldName;
         
         // Remove from toolbar
         Ext.removeNode(Ext.getDom(tb.items.get(bidx).id));
         if(bidx>0) Ext.removeNode(Ext.getDom(tb.items.get(bidx-1).id));;

         //console.debug("Remove button", fld);
         //console.dir(button);
         var cidx=this.panel.view.cm.findColumnIndex(fld);
         
         if(cidx<0)
           //console.error("Can't find column for field ", fld);
         
         this.panel.view.cm.setHidden(cidx, false);

         //Ext.removeNode(Ext.getDom(data.btn.id));

         // Remove this group from the groupField array
         // @todo - replace with method on store
         // this.panel.store.removeGroupField(fld);
         var temp=[];
         for(var i=this.panel.store.groupField.length-1;i>=0;i--) {
           if(this.panel.store.groupField[i]==fld) {
             this.panel.store.groupField.pop();
             break;
           }
           temp.push(this.panel.store.groupField[i]);
           this.panel.store.groupField.pop();
         }

         for(var i=temp.length-1;i>=0;i--) {
           this.panel.store.groupField.push(temp[i]);
         }

         if(this.panel.store.groupField.length==0)
           this.panel.store.groupField=false;

         this.panel.store.fireEvent('datachanged', this);
         return true;
       }
     }); 
   }

  ,buildFilters: function (columns, record) {
     //console.debug("Grid.buildFilters: Created Filters from ", columns, record);
     var config = [];
     for(var i=0;i<columns.length;i++) {
       var col = columns[i];
       var meta = record.getField(col.dataIndex);
       //console.debug("Meta Data For ", col.dataIndex, meta)
       if(meta && (meta.filter || meta.filterFieldName)) {
         var dt = meta.dataType || 'string';
         if (dt=='int' || dt=='long' || dt=='float' || dt=='double')
           dt=='numeric';
         else if (dt=='dateonly' || dt=='datetime')
           dt='date';
         //FIXME pass caseType on this filter definition, so it can be applied to the filter field  
         var f = {dataIndex:col.dataIndex, type:dt, paramName:col.filterFieldName};
         config[config.length] = f;
       }
     }
     //console.debug("Grid.buildFilters: Created Filters for ", config);
     if(config.length==0)
       return null;
     else  
       return new Ext.ux.grid.GridFilters({filters:config, local:false});
   }

  ,buildColumnModel: function (columns, record) {
     var config = [];
     for(var i=0;i<columns.length;i++) {
       var col = columns[i];
       var meta = record.getField(col.dataIndex);
       var cm = Ext.apply({},col);
       if(meta) {
         // Apply stuff from the Record's Meta Data
         if(!cm.hidden && meta.hidden==true) cm.hidden = true;   
         if(!cm.header && meta.label) cm.header = meta.label;
         if(!cm.renderer && meta.renderer) cm.renderer = meta.renderer;
         cm.sortable=(meta.sortable===true || (meta.sortFieldName&&meta.sortFieldName!=''));

         // Apply more metadata from associated ClassMetaData
         var mc = meta.metaClass || record.defaultMetaClass;
         var mfn = (meta.mapping||col.dataIndex).match(/.*\b(\w+)$/)[1];
         var mf = ClassMetaData[mc]?ClassMetaData[mc].fields[mfn]:undefined;
         if(!mf) mf = ClassMetaData[mc]?ClassMetaData[mc].fields[col.dataIndex]:undefined;
         //console.debug("Meta Class=",mc,ClassMetaData[mc],', dataIndex=',col.dataIndex,', mapping=',meta.mapping,', mfn=',mfn,', mf=',mf,', meta=',meta);
         if(mf) {
           // Default the header text
           if(!cm.header && mf.label) cm.header=mf.label;
           // Default the column width
           if(!cm.width) {
             if(mf.maxLength) cm.width = Math.min(Math.max(mf.maxLength,5),40)*8;
             else if(mf.type) {
               if(mf.type=='dateonly') cm.width=100;
               else if(mf.type=='datetime') cm.width=140;
               else if(mf.type=='boolean') cm.width=50;
             }
           }
           // Default the alignment
           if(!cm.align && mf.type && (mf.type=='float'||mf.type=='int'))
             cm.align = 'right';
           // Default standard renderers  
           if(!cm.renderer && mf.type) {
             if(mf.type=='dateonly') cm.renderer = Ext.util.Format.dateRenderer();
             else if(mf.type=='datetime') cm.renderer = Ext.util.Format.dateTimeRenderer();
           }
           if(mf.hidden==true) cm.hidden = true;   
         }
       }  
       if(!cm.header) cm.header = col.dataIndex;
       cm.groupable = (cm.groupable==true || cm.sortable==true);
       config[config.length] = cm;
       //console.debug("Grid.buildColumnModel: Width", cm.dataIndex, cm.width);

     }
     //console.debug("Grid.buildColumnModel: Created Columns for ", config);
     return new Ext.grid.ColumnModel(config);
  }

});

/**
 * @class Ext.ux.grid.MultiGroupingPagingStore
 * @extends Ext.ux.grid.MultiGroupingStore
 * A specialized {@link Ext.data.Store} that allows data to be appended a page at
 * a time as the user scrolls through. It is based on performing server-side sorting
 * and grouping and should be used in conjunction with a {@link Ext.ux.grid.MultiGroupPagingGrid}  
 * @constructor
 * Create a new MultiGroupingPagingStore
 * @param {Object} config The config object
 * 
 * @author PaulE  
 */
Ext.ux.grid.MultiGroupingPagingGrid = Ext.extend(Ext.ux.grid.MultiGroupingGrid, {

  /** When creating the store, register an internal callback for post load processing
   */  
    constructor: function(config) {
      config = config||{};
      config.bbar = [].concat(config.bbar);
      config.bbar = config.bbar.concat([
        {xtype:'tbfill'}
       ,{xtype:'tbtext',id:'counter', text: '? of ?'}
       ,{xtype:'tbspacer'}
       ,{xtype:'tbbutton',id:'loading',hidden: true,iconCls: "x-tbar-loading"}
       ,{xtype:'tbseparator'}
       ,{xtype:'tbbutton',id:'more',text: 'More...',handler: function() { this.store.loadMore(false); }, scope: this}
      ]);
      
      Ext.ux.grid.MultiGroupingPagingGrid.superclass.constructor.apply(this, arguments);
      
      // Create Event that asks for more data when we scroll to the end
      this.on("bodyscroll",  function() {
            var s = this.view.scroller.dom;
            if( (s.offsetHeight+s.scrollTop+5 > s.scrollHeight) && !this.isLoading) {
              //console.debug("Grid.on.bodyscroll: Get more...");
              this.store.loadMore(false);
            }  
      }, this);
      
      // When the grid start loading, display a loading icon    
      this.store.on("beforeload", function(store,o) {
        if(this.isLoading) {
          //console.debug("Store.on.beforeload: Reject Load, one is in progress");
          return false;
        }
        this.isLoading = true;
        if(this.rendered) {
          this.barLoading.show();
        }
        //console.debug("Store.on.beforeload: options=",o, this);
        return true;
      }, this);
      
      // When loading has finished, disable the loading icon, and update the row count  
      this.store.on("load", function() {
        delete this.isLoading;
        if(this.rendered) {
          this.barLoading.hide();
          //console.debug("Store.on.load: Finished loading.. ",this.store.totalCount);
          this.barCounter.getEl().innerHTML = "Showing " + this.store.getCount()+' of ' +
                (this.store.totalCount?this.store.totalCount:'?');
          if(this.store.totalCount)
            this.barMore.disable();
          else
            this.barMore.enable();  
        }
        return true;
      }, this);

      // When a loading error occurs, disable the loading icon and display error  
      this.store.on("loadexception", function(store, e) {
        //console.debug("Store.loadexception.Event:",arguments);
        delete this.isLoading;
        if(this.rendered) {
          this.barLoading.hide();
        }
        if(e)
          Ext.Msg.show({
            title:'Show Details',
            msg: "Error Loading Records - " + e,
            buttons: Ext.Msg.OK,
            icon: Ext.MessageBox.ERROR
          });
        return false;
      }, this);

      // As the default onLoad to refocus on the first row has been disabled,
      // This has been added so if a load does happen, and its an initial load
      // it refocuses. If this is a refresh caused by a sort/group or a new page
      // of data being loaded, it does not refocus  
      this.store.on("load", function(r,o) {
         if(o&&o.initial==true)
           Ext.ux.grid.MultiGroupingView.superclass.onLoad.call(this);        
      }, this.view);
    }
    
    // private
   ,onRender : function(ct, position){
        Ext.ux.grid.MultiGroupingPagingGrid.superclass.onRender.call(this, ct, position);
        var bb=this.getBottomToolbar();
        this.barCounter = bb.items.itemAt(bb.items.length-5);
        this.barMore = bb.items.itemAt(bb.items.length-1);
        this.barLoading = bb.items.itemAt(bb.items.length-3);
    }
});
