(function() {

   var util = YAHOO.util, lang = YAHOO.lang, Dom = util.Dom, Event = util.Event, msgs = inputEx.messages;

/**
 * Create an editable datatable
 * @class inputEx.widget.DataTable
 * @constructor
 * @param {Object} options Options:
 * <ul>
 *    <li>parentEl: DOMelement in which we have to insert the datatable</li>
 *
 *		<li>datasource (or datasourceConfig)</li>
 *    <li>datatableOpts: additionnal datatable options</li>
 *    <li>fields: inputEx fields</li>
 *    <li>dialogLabel: title of the dialog</li>
 *    <li>columnDefs: YUI datatable columnDefs</li>
 *
 *    <li>id: (optional, default is autogenerated) sets the id of the div wrapper around the widget</li>
 *    <li>allowInsert: adds the 'Insert' button (optional, default true)</li>
 *    <li>allowModify: default true</li>
 *    <li>allowDelete: default true</li>
 *    <li>showHideColumnsDlg: add a link to a dialog to show/hide columns</li>
 * 	<li>panelConfig: (optional) YUI's dialog panelConfig object</li>
 *
 * </ul>
 */
inputEx.widget.DataTable = function(options) {
   
   this.setOptions(options);
   
   this.render();
   
   this.initEvents();
	
};

inputEx.widget.DataTable.prototype = {
   
   /**
    * Set the options
    */
   setOptions: function(options) {

      this.options = {};
      this.options.id = options.id || Dom.generateId();
      this.options.parentEl = lang.isString(options.parentEl) ? Dom.get(options.parentEl) : options.parentEl;
      
		this.options.columnDefs = options.columnDefs;

      this.options.allowInsert = lang.isUndefined(options.allowInsert) ? true : options.allowInsert;
      this.options.allowModify = lang.isUndefined(options.allowModify) ? true : options.allowModify;
      this.options.allowDelete = lang.isUndefined(options.allowDelete) ? true : options.allowDelete; 
      
      this.options.showHideColumnsDlg = lang.isUndefined(options.showHideColumnsDlg) ? false : options.showHideColumnsDlg; 
      
      this.options.datasource = options.datasource;

		// Create a datasource if it does not exist, from the datasourceConfig Object
		if(!options.datasource && options.datasourceConfig) {
			var ds = new YAHOO.util.DataSource(options.datasourceConfig.url), fields = [];
			if(options.datasourceConfig.keys) {
				for ( var i = 0 ; i < options.datasourceConfig.keys.length ; i++ ) {
	         	fields.push({ key: options.datasourceConfig.keys[i] });
	      	}
			}
	      ds.responseType = options.datasourceConfig.responseType || YAHOO.util.DataSource.TYPE_JSON;
	      ds.responseSchema = options.datasourceConfig.responseSchema || { resultsList : "Result", fields : fields};
			this.options.datasource = ds;
		}

      this.options.datatableOpts = options.datatableOpts;
      this.options.fields = options.fields;

		this.options.dialogId = options.dialogId || null;
		this.options.dialogLabel = options.dialogLabel || "";
		
		this.options.panelConfig = options.panelConfig || {
			constraintoviewport: true, 
			underlay:"shadow", 
			close:true, 
			fixedcenter: true,
			visible:true, 
			draggable:true,
			modal: true
		};
   },
   
   
   /**
    * Init the events
    */
   initEvents: function() {
      
      // Call the rendering method when the container is available
      Event.onAvailable(this.options.id, this.renderDatatable, this, true);
      
      // Table options
      if(this.options.showHideColumnsDlg) {
         Event.addListener(this.tableOptions, 'click', this.showTableOptions, this, true);
      }

      /**
   	 * @event Event fired when an item is removed
   	 * @param {YAHOO.widget.Record} Removed record
   	 * @desc YAHOO custom event fired when an item is removed
   	 */
    	this.itemRemovedEvt = new util.CustomEvent('itemRemoved', this);

      /**
   	 * @event Event fired when an item is added
    	 * @param {YAHOO.widget.Record} Added record
   	 * @desc YAHOO custom event fired when an item is added
   	 */
    	this.itemAddedEvt = new util.CustomEvent('itemAdded', this);

      /**
   	 * @event Event fired when an item is modified
    	 * @param {YAHOO.widget.Record} Modified record
   	 * @desc YAHOO custom event fired when an item is modified
   	 */
    	this.itemModifiedEvt = new util.CustomEvent('itemModified', this);
   },
   
   /**
    * Render the main container only (not the datatable)
    */
   render: function() {
      
      /**
       * Main container 
       */
      this.element = inputEx.cn('div', {id: this.options.id });
      
      if(this.options.showHideColumnsDlg) {
         this.renderShowHideColumnsDlg();
      }

      // append it immediatly to the parent DOM element
      this.options.parentEl.appendChild(this.element);
      
   },
   
   
   /**
    * Render the datatable
    */
   renderDatatable: function() {
      
      var columndefs = this.setColumnDefs();

		/**
		 * YUI's datatable instance
		 */
      this.datatable = new YAHOO.widget.DataTable(this.element, columndefs, this.options.datasource, this.options.datatableOpts);
      this.datatable.subscribe('cellClickEvent', this._onCellClick, this, true);

		// Automatically set up the paginator
		if(this.options.datatableOpts && this.options.datatableOpts.paginator) {
			this.datatable.handleDataReturnPayload = function(oRequest, oResponse, oPayload) {
				if(oPayload) {
	        		oPayload.totalRecords = oResponse.meta.totalRecords;
				}
	        	return oPayload;
	    	};
		}
            
      // Insert button
      if ( this.options.allowInsert ){
         this.insertButton = inputEx.cn('input', {type:'button', value:msgs.insertItemText}, null, null);
         Event.addListener(this.insertButton, 'click', this.onInsertButton, this, true);
         this.options.parentEl.appendChild(this.insertButton);
      }

		
      // Set up editing flow
      var highlightEditableCell = function(oArgs) {
          var elCell = oArgs.target;
          if(Dom.hasClass(elCell, "yui-dt-editable") || Dom.hasClass(elCell,"yui-dt-col-delete") || Dom.hasClass(elCell,"yui-dt-col-modify") ) {
              this.highlightCell(elCell);
          }
      };
		
		// Locals
		this.datatable.set("MSG_LOADING", msgs.loadingText );
		this.datatable.set("MSG_EMPTY", msgs.emptyDataText );
		this.datatable.set("MSG_ERROR", msgs.errorDataText );

      this.datatable.subscribe("cellMouseoverEvent", highlightEditableCell);
      this.datatable.subscribe("cellMouseoutEvent", this.datatable.onEventUnhighlightCell);

   },

	/**
	 * Set the column definitions, create them if none from the fields, adds the modify and delete buttons
	 */
	setColumnDefs: function() {
		
		var columndefs = this.options.columnDefs || this.fieldsToColumndefs(this.options.fields);

    	// Adding modify column if we use form editing and if allowModify is true
      if(this.options.allowModify ) {
    	   columndefs = columndefs.concat([{
    	      key:'modify',
    	      label:' ',
    	      formatter:function(elCell) {
               elCell.innerHTML = msgs.modifyText;
               elCell.style.cursor = 'pointer';
            }
         }]);
      }
      
      // Adding delete column
      if(this.options.allowDelete) {
      	 columndefs = columndefs.concat([{
      	    key:'delete',
      	    label:' ',
      	    formatter:function(elCell) {
               elCell.innerHTML = msgs.deleteText;
               elCell.style.cursor = 'pointer';
            }
         }]);
      }
		
		return columndefs;
	},
	
   /**
    * Render the dialog for row edition
    */
   renderDialog: function() {
      
     var that = this;
      
     this.dialog = new inputEx.widget.Dialog({
				id: this.options.dialogId,
				inputExDef: {
				         type: 'form',
			            fields: this.options.fields,
			            buttons: [
			               {type: 'submit', value: msgs.saveText, onClick: function() { that.onDialogSave(); return false; /* prevent form submit */} },
			               {type: 'link', value: msgs.cancelText, onClick: function() { that.onDialogCancel(); } }
			            ]
				      },
				title: this.options.dialogLabel,
				panelConfig: this.options.panelConfig
		});
		
		// Add a listener on the closing button and hook it to onDialogCancel()
		YAHOO.util.Event.addListener(that.dialog.close,"click",function(){
			that.onDialogCancel();
		},that);
		
   },

	/**
    * When saving the Dialog
    */
   onDialogSave: function() {
		
		var newvalues, record;
		
	  	//Validate the Form
	  	if ( !this.dialog.getForm().validate() ) return ;
	   
		// Update the record
		if(!this.insertNewRecord){
						
			// Update the row
			newvalues = this.dialog.getValue();
			this.datatable.updateRow( this.selectedRecord , newvalues );

			// Get the new record
			record = this.datatable.getRecord(this.selectedRecord);
			
			// Fire the modify event
         this.itemModifiedEvt.fire(record);

		}
		// Adding new record
		else{
			// Insert a new row
	      this.datatable.addRow({});

			// Set the Selected Record
			var rowIndex = this.datatable.getRecordSet().getLength() - 1;
			this.selectedRecord = rowIndex;
			
			// Update the row
			newvalues = this.dialog.getValue();
			this.datatable.updateRow( this.selectedRecord , newvalues );
			
			// Get the new record
			record = this.datatable.getRecord(this.selectedRecord);
						
			// Fire the add event
         this.itemAddedEvt.fire(record);
		}
      
      this.dialog.hide();
   },

	/**
    * When canceling the Dialog
    */
	onDialogCancel: function(){
		this.insertNewRecord = false;
		this.dialog.hide();
	},

   
   /**
    * Handling cell click events
    */
   _onCellClick: function(ev,args) {
      var target = Event.getTarget(ev);
      var column = this.datatable.getColumn(target);      
      var rowIndex = this.datatable.getTrIndex(target);
      if (column.key == 'delete') {
         if (confirm(msgs.confirmDeletion)) {
            var record = this.datatable.getRecord(target);
            if(this.editingNewRecord) {
               this.editingNewRecord = false;
            }
            else {
               this.itemRemovedEvt.fire( record );
            }
            this.datatable.deleteRow(target);
            this.hideSubform();
         }
      }
      else if(column.key == 'modify') {
         this.onClickModify(rowIndex);
      } 
      else {				
      	this.onCellClick(ev,rowIndex);
      }
   },

   /**
    * Public cell click handler
    */
   onCellClick: function(ev, rowIndex) {

   },
   
   /**
    * Opens the Dialog to edit the row
    * Called when the user clicked on modify button
    */
   onClickModify: function(rowIndex) {

      if(!this.dialog) {
         this.renderDialog();
      }

      // NOT Inserting new record
		this.insertNewRecord = false;
		
		// Set the selected Record
		this.selectedRecord = rowIndex;
		
		// Get the selected Record
		var record = this.datatable.getRecord(this.selectedRecord);
		
		this.dialog.whenFormAvailable({
			fn: function() {
				this.dialog.setValue(record.getData());
				this.dialog.show();
			},
			scope: this
		});
		
	},
   
   /**
    * Insert button event handler
    */
   onInsertButton: function(e) {

      if(!this.dialog) {
         this.renderDialog();
      }
		
		// Inserting new record
		this.insertNewRecord = true;
		
		this.dialog.whenFormAvailable({
			fn: function() {
				this.dialog.getForm().clear();
				this.dialog.show();
			},
			scope: this
		});
		
   },
   
   /**
    * Remove the record that has not been saved
    */
   removeUnsavedRecord: function(record) {
      this.datatable.deleteRow(record);
   },
   
   /**
    * Cancel row edition
    */
   onCancelForm: function(e) {
      Event.stopEvent(e); 
      this.hideSubform();
      
      if(this.editingNewRecord) {
         this.removeUnsavedRecord();
         this.editingNewRecord = false;
      }
   },
   
   
   /**
    * Convert an inputEx fields definition to a DataTable columns definition
    */
   fieldsToColumndefs: function(fields) {
      var columndefs = [];
    	for(var i = 0 ; i < fields.length ; i++) {
    		columndefs.push( this.fieldToColumndef(fields[i]) );
    	}
    	return columndefs;
   },

   /**
    * Convert a single inputEx field definition to a DataTable column definition
    */
   fieldToColumndef: function(field) {
      
      var key, label, colmunDef;
      
      // Retro-compatibility with inputParms
      if (lang.isObject(field.inputParams)) {
         key = field.inputParams.name;
         label = field.inputParams.label;
      
      // New prefered way to set options of a field
      } else {
         key = field.name;
         label = field.label;
      }
      
      columnDef = {
         key: key,
         label: label,
         sortable: true, 
         resizeable: true
      };

      // Field formatter
      if(field.type == "date") {
      	columnDef.formatter = YAHOO.widget.DataTable.formatDate;
      }
		else if(field.type == "integer" || field.type == "number") {
			columnDef.formatter = YAHOO.widget.DataTable.formatNumber;
			/*columnDef.sortOptions = {
				defaultDir: "asc",
				sortFunction: // TODO: sort numbers !!!
			}*/
		}
      // TODO: other formatters
      return columnDef;
   },
   
   /**
    * Render the dialog (+link) to show/hide columns
    */
   renderShowHideColumnsDlg: function() {
      this.tableOptions = inputEx.cn('a', {href: '#'}, null, msgs.tableOptions);
      this.options.parentEl.appendChild(this.tableOptions);
      
      this.tableOptionsDlg = new YAHOO.widget.SimpleDialog( Dom.generateId(), {
              width: "30em",
		        visible: false,
		        modal: true,
		        buttons: [ 
				      { text:msgs.columnDialogCloseButton,  handler: function(e) { this.hide(); } }
              ],
              fixedcenter: true,
              constrainToViewport: true
	   });
	
		Dom.addClass(this.tableOptionsDlg.element.firstChild, "inputex-datatable-columnsDlg");
		
	   this.tableOptionsDlg.bodyId = Dom.generateId();
	   this.tableOptionsDlg.setHeader(msgs.columnDialogTitle);
	   this.tableOptionsDlg.setBody("<div id='"+this.tableOptionsDlg.bodyId+"'></div>");
	   this.tableOptionsDlg.render(document.body);
   },
   
   /**
    * Display the dialog to show/hide fields
    */
   showTableOptions: function(e) {
      
      Event.stopEvent(e);
      
      if(!this.noNewCols) {
          
          var that = this;
          var handleButtonClick = function(e, oSelf) {
              var sKey = this.get("name");
              if(this.get("value") === "Hide") {
                  // Hides a Column
                  that.datatable.hideColumn(sKey);
              }
              else {
                  // Shows a Column
                  that.datatable.showColumn(sKey);
              }
          };
          
           // Populate Dialog
           // Using a template to create elements for the SimpleDialog
           var allColumns = this.datatable.getColumnSet().keys;
           var elPicker = Dom.get(this.tableOptionsDlg.bodyId);
           
           var elTemplateCol = document.createElement("div");
           Dom.addClass(elTemplateCol, "dt-dlg-pickercol");
           var elTemplateKey = elTemplateCol.appendChild(document.createElement("span"));
           Dom.addClass(elTemplateKey, "dt-dlg-pickerkey");
           var elTemplateBtns = elTemplateCol.appendChild(document.createElement("span"));
           Dom.addClass(elTemplateBtns, "dt-dlg-pickerbtns");
           var onclickObj = {fn:handleButtonClick, obj:this, scope:false };
           
           // Create one section in the SimpleDialog for each Column
           var elColumn, elKey, elButton, oButtonGrp;
           for(var i=0,l=allColumns.length;i<l;i++) {
               var oColumn = allColumns[i];
               
               // Use the template
               elColumn = elTemplateCol.cloneNode(true);
               
               // Write the Column key
               elKey = elColumn.firstChild;
               elKey.innerHTML = (oColumn.label && oColumn.label !== "") ? oColumn.label : oColumn.getKey();
               
               if(oColumn.getKey() != "delete" && oColumn.getKey() != "modify") {
               
                  // Create a ButtonGroup
                  oButtonGrp = new YAHOO.widget.ButtonGroup({ 
                                  id: "buttongrp"+i, 
                                  name: oColumn.getKey(), 
                                  container: elKey.nextSibling
                  });
                  oButtonGrp.addButtons([
                      { label: msgs.showColumnButton, value: "Show", checked: ((!oColumn.hidden)), onclick: onclickObj},
                      { label: msgs.hideColumnButton, value: "Hide", checked: ((oColumn.hidden)), onclick: onclickObj}
                  ]);
                    
                  elPicker.appendChild(elColumn);
               
               }
           }
           this.noNewCols = true;
   	}
       this.tableOptionsDlg.show();
      
   }
   
};


msgs.saveText = "Save";
msgs.cancelText = "Cancel";
msgs.deleteText = "delete";
msgs.modifyText = "modify";
msgs.insertItemText = "Insert";
msgs.addButtonText = "Add";
msgs.loadingText = "Loading...";
msgs.emptyDataText = "No records found.";
msgs.errorDataText = "Data error.";
msgs.confirmDeletion = "Are you sure?";

msgs.tableOptions = "Table options";
msgs.showColumnButton = "Show";
msgs.hideColumnButton = "Hide";
msgs.columnDialogTitle = "Choose which columns you would like to see";
msgs.columnDialogCloseButton = "Close";

})();