Ext.namespace('Zarafa.widgets.folderwidgets');

/**
 * @class Zarafa.widgets.folderwidgets.AbstractFolderWidget
 * @extends Zarafa.core.ui.widget.Widget
 *
 * Widget which can be used to show the contents of a
 * {@link Zarafa.hierarchy.data.MAPIFolderRecord folder}
 * using a particular restriction (during {@link #store}{@link Ext.data.Store#load})
 * or a filter (using {@link #store}{@link Ext.data.Store#applyFilter}).
 *
 * Reload time is configurable per instance of the
 * widget (keys: 'reloadinterval', default 5 minutes).  These values are in
 * saved in miliseconds but displayed in seconds. The reload
 * interval is how often the folder is fully reloaded from the
 * server, to show records that were added to the folder
 * outside of WebApp.
 */
Zarafa.widgets.folderwidgets.AbstractFolderWidget = Ext.extend(Zarafa.core.ui.widget.Widget, {
	/**
	 * The folder which is shown inside this widget. This is initialized
	 * by {@link #onHierarchyLoad}.
	 * @property
	 * @type Zarafa.hierarchy.data.MAPIFolderRecord
	 * @private
	 */
	folder: undefined,

	/**
	 * @cfg {Zarafa.core.data.MAPIStore} store The store which is being used for loading the records
	 */
	store: undefined,

	/**
	 * @cfg {String} folderType The folder type to obtain the desired folder
	 * from the {@link Zarafa.hierarchy.data.HierarchyStore hierarchy}.
	 */
	folderType: undefined,

	/**
	 * The task that was {@link Ext.TaskMgr.start started} to reload the folder grid.
	 * @property
	 * @type Object
	 * @private
	 */
	reloadTask: undefined,

	/**
	 * The configuration object for the configure window of the widget
	 * @property
	 * @type Object
	 * @private
	 */
	configurationConfig: undefined,

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config)
	{
		config = config || {};

		// Set the guid so we can use the get() function
		this.guid = config.guid;

		Ext.applyIf(config, {
			hasConfig: true,
			height: this.get('widgetheight') || 300
		});

		Zarafa.widgets.folderwidgets.AbstractFolderWidget.superclass.constructor.call(this, config);
	},

	/**
	 * Initialize the event handlers for the {@link #store} and {@link Zarafa.hierarchy.data.HierarchyStore Hierarchy}.
	 * @protected
	 */
	initEvents: function ()
	{
		Zarafa.widgets.folderwidgets.AbstractFolderWidget.superclass.initEvents.apply(this, arguments);

		// Wait for the hierarchy store to be loaded.
		var hierarchyStore = container.getHierarchyStore();
		this.mon(hierarchyStore, 'load', this.onHierarchyLoad, this);
		// needed when adding the widget after hierarchy load
		this.onHierarchyLoad(hierarchyStore);

		// Wait for the store to be loaded, so we can activate
		// the refreshing and reloading times.
		this.mon(this.store, 'load', this.startReloadTask, this, {single: true});

		// Apply the filter when we have new records
		this.mon(this.store, 'load', this.updateFilter, this);

		// Listen for record updates, as that might have impact on the filtering
		// which should be applied.
		this.mon(this.store, 'update', this.onStoreUpdate, this);
	},

	/**
	 * Load the default calendar folder and retrieve the records.
	 * @param {Zarafa.hierarchy.data.HierarchyStore} hierarchyStore The store which fired the event
	 * @private
	 */
	onHierarchyLoad: function (hierarchyStore)
	{
		this.folder = hierarchyStore.getDefaultFolder(this.folderType);
		if (this.folder) {
			this.reloadStore();
		}
	},

	/**
	 * Starts a reload task for the store. If one already exists, it will be stopped first.
	 * @private
	 */
	startReloadTask: function ()
	{
		if ( this.reloadTask ) {
			Ext.TaskMgr.stop(this.reloadTask);
		}

		// Periodically reload data from the server to remove stale
		// data from the store.  But only do this when the store has
		// finished loading for the first time.
		var interval = this.get('reloadinterval') || 300;
		interval *= 1000; // convert seconds in milliseconds

		this.reloadTask = Ext.TaskMgr.start({
			run: this.reloadStore,
			interval: interval,
			scope: this
		});
	},

	/**
	 * When the store record has been changed, {@link #updateFilter apply filter}
	 * to ensure that unwanted records are immediately removed.
	 * @private
	 */
	onStoreUpdate: function ()
	{
		this.updateFilter();
	},

	/**
	 * This will {@link Ext.data.Store#load load} the {@link #store}.
	 * @private
	 */
	reloadStore: function ()
	{
		if (this.folder) {
			this.store.load({folder: this.folder});
		}
	},

	/**
	 * Update the filter.
	 * @private
	 */
	updateFilter: Ext.emptyFn,

	/**
	 * Apply custom style and content for the row body. This will color
	 * a task in red when its due-date is reached. If categories are applied
	 * to a task these categories will be displayed.
	 *
	 * @param {Ext.data.Record} record The {@link Ext.data.Record Record} corresponding to the current row.
	 * @param {Number} rowIndex The row index
	 * @param {Object} rowParams A config object that is passed to the row template during
	 * rendering that allows customization of various aspects of a grid row.
	 * If enableRowBody is configured true, then the following properties may be set by this function,
	 * and will be used to render a full-width expansion row below each grid row.
	 * @return {String} a CSS class name to add to the row
	 * @private
	 */
	viewConfigGetRowClass: function (record, rowIndex, rowParams)
	{
		rowParams.body = '<div class="zarafa-grid-body-container">';

		// Render the categories
		var categories = Zarafa.common.categories.Util.getCategories(record);
		var categoriesHtml = Zarafa.common.categories.Util.getCategoriesHtml(categories);
		rowParams.body += '<div class="k-category-container">' + categoriesHtml + '</div>';

		// Render the subject
		var subject = Zarafa.common.ui.grid.Renderers.subject(record.get('subject'), {});
		rowParams.body += String.format('<div class="grid_compact grid_compact_left grid_compact_subject_cell">{0}</div>', subject);
		rowParams.body += '</div>';

		// Get the due date class from the normal TaskGridView
		var rowClass = Zarafa.task.ui.TaskGridView.prototype.viewConfigGetRowClass(record);

		return "x-grid3-row-expanded " + rowClass;
	},

	/**
	 * Create spinner field with the given minimum and maximum value.
	 * @param {Number} minHeight minimum value of spinner field.
	 * @param {Number} maxHeight maximum value of spinner field.
	 * @return {Object[]} An array with configuration objects of spinner field.
	 */
	createConfigHeight: function (minHeight, maxHeight)
	{
		return {
			xtype: 'zarafa.spinnerfield',
			fieldLabel: _('Widget height'),
			name: 'widgetheight',
			boxLabel: _('pixels'),
			width: 60,
			minValue: minHeight || 100, // 100 pixel
			maxValue: maxHeight || 500, // 500 pixel
			incrementValue: 5, // 5 pixel
			defaultValue: this.get('widgetheight') || 300,
			listeners: {
				'change': this.onFieldChange,
				scope: this
			},
			plugins: ['zarafa.numberspinner']
		};
	},

	/**
	 * Configure the widget.  At this time, only the reload and
	 * refresh times can be configured.
	 * @todo Also allow the user to select the folder(s) to show here.
	 * @private
	 */
	config: function ()
	{
		var config = Ext.apply({}, this.configurationConfig || {}, {
			title: _('Configure widget'),
			layout: 'fit',
			width: 320,
			height: 135,
			modal: true,
			items: [{
				xtype: 'form',
				frame: true,
				cls: 'k-configure-widget',
				labelWidth: 120,
				items: [{
					xtype: 'zarafa.spinnerfield',
					fieldLabel: _('Reload interval'),
					name: 'reloadinterval',
					boxLabel: _('seconds'),
					width: 60,
					minValue: 30, // 30 seconds
					maxValue: 1800, // 30 minutes
					incrementValue: 30, // 30 seconds
					defaultValue: this.get('reloadinterval') || 300,
					listeners: {
						'change': this.onFieldChange,
						scope: this
					},
					plugins: ['zarafa.numberspinner']
				},
					this.createConfigHeight()
				].concat(this.getConfigurationItems()),
				buttons: [{
					text: _('Close'),
					scope: this,
					handler: function () {
						this.win.close();
					}
				}]
			}]
		});

		this.win = new Ext.Window(config);
		this.win.show(this);
	},

	/**
	 * Returns an array with the items that should be added to the configuration window
	 * of the widget. Should be overridden by child classes.
	 * @return {array} An array of configuration objects for {@link Ext.Component components}
	 */
	getConfigurationItems: function()
	{
		return [];
	},

	/**
	 * Event handler which is fired when one of the fields in the Configuration dialog
	 * has been changed. This will update the corresponding option in the settings.
	 * @param {Ext.form.Field} field The field which fired the event
	 * @param {Mixed} newValue The new value which was applied
	 * @param {Mixed} oldValue The old value which was applied
	 * @private
	 */
	onFieldChange: function (field, newValue, oldValue)
	{
		var fieldName = field.getName();
		this.set(fieldName, newValue);

		if (fieldName === 'widgetheight') {
			this.setHeight(newValue);
		}

		if (fieldName === 'reloadinterval') {
			this.startReloadTask();
		}
	}
});
Ext.namespace('Zarafa.widgets.folderwidgets');

