Ext.namespace('Zarafa.calendar.ui');

/**
 * @class Zarafa.calendar.ui.CalendarTabView
 * @extends Zarafa.core.ui.View
 *
 * A tab that sits on top of a calendar view. Calendar views can show multiple folders at once, and each folder has
 * a corresponding tab. The tabs can be used to move the corresponding folder to the view left of it (merge), move
 * the folder into its own view (separate), close the view, or make it the active folder. 
 */
Zarafa.calendar.ui.CalendarTabView = Ext.extend(Zarafa.core.ui.View, {
	/**
	 * @cfg {String} title The title which must be displayed as tab title.
	 * If not provided, then the {@link #folder} {@link Zarafa.hierarchy.data.MAPIFolderRecord#getDisplayname name}
	 * will be used.
	 */
	title : undefined,

	/**
	 * @cfg {Zarafa.hierarchy.data.MAPIFolderRecord} folder The folder which is being loaded in the {@link Zarafa.calendar.ui.AbstractCalenderView Calendar}
	 * to which this {@link Zarafa.calendar.ui.CalendarTabView tab} is attached.
	 */
	folder : undefined,

	/**
	 * The <div> element which is used as tab. It is placed between the left ({@link #tabLeft}) and
	 * right ({@link #tabRight}) elements. These three elements together are the entire visible tab. This
	 * middle element is used to provide the background of the tab.
	 * @property
	 * @type Ext.Element
	 */
	tab : undefined,

	/**
	 * The <div> element which is used as the content container for the {@link #tabText},
	 * {@link #mergeIcon}, {@link #separateIcon} and {@link #closeIcon} elements. These combined
	 * elements represent the contents of the tab.
	 * @property
	 * @type Ext.Element
	 */
	tabContents : undefined,

	/**
	 * The <div> element which is used to contain the {@link #title} for this tab. It is placed
	 * on top of the tab. But depending on the availability of the {@link #mergeIcon} this could be
	 * placed a bit over the {@link #leftTab}.
	 * @property
	 * @type Ext.Element
	 */
	tabText : undefined,

	/**
	 * The <div> element which is used to display the merge icon. This icon has an event
	 * handler which can merge two {@link Zarafa.calendar.ui.AbstractCalendarView calendars}
	 * together. This icon is placed on top of the tab, but depending on the position settings,
	 * this could be placed a bit over the {@link #leftTab}. This element will only be visible
	 * if {@link #showMergeIcon} is true.
	 * @property
	 * @type Ext.Element
	 */
	mergeIcon : undefined,

	/**
	 * The <div> element which is used to display the separate icon. This icon has
	 * an event handler which can separate two {@link Zarafa.calendar.ui.AbstractCalendarView calendars}
	 * after they were merged together with {@link #mergeIcon}. This element will only be visible
	 * if {@link #showSeparateIcon} is true.
	 * @property
	 * @type Ext.Element
	 */
	separateIcon : undefined,

	/**
	 * The <div> element which is used to display the close icon. This icon has
	 * an event handler which can close the selected {@link Zarafa.calendar.ui.AbstractCalendarView calendar}.
	 * @property
	 * @type Ext.Element
	 */
	closeIcon : undefined,

	/**
	 * The total with for the tab to use. This must at least be the result of {@link #getMinimumWidth}.
	 * This is configured before {@link #layout} by {@link #setWidth}.
	 * @property
	 * @type Number
	 */
	width : 0,

	/**
	 * Indicates if the current tab is selected by the user. This adds the 'selected' postfix to the
	 * CSS className if it is set to true. This is configured using {@link #setSelected}.
	 * @property
	 * @type Boolean
	 */
	selected : true,

	/**
	 * Indicates if the current tab is in the active group of calendars
	 * @property
	 * @type Boolean
	 */
	active : false,

	/**
	 * Indicates if the {@link #mergeIcon} must be made visible. This is configured before
	 * {@link #layout} by {@link #setShowMergeIcon}.
	 * @property
	 * @type Boolean
	 */
	showMergeIcon : false,

	/**
	 * Indicates if the {@link #separateIcon} must be made visible. This is configured before
	 * {@link #layout} by {@link #setShowSeparateIcon}.
	 * @property
	 * @type Boolean
	 */
	showSeparateIcon : false,

	/**
	 * indicates if the {@link #closeIcon} must be made visible. This is configured before
	 * {@link #layout} by {@link #setShowCloseIcon}.
	 * @property
	 * @type Boolean
	 */
	showCloseIcon : false,

	/**
	 * @constructor
	 * @param {Object} config configuration object. 
	 */
	constructor : function(config)
	{
		config = config || {};
		
		//TODO: move this to a separate function or make a folder method for it?
		var displayName;
		if (config.folder) {
			displayName = config.folder.getFullyQualifiedDisplayName();
		}

		Ext.applyIf(config, {
			baseCls : 'zarafa-calendar',
			title : displayName ? Ext.util.Format.htmlEncode(displayName) : undefined
		});
		
		// define drag/drop events
		this.addEvents(
			/**
			 * @event merge
			 * Fires when the left arrow icon is clicked.
			 * @param {MAPIFolder} folder MAPI folder.
			 */
			'merge',
			/**
			 * @event separate
			 * Fires when the right arrow icon is clicked.
			 * @param {MAPIFolder} folder MAPI folder.
			 */
			'separate',
			/**
			 * @event close
			 * Fires when the close icon is clicked.
			 * @param {MAPIFolder} folder MAPI folder.
			 */
			'close',
			/**
			 * @event click
			 * Fires when the tab is clicked. 
			 * @param {MAPIFolder} folder MAPI folder.
			 */
			'click'
		);		
		
		Zarafa.calendar.ui.CalendarTabView.superclass.constructor.call(this, config);
	},
	
	/**
	 * Calculates the desired width of the tab, that is, the size required to show the text,
	 * close icon, and any other enabled icons (merge, separate). 
	 * Used by {@link Zarafa.calendar.ui.AbstractCalendarView AbstractCalendarView} to lay out and size the tabs. 
	 * @return {Number} desired tab width
	 */
	getDesiredWidth : function()
	{
		// textMetrics has not been set up if not rendered. 
		if (!this.rendered) {
			return 0;
		}

		// Desired width is the minimum width (due to the icons), plus the width of the tab text.
		// We use the TextMetrics to calculate the width and height for the
		// contents of the tabText.
		var addClass = !this.tabContents.hasClass('zarafa-calendar-tab-selected');
		if ( addClass ){
			this.tabContents.addClass('zarafa-calendar-tab-selected');
		}
		var textMetrics = Ext.util.TextMetrics.createInstance(this.tabText);
		if ( addClass ){
			this.tabContents.removeClass('zarafa-calendar-tab-selected');
		}
		
		var desiredWidth = this.getMinimumWidth() + textMetrics.getWidth(this.title);

		// Check if we don't want a bigger width because of a min-width set in the css files
		this.tabContents.dom.style.removeProperty('min-width');
		var cssMinWidth = parseInt(this.tabContents.getStyle('min-width')) + this.tabContents.getPadding('lr');
		
		return Math.max(desiredWidth, cssMinWidth);
	},
	
	/**
	 * Calculates the minimum width of the tab, that is, the size required to show the close icon and
	 * any other enabled icons (merge, separate). This means the tabs should always be large enough to
	 * at least show all the icons.
	 * Used by {@link Zarafa.calendar.ui.AbstractCalendarView AbstractCalendarView} to lay out and size the tabs. 
	 * @return {Number} minimum tab width
	 */
	getMinimumWidth : function()
	{
		if (!this.rendered) {
			return 0;
		}

		var width = this.tabContents.getPadding('lr');

		// Calculate the width of each icon
		if (this.showCloseIcon){
			width += this.closeIcon.getWidth();
		}
		if (this.showMergeIcon){
			width += this.mergeIcon.getWidth();
		}
		if (this.showSeparateIcon){
			width += this.separateIcon.getWidth();
		}

		return width;		
	},

	/**
	 * Sets the tab width. Called by the parent
	 * {@link Zarafa.calendar.ui.AbstractCalendarView AbstractCalendarView} before layout.
	 * @param {Number} width tab width.
	 */
	setWidth : function(width)
	{
		this.width = width;
	},

	/**
	 * Sets whether the folder this tab corresponds to is the selected folder for that view.
	 * The effect of setting this to 'true' is that the text on the tab is shown in bold face.
	 * This.will also update the classNames assigned to {@link #tabBackground} and {@link #tabContents}.
	 * @param {Boolean} selected true iff the tab should be displayed as selected. 
	 * @param {Boolean} active true iff the tab should be displayed as active.
	 * This is different from selected in that only one tab can be active at any one point,
	 * while there is one selected tab for each calendar (i.e. the one on top, whose color scheme is used)
	 * @param {Boolean} init True to force the classes to be applied
	 */
	setSelected : function(selected, active, init)
	{
		if (init === true || this.selected !== selected || this.active !== active) {
			var className = this.getBaseClassName() + '-tab-selected';

			if (selected === true) {
				if (active === true) {
					this.tabContents.addClass(className);
					this.parentView.activeTabStroke.addClass(className);
				} else {
					this.tabContents.removeClass(className);
					this.parentView.activeTabStroke.removeClass(className);
				}
			} else {
				this.tabContents.removeClass(className);
				if (active !== true) {
					this.parentView.activeTabStroke.removeClass(className);
				}
			}
		}

		this.active = active;
		this.selected = selected;
	},

	/**
	 * Sets whether the merge icon (left arrow) is visible. When a tab belongs to the leftmost calendar
	 * view it cannot be moved to the left, thus the icon will not be displayed. 
	 * @param {Boolean} showMergeIcon true iff the merge icon should be displayed.
	 */
	setShowMergeIcon : function(showMergeIcon)
	{
		this.showMergeIcon = showMergeIcon;
	},
	
	/**
	 * Sets whether the separate icon (right arrow) is visible. When a calendar view is showing
	 * only a single folder, that folder cannot be separated out from the view, thus the icon will not be displayed. 
	 * @param {Boolean} showSeparateIcon true iff the merge icon should be displayed.
	 */
	setShowSeparateIcon : function(showSeparateIcon)
	{
		this.showSeparateIcon = showSeparateIcon;
	},
	
	/**
	 * Sets whether the close icon is visible. When a calendar view is showing only a single folder,
	 * that folder cannot be closed, thus the icon will not be displayed.
	 * @param {Boolean} showCloseIcon true iff the close icon should be displayed.
	 */
	setShowCloseIcon : function(showCloseIcon)
	{
		this.showCloseIcon = showCloseIcon;
	},
	
	/**
	 * Assign each {@link Ext.Element element} generated during {@link #render} with
	 * a CSS className. This will use {@link #getClassName} to obtain the desired
	 * className for each element.
	 * @private
	 */
	applyCSSClassNames : function()
	{
		var contextModel = container.getCurrentContext().getModel();
		if (contextModel instanceof Zarafa.calendar.CalendarContextModel) {
			var colorScheme = contextModel.getColorScheme(this.folder.get('entryid'));

			// Contents
			this.tabContents.dom.className = this.getClassName('tab', 'contents');
			this.mergeIcon.dom.className = this.getClassName('tab-icon', 'merge');
			this.tabText.dom.className = this.getClassName('tab-title');
			this.closeIcon.dom.className = this.getClassName('tab-icon', 'close');
			this.separateIcon.dom.className = this.getClassName('tab-icon', 'separate');
			
			this.tabContents.applyStyles({
				'background-color' : colorScheme.header
			});

			// Call setSelected to force the classed to be updated accordingly
			if (this.selected) {
				this.setSelected(this.selected, this.active, true);
			}
		}
	},

	/**
	 * Lays out the components of the tab.
	 * @protected
	 */
	onLayout : function()
	{
		// Apply CSS to all created elements
		// FIXME: We already did this during render, and we only repeat it here in
		// case things like the themeCls have been changed. We should however have
		// some mechanism to only set new class names when a given condition for a
		// particular class name has changed.
		this.applyCSSClassNames();

		var contextModel = container.getCurrentContext().getModel();
		if (contextModel instanceof Zarafa.calendar.CalendarContextModel) {
			var colorScheme = contextModel.getColorScheme(this.folder.get('entryid'));
			// Check if we have a light or dark color by converting the color code to HSL and checking the L-value
			var isDarkColor = Zarafa.core.ColorSchemes.getLuma(colorScheme.base) < 155;
			if ( !isDarkColor ){
				this.tabContents.addClass('light-background');
			}
		}

		// Add or remove classes to the container, so we can style it properly
		if ( this.showMergeIcon ){
			this.tabContents.addClass('zarafa-with-merge-icon');
		} else {
			this.tabContents.removeClass('zarafa-with-merge-icon');
		}
		if ( this.showSeparateIcon ){
			this.tabContents.addClass('zarafa-with-separate-icon');
		} else {
			this.tabContents.removeClass('zarafa-with-separate-icon');
		}
		if ( this.showCloseIcon ){
			this.tabContents.addClass('zarafa-with-close-icon');
		} else {
			this.tabContents.removeClass('zarafa-with-close-icon');
		}

		this.tabContents.setWidth(this.width);
		this.tabText.setWidth(this.width - this.getMinimumWidth());

		// Left arrow (merge left) icon.
		this.mergeIcon.setVisible(this.showMergeIcon);

		// The tabText element is a div that's laid out on top of the actual tab images and contains, surprise surprise,
		// the tab text.
		if (this.tabText.dom.innerHTML != this.title) {
			this.tabText.dom.innerHTML = this.title;
		}

		// Close icon.
		this.closeIcon.setVisible(this.showCloseIcon);

		// Right icon.
		this.separateIcon.setVisible(this.showSeparateIcon);
		
		Zarafa.calendar.ui.CalendarTabView.superclass.onLayout.call(this);
	},

	/**
	 * Renders the tab. This will create the {@link #tabBackground},
	 * {@link #tabContents} contains with the underlying {@link #tabLeft}, {@link #tabRight}, {@link #tab},
	 * {@link #tabText}, {@link #mergeIcon}, {@link #separateIcon} and {@link #closeIcon} elements.
	 * @param {Ext.Element} container The Ext.Element into which the view must be rendered.
	 */
	render : function(container)
	{
		// Container divs
		this.createDiv(this.parentView.tabArea, 'tabContents');
	
		// content divs
		this.createDiv(this.tabContents, 'mergeIcon');
		this.createDiv(this.tabContents, 'tabText');
		this.createDiv(this.tabContents, 'closeIcon');
		this.createDiv(this.tabContents, 'separateIcon');
		
		this.mergeIcon.setVisibilityMode(Ext.Element.DISPLAY);
		this.closeIcon.setVisibilityMode(Ext.Element.DISPLAY);
		this.separateIcon.setVisibilityMode(Ext.Element.DISPLAY);

		this.mon(this.mergeIcon, 'click', this.onMerge, this);
		this.mon(this.separateIcon, 'click', this.onSeparate, this);
		this.mon(this.closeIcon, 'click', this.onClose, this);
		this.mon(this.tabText, 'click', this.onClick, this);

		// We use the TextMetrics to calculate the width and height for the
		// contents of the tabText.
		this.tabText.textMetrics = Ext.util.TextMetrics.createInstance(this.tabText);

		// Apply CSS to all created elements, this will guarentee that we can request
		// styles from the elements like getWidth() and getMargins().
		this.applyCSSClassNames();

		Zarafa.calendar.ui.CalendarTabView.superclass.render.call(this, container);
	},
	
	/**
	 * Handles the 'click' event of the merge icon, and fires the {@link #merge} event.
	 * @private
	 */
	onMerge : function()
	{
		this.fireEvent('merge', this.folder);
	},
	
	/**
	 * Handles the 'click' event of the separate icon, and fires the {@link #separate} event.
	 * @private
	 */
	onSeparate : function()
	{
		this.fireEvent('separate', this.folder);
	},
	
	/**
	 * Handles the 'click' event of the close icon, and fires the {@link #close} event.
	 * @private
	 */
	onClose : function()
	{
		this.fireEvent('close', this.folder);
	},
	
	/**
	 * Handles the 'click' event of the tab text, and fires the {@link #click} event.
	 * @private
	 */
	onClick : function()
	{
		this.fireEvent('click', this.folder);
	}
});