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

/**
 * @class Zarafa.common.ui.grid.MapiMessageGrid
 * @extends Zarafa.common.ui.grid.GridPanel
 * @xtype zarafa.mapimessagegrid
 *
 * Grid for MAPI messages, like Notes, Mails, Appointments, Tasks, Contacts.
 * To handle specific action that are performed on MAPI messages grid, like keyboard controls.
 */
Zarafa.common.ui.grid.MapiMessageGrid = Ext.extend(Zarafa.common.ui.grid.GridPanel, {
	/*
	 * @TODO :
	 * This grid is created only to handle keyboard events easily for
	 * Notes, Mails, Appointments, Tasks, Contacts.
	 * This allows 'grid.mapimessage' mapid to register common key events on
	 * above grids and handle them without code duplication.
	 * Grids like addressbookgrid, attachitemgrid, rulesgrid, remindergrid doesn't
	 * have common key mapping but extends Zarafa.common.ui.grid.GridPanel.
	 */

	/**
	 * The tooltip that will be used to show the full name of truncated categories
	 * @property
	 * @type {Zarafa.common.categories.ui.Tooltip}
	 */
	categoryTooltip : null,

	/**
	 * Initialize event handlers
	 * @private
	 */
	initEvents : function()
	{
		Zarafa.common.ui.grid.MapiMessageGrid.superclass.initEvents.call(this);

		this.on({
			'afterrender': this.onRenderGrid,
			'cellcontextmenu': this.onCellContextMenu,
			scope : this
		});

		// Only add the event listeners that resize the category labels when
		// we actually have a category column
		var columnModel = this.getColumnModel();
		if ( columnModel.getIndexById('categories') >= 0 ){
			this.on({
				'viewready': this.resizeCategoryLabels,
				'resize': this.resizeCategoryLabels,
				'columnresize': this.resizeCategoryLabels,
				scope : this
			});

			this.mon(this.store, 'load', this.resizeCategoryLabels, this);
		}
	},

	/**
	 * Event handler for the afterrender event of this panel. Creates a tooltip
	 * that will be used to show the full name of truncated categories.
	 * @param (Zarafa.mail.ui.MailGrid) grid this
	 */
	onRenderGrid : function(grid)
	{
		// Create a tooltip that will be used for truncated category labels
	    this.categoryTooltip = new Zarafa.common.categories.ui.Tooltip({
	        target: grid.getView().mainBody
	    });
	},

	/**
	 * Event handler which is triggered when the user opens the context menu.
	 *
	 * There are some selection rules regarding the context menu. If no rows where
	 * selected, the row on which the context menu was requested will be marked
	 * as selected. If there have been rows selected, but the context menu was
	 * requested on a different row, then the old selection is lost, and the new
	 * row will be selected. If the row on which the context menu was selected is
	 * part of the previously selected rows, then the context menu will be applied
	 * to all selected rows.
	 *
	 * @param {Zarafa.mail.ui.MailGrid} grid The grid which was right clicked
	 * @param {Number} rowIndex The index number of the row which was right clicked
	 * @param {Number} cellIndex The index number of the column which was right clicked
	 * @param {Ext.EventObject} event The event structure
	 * @private
	 */
	onCellContextMenu : function(grid, rowIndex, cellIndex, event)
	{
		var selectionModel = this.getSelectionModel();
		var columnModel = this.getColumnModel();

		if ( !selectionModel.isSelected(rowIndex) ) {
			selectionModel.selectRow(rowIndex);
		}

		// Take into account that the function onRowBodyContextMenu passes -1 as the column index.
		var dataIndex = (cellIndex >= 0) ? columnModel.getDataIndex(cellIndex) : undefined;
		var records = selectionModel.getSelections();

		switch (dataIndex) {
			case 'importance':
				Zarafa.core.data.UIFactory.openContextMenu(Zarafa.core.data.SharedComponentType['common.contextmenu.importance'], records, { position : event.getXY() });
				break;
			default:
				// If the click was on a category, we must open the category context menu
				var targetElement = Ext.get(event.target);
				if ( targetElement.hasClass('k-category-block') ){
					Zarafa.core.data.UIFactory.openContextMenu(Zarafa.core.data.SharedComponentType['common.contextmenu.category'], records, {
						category: targetElement.dom.textContent,
						position : event.getXY()
					});
					return;
				}

				Zarafa.core.data.UIFactory.openDefaultContextMenu(records, {
					position : event.getXY(),
					context : this.context,
					actsOnTodoListFolder : this.model.getDefaultFolder().isTodoListFolder()
				});
				break;
		}
	},

	/**
	 * Event handler for the viewready, resize, and columnresize events. Will
	 * resize the category labels in the category column (for the non-compact view)
	 */
	resizeCategoryLabels : function()
	{
		var columnModel = this.getColumnModel();
		var categoriesColIndex = columnModel.findColumnIndex('categories');

		// Only do the resizing when we are in the full view and the category
		// column is not hidden
		if ( categoriesColIndex === -1 || columnModel.isHidden(categoriesColIndex) ){
			return;
		}

		// Subtract the padding of the column
		var colWidth = columnModel.getColumnWidth(categoriesColIndex) - 16;

		var rows = this.getView().getRows();
		Ext.each(rows, function(row){
			var labels = row.querySelectorAll('.k-category-block');
			if ( Ext.isEmpty(labels[0]) ){
				return;
			}

			this.resizeCategoryLabelsInRow(row, labels, colWidth);
		}, this);
	},

	/**
	 * Calculates the width of the category labels in a row and will resize them if they
	 * are too large.
	 * @param {HtmlElement} row The container element of the row in the grid
	 * @param {Array} labels Array of HtmlElements that represent the category labels of
	 * the given row
	 * @param {Number} colWidth The width of the categories column in pixels
	 * @private
	 */
	resizeCategoryLabelsInRow : function(row, labels, colWidth)
	{
		var labelCount = labels.length;

		// Instead of hardcoding the margin and padding, we will read it once from the first label we find
		this.labelMarginRight = this.labelMarginRight || parseFloat(window.getComputedStyle(labels[0]).marginRight);
		this.labelPaddingLeftRight = this.labelPaddingLeftRight || parseFloat(window.getComputedStyle(labels[0]).paddingLeft) + parseFloat(window.getComputedStyle(labels[0]).paddingRight);

		// Calculate what the label width would be if we would divide the column width by the number of labels
		// (taking into acount the right-margin of a label)
		var labelWidth = (colWidth + this.labelMarginRight)/labelCount - this.labelMarginRight - this.labelPaddingLeftRight;

		// Some labels are smaller than the calculated label width, so we will divide the space that
		// is not used over the remaining labels
		var unusedSpace = 0;
		var smallLabels = [];
		Ext.each(labels, function(label, index){
			// Temporarily disable the max-width, so we can get the untruncated width of the label
			var maxWidth = label.style.maxWidth;
			label.style.maxWidth = 'none';
			var width = parseFloat(window.getComputedStyle(label).width);
			if ( width < labelWidth ){
				smallLabels.push(index);
				unusedSpace += labelWidth - width;
			}
			label.style.maxWidth = maxWidth;
		}, this);

		// Now we can recalculate the label width
		labelWidth = labelWidth + unusedSpace/(labelCount-smallLabels.length);

		// Finally apply the calculated label width to all labels that need it
		Ext.each(labels, function(label, index){
			if ( smallLabels.indexOf(index) > -1 ){
				label.style.maxWidth = '';
			} else {
				label.style.maxWidth = labelWidth + 'px';
			}
		}, this);

		// Note: There might still be labels smaller than the maxWidth, so there could be space left.
		// Fixing this iteratively would be too much of a performance decrease, so we will settle for
		// these 'pseudo' optimized label widths.
	}
});

Ext.reg('zarafa.mapimessagegrid', Zarafa.common.ui.grid.MapiMessageGrid);