/**
 * @class Zarafa.widgets.folderwidgets.AppointmentsWidget
 * @extends Zarafa.widgets.folderwidgets.AbstractFolderWidget
 *
 * Widget that displays the appointments for today, from the default
 * calendar.  It only displays appointments that occur on or after the
 * current time, so outdated information is never shown.
 *
 * Reload time is configurable per instance of the
 * widget (keys: 'reloadinterval', default 5 minutes).  These values are in
 * saved in miliseconds but displayed in seconds. The reload
 * interval is how often the calendar is fully reloaded from the
 * server, to show records that were added to the folder
 * outside of WebApp.
 */
Zarafa.widgets.folderwidgets.AppointmentsWidget = Ext.extend(Zarafa.widgets.folderwidgets.AbstractFolderWidget, {
	/**
	 * The folder which was selected by the user
	 * @property
	 * @type Zarafa.hierarchy.data.MAPIFolderRecord
	 */
	folder : undefined,

	/**
	 * 'entryid' of folder which was selected by the user
	 * @property
	 * @type String
	 */
	folderId : undefined,

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config)
	{
		config = config || {};

		var store = new Zarafa.calendar.AppointmentStore();

		Ext.applyIf(config, {
			autoScroll: true,
			layout: 'fit',
			folderType: 'calendar',
			store: store,
			items: [{
				xtype: 'zarafa.gridpanel',
				cls: 'k-appointmentwidget',
				store: store,
				hideHeaders: true,
				ref : 'appointmentsGrid',
				loadMask: {
					msg: _('Loading appointments...')
				},
				sm: new Zarafa.common.ui.grid.RowSelectionModel({
					singleSelect: true
				}),
				viewConfig: {
					deferEmptyText: false,
					emptyText: '<div class="emptytext">' + _("Please select a calendar via widget settings") + '</div>',
					forceFit: true,
					enableRowBody: true,
					rowSelectorDepth: 15,
					getRowClass: this.viewConfigGetRowClass,
					scope: this
				},
				colModel: new Ext.grid.ColumnModel({
					columns: [{
						header: _('Time'),
						dataIndex: 'startdate',
						renderer: this.timeRenderer,
						scope: this
					}]
				}),
				listeners: {
					'rowcontextmenu': this.onRowContextMenu,
					'rowdblclick': this.onRowDblClick,
					scope: this
				}
			}]
		});

		// Customize the configuration of the settings window
		this.configurationConfig = {
			height: 180
		};

		Zarafa.widgets.folderwidgets.AppointmentsWidget.superclass.constructor.call(this, config);
		this.folderId = this.get('folderId');
	},

	/**
	 * Adds a field to configure the number of days for which appointments will be shown
	 * @return {array} An array of configuration objects for {@link Ext.Component components}
	 */
	getConfigurationItems: function()
	{
		return [{
			xtype: 'zarafa.spinnerfield',
			fieldLabel: _('Number of days'),
			name: 'numdays',
			boxLabel: _('day(s)'),
			width: 60,
			minValue: 1, // 1 day
			maxValue: 365, // 1 year
			incrementValue: 1, // 1 day
			defaultValue: this.get('numdays') || 5,
			listeners: {
				'change': this.onNumDaysChange,
				scope: this
			},
			plugins: ['zarafa.numberspinner']
		},{
			xtype: 'zarafa.calendarfolderselectionlink',
			folder: this.folder,
			fieldLabel: _('Calendar'),
			name: 'Calendar',
			listeners: {
				'folderupdate': this.onUpdateFolder,
				scope: this
			}
		}];
	},

	/**
	 * Initialize the event handlers for the {@link #store} and {@link Zarafa.hierarchy.data.HierarchyStore HierarchyStore}.
	 * @protected
	 */
	initEvents: function () {
		Zarafa.widgets.folderwidgets.AppointmentsWidget.superclass.initEvents.apply(this, arguments);

		// set handlers to update widget state on remove of folder from hierarchy.
		var hierarchyStore = container.getHierarchyStore();
		this.mon(hierarchyStore, {
			'remove': this.onSharedStoreRemove,
			'removeFolder': this.onSharedStoreRemove,
			scope: this
		});

		// Wait for the store to be loaded, so we can activate
		// the filter task and set empty text if no appointments available.
		this.mon(this.store, 'load', this.onStoreLoad, this, { single: true });
		this.mon(this.store, 'exception', this.onStoreException, this);
	},

	/**
	 * Handler for {@link #store} for {@link Ext.data.Store#load load} event.
	 * This will call {@link #startFilterTask} and {@link #setEmptytext} after the {@link #store} load.
	 * @private
	 */
	onStoreLoad : function()
	{
		this.startFilterTask();
		this.setEmptytext();
	},

	/**
	 * Handler for {@link #store} for {@link Ext.data.Store#exception exception} event.
	 * This will remove all records from store and set empty text as error message received from php side.
	 *
	 * @param {Misc} misc See {@link Ext.data.DataProxy}#{@link Ext.data.DataProxy#exception exception}
	 * for description.
	 * @private
	 */
	onStoreException: function(proxy, type, action, options, response, args)
	{
		var emptyText;
		if (response && response.error) {
			var errObj = response.error;

			if (errObj.info && errObj.info.display_message) {
				emptyText = String.format(_("{0}"), errObj.info.display_message);
			}
		}

		// Empty store so that we can show error message.
		this.appointmentsGrid.getStore().removeAll();
		this.setEmptytext(emptyText);
	},

	/**
	 * Load the stored calendar folder and retrieve the records.
	 * @param {Zarafa.hierarchy.data.HierarchyStore} hierarchyStore The store which fired the event
	 * @private
	 */
	onHierarchyLoad: function (hierarchyStore)
	{
		if (Ext.isEmpty(this.folderId)) {
			return;
		}

		this.folder = hierarchyStore.getFolder(this.folderId);
		var folderReadRight = Zarafa.core.mapi.Rights.RIGHTS_READ_ANY;
		if (Ext.isEmpty(this.folder) ||
			(this.folder.get('rights') & folderReadRight) !== folderReadRight) {
			// Reset when there is folderId and we can't find folder
			// or folder has not 'RIGHTS_FOLDER_VISIBLE' rights.
			// So, we need to reset the state.
			this.resetWidget();
			return;
		}

		this.setWidgetTitle(this.folder);
		this.reloadStore();
	},

	/**
	 * Reset when previously selected folder is not found (e.g. saved states will be cleared on reset settings of webapp).
	 * This will be called from {@link Zarafa.widgets.folderwidgets.AbstractFolderWidget#onHierarchyLoad onHierarchyLoad}
	 * when folder is not found and when the 'remove' or 'removeFolder' event fired for hierarchy store.
	 */
	resetWidget : function()
	{
		this.folder = undefined;
		this.folderId = undefined;
		this.set("folderId", undefined);
		this.appointmentsGrid.getStore().removeAll();
	},

	/**
	 * This method will set name of this widget. 
	 * If folder is not given, it will set default title otherwise it will prepare
	 * title for widget as per folder given and apply it.
	 *
	 * @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder folder which is selected to show appointments from.
	 * @private
	 */
	setWidgetTitle : function(folder)
	{
		var title = _('Upcoming Appointments');
		
		if (folder) {
			var folderStore = folder.getMAPIStore();
			var isOwnStore = folderStore.isDefaultStore();
			var isDefaultFolder = folder.isDefaultFolder();

			var ownerName = folderStore.get('display_name');
			var folderName = folder.get('display_name');
			
			// Do not show ownername for own store folder.
			// And don't change title for default calendar folder of own store.
			if (!(isOwnStore && isDefaultFolder)) {
				if (isOwnStore) {
					title = String.format(_('Appointments of {0}'), folderName);
				} else {
					title = String.format(_('Appointments of {0} - {1}'), folderName, ownerName);
				}
			}
		}

		this.setTitle(title);
	},

	/**
	 * This method will set empty text message of this widget.
	 * If the emptyText param is not provided then it will set 'No appointments' message
	 * when no appointments are available.
	 * Or if {@link #folder} has no value (no folder is selected currently) then
	 * this will display a message to select folder.
	 *
	 * @param {String} emptyText which needs to be set as empty text of grid view.
	 */
	setEmptytext : function(emptyText)
	{
		var gridView = this.appointmentsGrid.getView();

		if (Ext.isEmpty(emptyText)) {
			emptyText = this.folder ? _('No appointments') : _("Please select a calendar via widget settings");
		}

		gridView.emptyText = '<div class="emptytext">'+ emptyText +'</div>';
		gridView.refresh();
	},

	/**
	 * Event handler for 'remove' and 'removeFolder' events of {@link Zarafa.hierarchy.data.HierarchyStore Hierarchy}
	 * This will reset widget if state folder of this widget gets deleted.
	 * 
	 * @param {Zarafa.hierarchy.data.HierarchyStore} store hierarchy store after removing record.
	 * @param {Zarafa.hierarchy.data.MAPIStoreRecord} record The record which is removed
	 * @param {Number} totalRecords total number of records in store.
	 */
	onSharedStoreRemove : function(store, record, totalRecords)
	{
		var stateFolderId = this.get('folderId');
		if (!Ext.isEmpty(stateFolderId)) {
			var stateFolder = store.getFolder(stateFolderId);

			// If deleted folder is state folder or closed shared store includes
			// state folder then reset this widget.
			if (Ext.isEmpty(stateFolder)) {
				this.resetWidget();
				this.setEmptytext();
			}
		}
	},

	/**
	 * Event handler for 'folderupdate' event.
	 * This will update the {@link #folder} , save the folder entryid in state,
	 * set title of this widget and reload the state with updated folder.
	 * 
	 * @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder folder which is selected to show appointments from.
	 * @private
	 */
	onUpdateFolder: function (folder)
	{
		this.folder = folder;
		this.folderId = folder.get('entryid');
		this.set("folderId", this.folderId);
		this.setWidgetTitle(folder);
		this.reloadStore();
		this.setEmptytext();
	},

	/**
	 * Event handler which is fired when 'Number of Days' field in the Configuration dialog
	 * has been changed. This will update the corresponding option in the settings.
	 * @param {Ext.form.Field} field The field which fired the event
	 * @param {Mixed} newValue The new value which was applied
	 * @param {Mixed} oldValue The old value which was applied
	 * @private
	 */
	onNumDaysChange: function (field, newValue, oldValue)
	{
		this.set(field.getName(), newValue);
		this.reloadStore();
	},

	/**
	* Starts a filter task for the store. Will make sure that the filter is updated at
	* every 30 seconds so old appointments will be filtered out.
	* @private
	*/
	startFilterTask: function ()
	{
		Ext.TaskMgr.start({
			run: this.updateFilter,
			interval: 30000,
			scope: this
		});
	},

	/**
	 * This will {@link Ext.data.Store#load load} the {@link #store} using
	 * a restriction which only allows todays appointments.
	 * @private
	 */
	reloadStore: function ()
	{
		if (!this.folder) {
			return;
		}

		var numDays = this.get('numdays') || 5;

		var startdate = new Date().clearTime().getTime() / 1000;
		var duedate = new Date().clearTime().add(Date.DAY, numDays).getTime() / 1000;

		this.store.load({
			folder: this.folder,
			params: {
				ignoreException: true,
				restriction: {
					startdate: startdate,
					duedate: duedate
				}
			}
		});
	},

	/**
	 * Update the filter with the current time. Items that end
	 * before now are removed.
	 * @private
	 */
	updateFilter: function ()
	{
		var now = new Date().getTime() / 1000;
		this.store.filterBy(function (record) {
			var dueDate = record.get('duedate').getTime() / 1000;
			return !dueDate || dueDate >= now;
		}, this);
	},

	/**
	 * Renderer for the time column. Adds a recurrence icon and a private icon if applicable
	 *
	 * @param {Mixed} value The subject of the appointment
	 * @param {Object} metaData Used to set style information to gray out appointments that occur now
	 * @param {Ext.data.Record} record The record being displayed, used to retrieve the start and end times
	 * @private
	 */
	timeRenderer: function (value, metaData, record)
	{
		var recurringIcon = '';
		var recurringPattern = record.get('recurring_pattern');
		if (recurringPattern) {
			if (record.get('exception') === true) {
				recurringIcon =
					'&nbsp;<span ext:qwidth="300" ext:qtip="' + recurringPattern + '">' +
						'<img src="' + Zarafa.calendar.ui.IconCache.getExceptionIcon().src + '"/>' +
					'</span>';
			} else {
				recurringIcon =
					'&nbsp;<span ext:qwidth="300" ext:qtip="' + recurringPattern + '">' +
						'<img src="' + Zarafa.calendar.ui.IconCache.getRecurringIcon().src + '"/>' +
					'</span>';
			}
		}

		var privateIcon = '';
		if (record.get('private') === true) {
			privateIcon = '&nbsp;<img src="' + Zarafa.calendar.ui.IconCache.getPrivateIcon().src + '"/>';
		}

		var dayStart = new Date().clearTime();
		var dayEnd = new Date().add(Date.DAY, 1).clearTime();
		var start = value;
		var end = record.get('duedate');
		var allDayEvent = record.get('alldayevent');

		if ( start >= dayStart && end <= dayEnd ) {
			if ( !allDayEvent ) {
				return String.format(_('Today {0} - {1}'), start.format(_('G:i')), end.format(_('G:i'))) + recurringIcon + privateIcon;
			}

			return _('Today (all day)') + recurringIcon + privateIcon;
		}

		if ( !allDayEvent ) {
			return String.format(_('{0} - {1}'), start.format(_('d/m/y G:i')), end.format(_('d/m/y G:i'))) + recurringIcon + privateIcon;
		}

		return String.format(_('{0} - {1} (all day)'), start.format(_('d/m/y')), end.format(_('d/m/y'))) + recurringIcon + privateIcon;
	},

	/**
	 * Event handler which is triggered when user opens context menu
	 * @param {Ext.grid.GridPanel} grid grid panel object
	 * @param {Number} rowIndex index of row
	 * @param {Ext.EventObject} event event object of the event
	 * @private
	 */
	onRowContextMenu: function (grid, rowIndex, event)
	{
		// check row is already selected or not, if its not selected then select it first
		var selectionModel = grid.getSelectionModel();
		if (!selectionModel.isSelected(rowIndex)) {
			selectionModel.selectRow(rowIndex);
		}

		// The ContextMenu needs the ContextModel for cases where we want to reply the mail.
		var model;
		if (this.folder) {
			var context = container.getContextByFolder(this.folder);
			if (context) {
				model = context.getModel();
				Zarafa.core.data.UIFactory.openDefaultContextMenu(selectionModel.getSelections(), {
					position: event.getXY(),
					model: model
				});
			}
		}
	},

	/**
	 * Called when the user double-clicks on an appointment.
	 * @param {Ext.grid.GridPanel} grid The grid which fired the event
	 * @param {Number} rowIndex The row which was double clicked
	 * @param {Ext.EventObject} event The event object
	 * @private
	 */
	onRowDblClick: function (grid, rowIndex, event)
	{
		var record = grid.getSelectionModel().getSelected();
		if (!Ext.isEmpty(record)) {
			// FIXME what about recurring series records ?
			if (record.isRecurringOccurence()) {
				record = record.convertToOccurenceRecord();
			}
		}
		Zarafa.core.data.UIFactory.openViewRecord(record);
	}
});

