Ext.namespace('Zarafa.common.rules.ui');

/**
 * @class Zarafa.common.rules.ui.RulesGrid
 * @extends Zarafa.common.ui.grid.GridPanel
 * @xtype zarafa.rulesgrid
 *
 * {@link Zarafa.common.rules.ui.RulesGrid RulesGrid} will be used to display
 * rules information of the current user.
 */
Zarafa.common.rules.ui.RulesGrid = Ext.extend(Zarafa.common.ui.grid.GridPanel, {
	/**
	 * @constructor
	 * @param {Object} config Configuration structure
	 */
	constructor : function(config)
	{
		config = config || {};

		Ext.applyIf(config, {
			xtype : 'zarafa.rulesgrid',
			cls : 'k-rulesgrid',
			border : false,
			enableHdMenu : false,
			viewConfig : {
				forceFit : true,
				emptyText : '<div class=\'emptytext\'>' + _('No rule exists') + '</div>'
			},
			loadMask : this.initLoadMask(),
			sm : new Ext.grid.RowSelectionModel({
				singleSelect : true
			}),
			columns : this.initColumnModel(),
			listeners : {
				rowdblclick : this.onRowDblClick,
				scope : this
			},
			tbar : this.initToolbar()
		});

		Zarafa.common.rules.ui.RulesGrid.superclass.constructor.call(this, config);
	},

	/**
	 * initialize events for the grid panel.
	 * @private
	 */
	initEvents : function()
	{
		Zarafa.common.rules.ui.RulesGrid.superclass.initEvents.call(this);

		// register event to enable/disable buttons based on selection
		this.mon(this.getSelectionModel(), 'selectionchange', this.onGridSelectionChange, this);
	},

	/**
	 * Creates a {@link Ext.Toolbar} config object to be used with {@link #tbar}.
	 * @return {Ext.Toolbar} toolbar config object
	 * @private
	 */
	initToolbar : function()
	{
		return [{
			xtype : 'button',
			text : _('New'),
			handler : this.onRuleAdd,
			ref : '../addButton',
			scope : this
		}, {
			xtype : 'button',
			text : _('Delete'),
			disabled : true,
			ref : '../removeButton',
			handler : this.onRuleRemove,
			scope : this
		}, {
			xtype : 'button',
			text : _('Edit'),
			disabled : true,
			ref : '../editButton',
			handler : this.onRuleEdit,
			scope : this
		}, {
			xtype : 'button',
			iconCls : 'zarafa-rules-sequence-up',
			disabled : true,
			ref : '../upButton',
			handler : this.onRuleSequenceUp,
			scope : this
		}, {
			xtype : 'button',
			iconCls : 'zarafa-rules-sequence-down',
			disabled : true,
			ref : '../downButton',
			handler : this.onRuleSequenceDown,
			scope : this
		}];
	},

	/**
	 * Creates a column model object, used in {@link #colModel} config
	 * @return {Ext.grid.ColumnModel} column model object
	 * @private
	 */
	initColumnModel : function()
	{
		return [{
			xtype : 'checkcolumn',
			dataIndex : 'rule_state',
			header : _('Active'),
			fixed : true,
			width : 60,
			sortable : false,
			// override renderer to show active state of rule
			renderer : this.ruleStateRenderer.createDelegate(this),
			// override processEvent so we can save the change in the record
			processEvent : this.onRuleStateColumnProcessEvent.createDelegate(this)
		}, {
			dataIndex : 'rule_name',
			header : _('Rule'),
			sortable : false,
			renderer : Zarafa.common.ui.grid.Renderers.text
		}];
	},

	/**
	 * Render the cell as Rule state checkbox, function will call {@link Ext.ux.grid.CheckColumn#renderer CheckColumn#renderer}
	 * after setting boolean value for rule state.
	 *
	 * @param {Object} value The data value for the cell.
	 * @param {Object} p An object with metadata
	 * @param {Ext.data.record} record The {Ext.data.Record} from which the data was extracted.
	 */
	ruleStateRenderer : function(value, p, record)
	{
		value = ((value & Zarafa.core.mapi.RuleStates.ST_ENABLED) === Zarafa.core.mapi.RuleStates.ST_ENABLED);

		return Ext.ux.grid.CheckColumn.prototype.renderer.apply(this, arguments);
	},

	/**
	 * Function will be called when user toggles active/inactive state of rule.
	 * This will update the corresponding property to indicate active state of the rule.
	 * @param {String} name name of the event which triggered this function
	 * @param {Ext.EventObject} e event object
	 * @param {Zarafa.common.ui.grid.GridPanel} grid grid panel which holds this {@link Zarafa.common.ui.grid.ColumnModel ColumnModel}
	 * @param {Number} rowIndex index of the row which is toggled
	 * @param {Number} colIndex index of the column which is toggled
	 * @private
	 */
	onRuleStateColumnProcessEvent : function(name, e, grid, rowIndex, colIndex)
	{
		if(name === 'mousedown') {
			var record = grid.getStore().getAt(rowIndex);
			var rule_state = record.get('rule_state');

			if ((rule_state & Zarafa.core.mapi.RuleStates.ST_ENABLED) === Zarafa.core.mapi.RuleStates.ST_ENABLED) {
				record.set('rule_state', rule_state & ~Zarafa.core.mapi.RuleStates.ST_ENABLED);
			} else {
				record.set('rule_state', rule_state | Zarafa.core.mapi.RuleStates.ST_ENABLED);
			}
		}
	},

	/**
	 * Initialize the {@link Ext.grid.GridPanel.loadMask} field
	 * @return {Ext.LoadMask} The configuration object for {@link Ext.LoadMask}
	 * @private
	 */
	initLoadMask : function()
	{
		return {
			msg : _('Loading rules') + '...'
		};
	},

	/**
	 * Event handler for the {@link #viewready} event.
	 * This will {@link Ext.grid.RowSelectionModel#selectFirstRow select the first row} in the grid.
	 * @private
	 */
	onViewReady : function()
	{
		Zarafa.common.rules.ui.RulesGrid.superclass.onViewReady.apply(this, arguments);
		this.getSelectionModel().selectFirstRow();
	},

	/**
	 * Called when {@link Ext.grid.GridPanel#store store} on the {@link #field} is
	 * loading new data.
	 * This will {@link Ext.grid.RowSelectionModel#selectFirstRow select the first row} in the grid.
	 *
	 * @param {Zarafa.core.data.MAPIStore} store The store which being loaded.
	 * @param {Ext.data.Record[]} records The records which have been loaded from the store
	 * @param {Object} options The loading options that were specified (see {@link Ext.data.Store#load load} for details)
	 * @private
	 */
	onStoreLoad : function(store, records, options)
	{
		Zarafa.common.rules.ui.RulesGrid.superclass.onStoreLoad.apply(this, arguments);
		if (!this.getSelectionModel().hasSelection()) {
			this.getSelectionModel().selectFirstRow();
		}
	},

	/**
	 * Event handler which is fired when the {@link Zarafa.common.rules.ui.RulesGrid RulesGrid} is double clicked.
	 * @private
	 */
	onRowDblClick : function(grid, rowIndex)
	{
		// open rule edit dialog
		Zarafa.common.Actions.openRulesEditContent(grid.getStore().getAt(rowIndex));
	},

	/**
	 * Event handler will be called when selection in {@link Zarafa.common.rules.ui.RulesGrid RulesGrid}
	 * has been changed
	 * @param {Ext.grid.RowSelectionModel} selectionModel selection model that fired the event
	 * @private
	 */
	onGridSelectionChange : function(selectionModel)
	{
		var noSelection = (selectionModel.hasSelection() === false);

		this.removeButton.setDisabled(noSelection);
		this.editButton.setDisabled(noSelection);

		this.upButton.setDisabled(!selectionModel.hasPrevious());
		this.downButton.setDisabled(!selectionModel.hasNext());
	},

	/**
	 * Handler function will be called when user clicks on 'Add' button
	 * @private
	 */
	onRuleAdd : function()
	{
		// open rule edit dialog
		var ruleRecord = Zarafa.core.data.RecordFactory.createRecordObjectByCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_RULE);
		this.getStore().add(ruleRecord);

		Zarafa.common.Actions.openRulesEditContent(ruleRecord, { removeRecordOnCancel : true });
	},

	/**
	 * Handler function will be called when user clicks on 'Remove' button.
	 * @private
	 */
	onRuleRemove : function()
	{
		var store = this.getStore();
		var sm = this.getSelectionModel();

		var ruleRecord = sm.getSelected();
		if (!ruleRecord) {
			container.getNotifier().notify('error.rules', _('Error'), _('Please select a rule.'));
			return;
		}

		// remove rule from store
		store.remove(ruleRecord);
	},

	/**
	 * Handler function will be called when user clicks on 'Edit' button.
	 * @private
	 */
	onRuleEdit : function()
	{
		var ruleRecord = this.getSelectionModel().getSelected();
		if (!ruleRecord) {
			container.getNotifier().notify('error.rules', _('Error'), _('Please select a rule.'));
			return;
		}

		// open rule edit dialog
		Zarafa.common.Actions.openRulesEditContent(ruleRecord);
	},

	/**
	 * Handler function will be called when user clicks on 'Up' button
	 * This will determine which rules to swap and call {@link #swapRules}.
	 * @private
	 */
	onRuleSequenceUp : function()
	{
		var store = this.getStore();
		var sm = this.getSelectionModel();
		var rule = sm.getSelected();

		if (!rule) {
			container.getNotifier().notify('error.rules', _('Error'), _('Please select a rule.'));
			return;
		}

		if (!sm.hasPrevious()) {
			container.getNotifier().notify('error.rules', _('Error'), _('Rule already has highest priority.'));
			return;
		}

		// Start looking for the first sequence number which is lower then
		// the current sequence number. Note that we want the rule_sequence
		// which is closest to the current rule_sequence, hence the rule:
		//    rule.get('rule_sequence') > record.get('rule_sequence') > swapRule.get('rule_sequence')
		var swapRule;
		store.each(function(record) {
			if (rule.get('rule_sequence') > record.get('rule_sequence')) {
				if (!swapRule || record.get('rule_sequence') > swapRule.get('rule_sequence')) {
					swapRule = record;
				}
			}
		}, this);

		this.swapRules(rule, swapRule);
	},

	/**
	 * Handler function will be called when user clicks on 'Down' button
	 * This will determine which rules to swap and call {@link #swapRules}.
	 * @private
	 */
	onRuleSequenceDown : function()
	{
		var store = this.getStore();
		var sm = this.getSelectionModel();
		var rule = sm.getSelected();

		if (!rule) {
			container.getNotifier().notify('error.rules', _('Error'), _('Please select a rule.'));
			return;
		}

		if (!sm.hasNext()) {
			container.getNotifier().notify('error.rules', _('Error'), _('Rule already has lowest priority.'));
			return;
		}

		// Start looking for the first sequence number which is higher then
		// the current sequence number. Note that we want the rule_sequence
		// which is closest to the current rule_sequence, hence the rule:
		//    rule.get('rule_sequence') < record.get('rule_sequence') < swapRule.get('rule_sequence')
		var swapRule;
		store.each(function(record) {
			if (rule.get('rule_sequence') < record.get('rule_sequence')) {
				if (!swapRule || record.get('rule_sequence') < swapRule.get('rule_sequence')) {
					swapRule = record;
				}
			}
		}, this);

		this.swapRules(rule, swapRule);
	},

	/**
	 * Swap to rules by changing the 'rule_sequence' property
	 * for both rules, and {@link Ext.data.Store#sort sort}
	 * the {@link #store}.
	 * @param {Zarafa.common.rules.data.RulesRecord} a The first rule
	 * @param {Zarafa.common.rules.data.RulesRecord} b The second rule
	 * @private
	 */
	swapRules : function(a, b)
	{
		var aSeq = a.get('rule_sequence');
		var bSeq = b.get('rule_sequence');

		// Swap the 2 rules
		a.set('rule_sequence', bSeq);
		b.set('rule_sequence', aSeq);

		// Reapply the sorting, this will update the UI
		this.getStore().sort('rule_sequence', 'ASC');

		// Update the 'up'/'down' button
		var sm = this.getSelectionModel();
		this.upButton.setDisabled(!sm.hasPrevious());
		this.downButton.setDisabled(!sm.hasNext());
	}
});

Ext.reg('zarafa.rulesgrid', Zarafa.common.rules.ui.RulesGrid);