Zarafa.onReady(function () {
	container.registerWidget(new Zarafa.core.ui.widget.WidgetMetaData({
		name : 'appointments',
		displayName : _('Upcoming Appointments'),
		widgetConstructor : Zarafa.widgets.folderwidgets.AppointmentsWidget
	}));
});
Ext.namespace('Zarafa.widgets.folderwidgets');

/**
 * @class Zarafa.widgets.folderwidgets.MailWidget
 * @extends Zarafa.widgets.folderwidgets.AbstractFolderWidget
 *
 * Widget that shows the unread mail.
 */
Zarafa.widgets.folderwidgets.MailWidget = Ext.extend(Zarafa.widgets.folderwidgets.AbstractFolderWidget, {
	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor : function(config)
	{
		config = config || {};

		var store = new Zarafa.mail.MailStore();

		// The store already has the default sort info, but we
		// must apply it still. (Bug in the ListModuleStore?).
		store.setDefaultSort(store.defaultSortInfo.field, store.defaultSortInfo.direction);

		// Create a restriction, we only want unread mails, so mails which
		// do not have the MSGFLAG_READ flag on the PR_MESSAGE_FLAGS
		store.setRestriction({
			'search' : Zarafa.core.data.RestrictionFactory.dataResBitmask(
				'0x00E070003', /* PR_MESSAGE_FLAGS */
				Zarafa.core.mapi.Restrictions.BMR_EQZ,
				Zarafa.core.mapi.MessageFlags.MSGFLAG_READ
			)
		});
		// If we want to use a search restriction we must say that this store is an advanceSearchStore
		// or it will be removed.
		store.isAdvanceSearchStore = function(){
			return true;
		};

		Ext.applyIf(config, {
			autoScroll: true,
			layout: 'fit',
			folderType : 'inbox',
			store : store,
			items : [{
				xtype: 'zarafa.gridpanel',
				id: 'unread-mail-widget',
				store: store,
				border: true,
				loadMask : {
					msg : _('Loading mail') + '...'
				},
				sm: new Zarafa.common.ui.grid.RowSelectionModel({
					singleSelect: true
				}),
				viewConfig: {
					deferEmptyText: false,
					emptyText: '<div class="emptytext">' + _('No unread mail.') + '</div>',
					forceFit: true,

					// Enable the row body in which we can render
					// the subject of the mail and some icons
					// for the attachment and importance.
					enableRowBody : true,
					rowSelectorDepth : 15,
					getRowClass : this.viewConfigGetRowClass
				},
				colModel : new Ext.grid.ColumnModel({
					columns: [{
						header: _('From'),
						dataIndex: 'sent_representing_name',
						menuDisabled : true,
						renderer: Ext.util.Format.htmlEncode
					},{
						header: _('Received'),
						dataIndex: 'message_delivery_time',
						editable: false,
						menuDisabled : true,
						renderer : Zarafa.common.ui.grid.Renderers.datetime
					}]
				}),
				listeners: {
					'rowcontextmenu' : this.onRowContextMenu,
					'rowdblclick': this.onRowDblClick,
					scope: this
				}
			}]
		});

		Zarafa.widgets.folderwidgets.MailWidget.superclass.constructor.call(this, config);
	},

	/**
	 * Update the filter. This will filter the records using
	 * {@link Zarafa.core.data.IPMRecord#isRead}.
	 * @private
	 */
        updateFilter : function()
	{
		this.store.filterBy(function(record) {
			return !record.isRead();
		}, this);
	},

	/**
	 * Apply custom style and content for the row body. This will always
	 * apply the Read/Unread style to the entire row. Optionally it will
	 * enable the row body containing the subject and icons for attachment
	 * and priority.
	 *
	 * @param {Ext.data.Record} record The {@link Ext.data.Record Record} corresponding to the current row.
	 * @param {Number} rowIndex The row index
	 * @param {Object} rowParams A config object that is passed to the row template during
	 * rendering that allows customization of various aspects of a grid row.
	 * If enableRowBody is configured true, then the following properties may be set by this function,
	 * and will be used to render a full-width expansion row below each grid row.
	 * @param {Ext.data.Store} store The Ext.data.Store this grid is bound to
	 * @return {String} a CSS class name to add to the row
	 * @private
	 */
	viewConfigGetRowClass : function(record, rowIndex, rowParams, store)
	{
		var cssClass = (record.isRead() ? 'mail_read' : 'mail_unread');

		var meta = {}; // Metadata object for Zarafa.common.ui.grid.Renderers.
		var value = ''; // The value which must be rendered
		rowParams.body = '<table cellspacing="0" cellpadding="0" border="0" style="width: 100%;">';
		rowParams.body += '<tr>';

		// Render the subject
		meta = {};
		value = Zarafa.common.ui.grid.Renderers.subject(record.get('subject'), meta, record);
		rowParams.body += String.format('<td style="width: 100%"><div class="grid_compact grid_compact_left grid_compact_subject_cell {0}" style="height: 24px;">{1}</div></td>', meta.css, value);

		// Render the attachment icon (always aligned to the right)
		meta = {};
		value = Zarafa.common.ui.grid.Renderers.attachment(record.get('hasattach'), meta, record);
		rowParams.body += String.format('<td style="width: 24px"><div class="grid_compact {0}" style="height: 24px; width: 24px;">{1}</div></td>', meta.css, value);

		// Render the importance icon (always aligned to the right)
		meta = {};
		value = Zarafa.common.ui.grid.Renderers.importance(record.get('importance'), meta, record);
		rowParams.body += String.format('<td style="width: 24px"><div class="grid_compact {0}" style="height: 24px; width: 24px;">{1}</div></td>', meta.css, value);

		rowParams.body += '</tr></table>';
		return 'x-grid3-row-expanded ' + cssClass;
	},

	/**
	 * Event handler which is triggered when user opens context menu
	 * @param {Ext.grid.GridPanel} grid grid panel object
	 * @param {Number} rowIndex index of row
	 * @param {Ext.EventObject} eventObj eventObj object of the event
	 * @private
	 */
	onRowContextMenu : function(grid, rowIndex, event)
	{
		// check row is already selected or not, if its not selected then select it first
		var selectionModel = grid.getSelectionModel();
		if (!selectionModel.isSelected(rowIndex)) {
			selectionModel.selectRow(rowIndex);
		}

		// The ContextMenu needs the ContextModel for cases where we want to reply the mail.
		var model;
		if (this.folder) {
			var context = container.getContextByFolder(this.folder);
			if (context) {
				model = context.getModel();
			}
		}

		Zarafa.core.data.UIFactory.openDefaultContextMenu(selectionModel.getSelections(), { position : event.getXY(), model : model });
	},

	/**
	 * Called when the user double-clicks on a mail.
	 * @param {Ext.grid.GridPanel} grid The grid which fired the event
	 * @param {Number} rowIndex The row which was double clicked
	 * @param {Ext.EventObject} event The event object
	 * @private
	 */
	onRowDblClick : function(grid, rowIndex, event)
	{
		Zarafa.core.data.UIFactory.openViewRecord(grid.getSelectionModel().getSelected());
	}
});

Zarafa.onReady(function() {
	container.registerWidget(new Zarafa.core.ui.widget.WidgetMetaData({
		name : 'mail',
		displayName : _('Unread Mail'),
		widgetConstructor : Zarafa.widgets.folderwidgets.MailWidget
	}));
});
Ext.namespace('Zarafa.widgets.folderwidgets');

/**
 * @class Zarafa.widgets.folderwidgets.TasksWidget
 * @extends Zarafa.widgets.folderwidgets.AbstractFolderWidget
 *
 * Widget that shows current, non-completed tasks incl. its duedate, importance,
 * % completed, owner and categories
 */
Zarafa.widgets.folderwidgets.TasksWidget = Ext.extend(Zarafa.widgets.folderwidgets.AbstractFolderWidget, {
	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config)
	{
		config = config || {};

		// FIXME - add something to find mails marked for follow-up as well
		var store = new Zarafa.task.TaskStore();

		// sort by duedate
		store.setDefaultSort('duedate', 'asc');

		// Create a restriction, we only want uncomplete tasks, so tasks which
		// do not have the status flag set to Zarafa.core.mapi.TaskStatus.COMPLETE or
		// Zarafa.core.mapi.FlagStatus.completed
		store.setRestriction({
			'task': Zarafa.core.data.RestrictionFactory.createResAnd([Zarafa.core.data.RestrictionFactory.dataResProperty(
				'status',
				Zarafa.core.mapi.Restrictions.RELOP_NE,
				Zarafa.core.mapi.TaskStatus.COMPLETE
			),Zarafa.core.data.RestrictionFactory.dataResProperty(
				'flag_status',
				Zarafa.core.mapi.Restrictions.RELOP_NE,
				Zarafa.core.mapi.FlagStatus.completed
			)])
		});

		Ext.applyIf(config, {
			autoScroll: true,
			layout: 'fit',
			folderType: 'todolist',
			store: store,
			items: [{
				xtype: 'zarafa.gridpanel',
				cls: 'k-taskwidget',
				store: store,
				loadMask: {
					msg: _('Loading tasks') + '...'
				},
				sm: new Zarafa.common.ui.grid.RowSelectionModel({
					singleSelect: true
				}),
				viewConfig: {
					deferEmptyText: false,
					emptyText: '<div class="emptytext">' + _('No tasks.') + '</div>',
					forceFit: true,
					enableRowBody: true,
					rowSelectorDepth: 15,
					getRowClass: this.viewConfigGetRowClass

				},
				colModel: new Ext.grid.ColumnModel({
					columns: [{
						header: _("Due"),
						tooltip : _('Sort by: Due Date'),
						dataIndex: "duedate",
						renderer: Zarafa.common.ui.grid.Renderers.utcdate
					}, {
						header: "<p class='icon_importance'>&nbsp;</p>",
						tooltip : _('Sort by: Priority'),
						dataIndex: "importance",
						width: 24,
						fixed: true,
						renderer: Zarafa.common.ui.grid.Renderers.importance
					}, {
						header: "%",
						tooltip : _('Sort by: Percentage Completed'),
						dataIndex: "percent_complete",
						width : 75,
						renderer: Zarafa.common.ui.grid.Renderers.percentage
					}, {
						header: _('Owner'),
						tooltip : _('Sort by: Owner'),
						dataIndex: 'owner',
						renderer: this.ownerRenderer
					}],
					defaults: {
						sortable: true,
						menuDisabled: true
					}
				}),
				listeners: {
					'rowcontextmenu': this.onRowContextMenu,
					'rowdblclick': this.onRowDblClick,
					scope: this
				}
			}]
		});

		Zarafa.widgets.folderwidgets.TasksWidget.superclass.constructor.call(this, config);
	},

	/**
	 * Update the filter to make sure only non-completed tasks are shown
	 * @private
	 */
	updateFilter: function ()
	{
		this.store.filterBy(function (record) {
			return (record.get('status') != Zarafa.core.mapi.TaskStatus.COMPLETE);
		}, this);
	},

	/**
	 * Renders the owner of the task as its initials and adds its full
	 * form as a tooltip
	 *
	 * @param {Mixed} value The subject of the appointment
	 * @param {Object} metaData Used to set style information to gray out appointments that occur now
	 * @param {Ext.data.Record} record The record being displayed, used to retrieve the start and end times
	 * @private
	 */
	ownerRenderer: function (value, metaData, record)
	{
		var owner = record.get("owner");
		if (!Ext.isString(owner)) {
			return '';
		}

		var ownerNames = owner.split(" "); // array of all parts of the owner's name
		var initials = '';

		for (var i = 0, len = ownerNames.length; i < len; i++) {
			initials += ownerNames[i].substring(0, 1);
		}

		return '<span ext:qtip="' + Ext.util.Format.htmlEncode(owner) + '" ext:qwidth="100%">' + Ext.util.Format.htmlEncode(initials) + '</span>';
	},

	/**
	 * Event handler which is triggered when user opens context menu
	 * @param {Ext.grid.GridPanel} grid grid panel object
	 * @param {Number} rowIndex index of row
	 * @param {Ext.EventObject} event event object of the event
	 * @private
	 */
	onRowContextMenu: function (grid, rowIndex, event)
	{
		// check row is already selected or not, if its not selected then select it first
		var selectionModel = grid.getSelectionModel();
		if (!selectionModel.isSelected(rowIndex)) {
			selectionModel.selectRow(rowIndex);
		}

		// The ContextMenu needs the ContextModel for cases where we want to reply the mail.
		var model;
		if (this.folder) {
			var context = container.getContextByFolder(this.folder);
			if (context) {
				model = context.getModel();
				Zarafa.core.data.UIFactory.openDefaultContextMenu(selectionModel.getSelections(), {
					position: event.getXY(),
					model: model
				});
			}
		}
	},

	/**
	 * Called when the user double-clicks on a task.
	 * @param {Ext.grid.GridPanel} grid The grid which fired the event
	 * @param {Number} rowIndex The row which was double clicked
	 * @param {Ext.EventObject} event The event object
	 * @private
	 */
	onRowDblClick: function (grid, rowIndex, event)
	{
		Zarafa.core.data.UIFactory.openViewRecord(grid.getSelectionModel().getSelected());
	}
});

Zarafa.onReady(function () {
	container.registerWidget(new Zarafa.core.ui.widget.WidgetMetaData({
		name : 'tasks',
		displayName : _('Tasks / To-Do'),
		widgetConstructor : Zarafa.widgets.folderwidgets.TasksWidget
	}));
});
Ext.namespace('Zarafa.widgets.folderwidgets');

/**
 * @class Zarafa.widgets.folderwidgets.AppointmentsWidget
 * @extends Zarafa.widgets.folderwidgets.AbstractFolderWidget
 *
 * Widget that displays the appointments for today, from the default
 * calendar.  It only displays appointments that occur on or after the
 * current time, so outdated information is never shown.
 *
 * Reload time is configurable per instance of the
 * widget (keys: 'reloadinterval', default 5 minutes).  These values are in
 * saved in miliseconds but displayed in seconds. The reload
 * interval is how often the calendar is fully reloaded from the
 * server, to show records that were added to the folder
 * outside of WebApp.
 */
Zarafa.widgets.folderwidgets.DatepickerWidget = Ext.extend(Zarafa.widgets.folderwidgets.AbstractFolderWidget, {

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config)
	{
		config = config || {};

		var store = new Zarafa.calendar.AppointmentStore();

		Ext.applyIf(config, {
			autoScroll: false,
			autoHeight: true,
			layout: 'hbox',
			folderType: 'calendar',
			store: store,
			items: [{
				xtype: 'zarafa.datepicker',
				ref: 'datePicker',
				hideLabel: true,
				showWeekNumber: true,
				showToday: true,
				width: '100%',
				listeners: {
					scope: this
				},
				handler: this.datePickerHandler
			}]
		});


		Zarafa.widgets.folderwidgets.DatepickerWidget.superclass.constructor.call(this, config);

		// FIXME Workaround for KW-1812 (not rendering busy days bold when coming back to current month)
		Ext.TaskMgr.start({
			run: this.updateFilter,
			interval: 2000, // every 2 seconds
			scope: this
		});
	},

	/**
	 * FIXME React on clicks: switch to calendar-view + selected date
	 * Event handler which is triggered when user clicks on any date
	 * @param {DatePicker} DatePicker object
	 * @param {Date} Date that was clicker
	 * @private
	 */
	datePickerHandler: Ext.emptyFn,

	/**
	 * Update the DatePicker when the calender is changed
	 * @private
	 */
	updateFilter: function ()
	{
		if (this.datePicker) {
			this.datePicker.show();
		}

		return Zarafa.widgets.folderwidgets.DatepickerWidget.superclass.updateFilter.apply(this, arguments);
	}
});

Zarafa.onReady(function () {
	container.registerWidget(new Zarafa.core.ui.widget.WidgetMetaData({
		name: 'datepicker',
		displayName: _('Datepicker'),
		widgetConstructor: Zarafa.widgets.folderwidgets.DatepickerWidget
	}));
});
Ext.namespace('Zarafa.widgets.folderwidgets');

/**
 * @class Zarafa.widgets.folderwidgets.CalendarFolderSelectionLink
 * @extends Ext.BoxComponent
 * @xtype zarafa.calendarfolderselectionlink
 *
 * This will show the currently selected {@link #folder} to the user, and allows
 * the user to select the desired folder to view appointments from.
 */
Zarafa.widgets.folderwidgets.CalendarFolderSelectionLink = Ext.extend(Ext.BoxComponent, {
	/**
	 * @cfg {String} fieldLabel The label which must be applied to template
	 * as a prefix to the list of attachments.
	 */
	emptyText :_('Select one...'),

	/**
	 * The folder which was selected by the user
	 * @property
	 * @type Zarafa.hierarchy.data.MAPIFolderRecord
	 */
	folder : undefined,

	/**
	 * @constructor
	 * @param {Object} config configuration object.
	 */
	constructor : function(config)
	{
		config = config || {};

		Ext.applyIf(config,{
			xtype: 'zarafa.calendarfolderselectionlink',
			border : false,
			autoScroll :true,
			anchor : '100%',
			tpl : new Ext.XTemplate(
				'<div class="zarafa-folder-link">' +
					'<tpl if="!Ext.isEmpty(values.display_name)">' +
						'&quot;{display_name:htmlEncode}&quot;' +
					'</tpl>' +
					'<tpl if="Ext.isEmpty(values.display_name)">' +
						'&quot;' + _('Unnamed folder') + '&quot;' +
					'</tpl>' +
				'</div>',
				{
					compiled : true
				}
			)
		});

		Zarafa.widgets.folderwidgets.CalendarFolderSelectionLink.superclass.constructor.call(this, config);
	},

	/**
	 * This function is called after the component has been rendered.
	 * This will update {@link Zarafa.widgets.folderwidgets.CalendarFolderSelectionLink} with selected folder
	 * and will register {@link #onClick} event handler.
	 * @private
	 */
	afterRender : function()
	{
		Zarafa.widgets.folderwidgets.CalendarFolderSelectionLink.superclass.afterRender.apply(this, arguments);

		this.update();
		this.mon(this.getEl(), 'click', this.onClick, this);
	},

	/**
	 * Called when user clicks on a {@link Zarafa.widgets.folderwidgets.CalendarFolderSelectionLink}
	 * It opens hierarchy folder selection dialog containing all calendar folders.
	 * @param {Ext.DataView} dataView Reference to this object
	 * @param {Number} index The index of the target node
	 * @param {HTMLElement} node The target node
	 * @param {Ext.EventObject} evt The mouse event
 	 * @protected
	 */
	onClick : function(dataView, index, node, evt)
	{
		var defaultCalendar = container.getHierarchyStore().getDefaultFolder('calendar');
		var folder = Ext.isEmpty(this.folder) ? defaultCalendar : this.folder;
		Zarafa.hierarchy.Actions.openFolderSelectionContent({
			folder : folder,
			hideTodoList: true,
			IPMFilter: 'IPF.Appointment',
			callback : this.update,
			scope : this,
			modal : true
		});
	},

	/**
	 * Update the contents of this dataview, this will apply the {@link #tpl} for
	 * the {@link #folder}.
	 * @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder The folder to show
	 */
	update : function(folder)
	{
		// Avoid 'folderupdate' for the first time when this component loads.
		if (folder) {
			this.folder = folder;
			this.fireEvent('folderupdate', folder);
		} else {
			folder = this.folder;
		}

 		var data = folder ? folder.data : { display_name : this.emptyText };
		Zarafa.widgets.folderwidgets.CalendarFolderSelectionLink.superclass.update.call(this, this.tpl.apply(data));
	}
});

Ext.reg('zarafa.calendarfolderselectionlink', Zarafa.widgets.folderwidgets.CalendarFolderSelectionLink);
