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,

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

		Ext.applyIf(config, {
			hasConfig: true
		});

		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.onStoreLoad, this, {single: true});

		// 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();
		}
	},

	/**
	 * When the store has finished loading, update the filter for the first time.
	 * @private
	 */
	onStoreLoad: function ()
	{
		// 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

		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,

	/**
	 * Helper function called when applying custom style and content for the
	 * row-body.
	 * Renders colored boxes per category with the first letter of the
	 * category and its full name in a tooltip.
	 *
	 * @param {Ext.data.Record} record The {@link Ext.data.Record Record} corresponding to the current row.
	 * @return {String} the colored boxes in HTML-format
	 * @private
	 */
	renderCategories: function (record)
	{
		var valueCategories = ''; // will contain list of categories as colored squares

		var categories = Zarafa.common.categories.Util.getCategories(record);
		categories.forEach(function (category, i) {
			var letter = category.substring(0, 1);
			var color = Zarafa.common.categories.Util.getCategoryColor(category);
			valueCategories = '<span class="advanced-folderwidget-category-box" style="background-color: ' + color + '" title="' + Ext.util.Format.htmlEncode(category) + '">' + Ext.util.Format.htmlEncode(letter) + '</span>';
		});

		// Add an '|' as separator if list of categories is not empty
		var separator = valueCategories ? " | " : "";

		return valueCategories + separator;
	},

	/**
	 * 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 win = new Ext.Window({
			title: _('Configure widget'),
			layout: 'fit',
			width: 320,
			height: 130,
			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()
				],
				buttons: [{
					text: _('Close'),
					scope: this,
					handler: function () {
						win.close();
					}
				}]
			}]
		});

		win.show(this);
	},

	/**
	 * 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 whcih 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);
		}
	}
});
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, {

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

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

		Ext.applyIf(config, {
			height: this.get('widgetheight') || 300,
			autoScroll: true,
			layout: 'fit',
			folderType: 'calendar',
			store: store,
			items: [{
				xtype: 'zarafa.gridpanel',
				store: store,
				hideHeaders: true,
				loadMask: {
					msg: _('Loading appointments') + '...'
				},
				sm: new Ext.grid.RowSelectionModel({
					singleSelect: true
				}),
				viewConfig: {
					deferEmptyText: false,
					emptyText: '<div class="emptytext">' + _('No appointments for today.') + '</div>',
					forceFit: true,
					enableRowBody: true,
					rowSelectorDepth: 15,
					getRowClass: this.viewConfigGetRowClass,
					scope: this
				},
				colModel: new Ext.grid.ColumnModel({
					columns: [{
						header: _('Time'),
						dataIndex: 'startdate',
						renderer: this.dummyColumnRenderer
					}]
				}),
				listeners: {
					'rowcontextmenu': this.onRowContextMenu,
					'rowdblclick': this.onRowDblClick,
					scope: this
				}
			}]
		});

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

	/**
	 * 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) {
			var now = new Date();
			var today = now.clearTime();
			var dueDate = today.add(Date.DAY, 10);

			this.store.load({
				folder: this.folder,
				params: {
					restriction: {
						startdate: now.getTime() / 1000,
						// FIXME: Create Config-Option for duedate
						duedate: dueDate.getTime() / 1000,
						// FIXME: Create Config-Option for limit
						limit: 10
					}
				}
			});
		}
	},

	/**
	 * Update the filter with the current time.  Items that end
	 * before now are removed.
	 * @private
	 */
	updateFilter: function ()
	{
		this.store.filterBy(function (record) {
			var now = new Date();
			var startdate = record.get('startdate') || now;
			var duedate = record.get('duedate') || now;
			return (startdate >= now || duedate >= now) && startdate < now.clearTime().add(Date.DAY, 1);
		}, this);
	},

	/**
	 * Just needed to make sure the 'columns'-object renders a line with height=0px
	 *
	 * @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
	 */
	dummyColumnRenderer: function (value, metaData, record)
	{
		metaData.attr = "style='padding:0; margin:0; height:0px; '";
		return "";
	},

	/**
	 * 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 an appointment these categories will be displayed in a colored square naming
	 * the first letter of the category and its full name in a tooltip.
	 *
	 * @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)
	{

		var iconToString = function (icon) {
			return '<img src="' + icon.src + '" /> ';
		};

		var value = '<table width="100%"><tbody>';

		/**
		 * this.startdateArray is used to simulate some "grouping" in the table
		 * as 'this' is not in the context of this class, the array has to be
		 * initialized here.
		 */
		if (!this.startdateArray) {
			this.startdateArray = new Array();
		}

		var recordDate = record.get("startdate");
		var todaysDate = new Date;

		this.startdateArray[rowIndex] = recordDate;

		if ((!this.startdateArray[rowIndex - 1]) || (this.startdateArray[rowIndex - 1].add(Date.DAY, 1).clearTime() <= recordDate)) {
			// the appointment before was on another day then this appointment

			value += '<tr><td><div class="folderwidget-groupedview">';

			if (recordDate <= todaysDate.add(Date.DAY, 1).clearTime()) {
				value += _("Today");
			} else if (recordDate < todaysDate.add(Date.DAY, 2).clearTime()) {
				value += _("Tomorrow");
			} else if (recordDate <= todaysDate.add(Date.DAY, 7).clearTime()) {
				value += recordDate.format('l');
			} else {
				value += recordDate.format(_("d.m.Y"));
			}

			value += '</div></td></tr>';
		}

		// Row to display time + recurrance + location
		value += '<tr><td>';

		if (record.get('alldayevent')) {
			value += _('All Day');
		} else {
			value += record.get("startdate").format(_("G:i")) + ' - ' + record.get("duedate").format(_("G:i"));
		}

		var recurringPattern = record.get('recurring_pattern');
		if (recurringPattern) {
			if (record.get('exception') === true) {
				value += ' <span title="' + recurringPattern + '">' + iconToString(Zarafa.calendar.ui.IconCache.getExceptionIcon()) + '</span>';
			} else {
				value += ' <span title="' + recurringPattern + '">' + iconToString(Zarafa.calendar.ui.IconCache.getRecurringIcon()) + '</span>';
			}
		}

		// Row to display categories + subject + private-flag
		value += '</td></tr><tr><td>';

		value += Zarafa.widgets.folderwidgets.AppointmentsWidget.superclass.renderCategories(record);

		value += record.get("subject");

		if (record.get('private') === true) {
			value += ' ' + iconToString(Zarafa.calendar.ui.IconCache.getPrivateIcon());
		}

		value += '</td></tr></tbody></table>';

		rowParams.body = value;

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

	/**
	 * Render the subject, which is the time span + the subject of
	 * the appointment.  The passed value is the subject of the
	 * appointment, the rest of the data is retrieved through record.
	 *
	 * @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
	 * @param {Number} rowIndex The index of the rendered row
	 * @param {Number} colIndex The index of the rendered column
	 * @param {Ext.data.Store} store The store to which the record belongs
	 * @private
	 */
	subjectRenderer : function(value, metaData, record, rowIndex, colIndex, store) {
			var now = new Date();
			// # TRANSLATORS: See http://docs.sencha.com/ext-js/3-4/#!/api/Date for the meaning of these formatting instructions
			var starttime = (record.get('startdate') || now).format(_("G:i"));
			// # TRANSLATORS: See http://docs.sencha.com/ext-js/3-4/#!/api/Date for the meaning of these formatting instructions
			var duetime = (record.get('duedate') || now).format(_("G:i"));
			if ((record.get('startdate') || now) < now) {
				metaData.attr = "style='color: #888; font-weight: bold;'";
			} else {
				metaData.attr = "style='font-weight: bold;'";
			}

			var icons = '';
			var iconToString = function(icon) { return '<img src="' + icon.src + '" /> '; };

			if (record.get('private') === true) {
				icons += iconToString(Zarafa.calendar.ui.IconCache.getPrivateIcon());
			}
			if (record.get('recurring') === true) {
				if (record.get('exception') === true) {
					icons += iconToString(Zarafa.calendar.ui.IconCache.getExceptionIcon());
				} else {
					icons += iconToString(Zarafa.calendar.ui.IconCache.getRecurringIcon());
				}
			}

			if (record.get('alldayevent')) {
				// # TRANSLATORS: {0} is the subject of an all-day
				// # appointment, {1} is the html code to display
				// # icons.
				return String.format(_("Today: {1} {0}"), Ext.util.Format.htmlEncode(value), icons);
			}

			return String.format("{1}-{2}: {3} {0}", Ext.util.Format.htmlEncode(value), starttime, duetime, icons);
	},

	/**
	 * Add additional information to the row, in this case the location of
	 * the meeting.
	 * TODO: add a preview of the body here.
	 * The location is rendered in its own table for future use.
	 *
	 * @param record The record being displayed.  If it has no
	 * location set, then the table is omitted entirely.
	 * @param {Number} rowIndex The index of the rendered row
	 * @param {Object} rowParams The row parameters
	 * @param {Ext.data.Store} store The store in which the record is placed
	 * @returns A string containing a space separated list of css
	 * classes to apply.  It will always contain 'today-item', and a
	 * second class that indicates the intended busy state of the
	 * appointment.
	 * @private
	 */
	applyRowClass: function (record, rowIndex, rowParams, store)
	{
		var location = record.get('location');
		if (location) {
			rowParams.body = '<table style="width: 100%; padding: 0; border-spacing: 0;">';
			rowParams.body += String.format('<tr><td style="width: 100%; font-size: 80%;"><i>{0}</i></td></tr>',
				Ext.util.Format.htmlEncode(location));
			rowParams.body += '</table>';
		} else {
			rowParams.body = '';
		}

		var css = 'today-item ';
		var busystatus = record.get('busystatus');
		switch (busystatus) {
			case Zarafa.core.mapi.BusyStatus.FREE:
				return css + 'today-free';
			case Zarafa.core.mapi.BusyStatus.TENTATIVE:
				return css + 'today-tentative';
			case Zarafa.core.mapi.BusyStatus.BUSY:
				return css + 'today-busy';
			case Zarafa.core.mapi.BusyStatus.OUTOFOFFICE:
				return css + 'today-outofoffice';
			default:
				return css + 'today-unknown';
		}
	},

	/**
	 * 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 : _('Today\'s Appointments'),
		widgetConstructor : Zarafa.widgets.folderwidgets.AppointmentsWidget
	}));
});
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.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, {
			height : 200,
			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 Ext.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
		store.setRestriction({
			'task': Zarafa.core.data.RestrictionFactory.dataResProperty(
				'status',
				Zarafa.core.mapi.Restrictions.RELOP_NE,
				Zarafa.core.mapi.TaskStatus.COMPLETE
			)
		});

		Ext.applyIf(config, {
			height: this.get('widgetheight') || 300,
			autoScroll: true,
			layout: 'fit',
			folderType: 'todolist',
			store: store,
			items: [{
				xtype: 'zarafa.gridpanel',
				store: store,
				loadMask: {
					msg: _('Loading tasks') + '...'
				},
				sm: new Ext.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 Date"),
						tooltip: _("Due Date"),
						dataIndex: "duedate",
						renderer: this.dueDateRenderer
					}, {
						header: "<p class='icon_importance'>&nbsp;</p>",
						tooltip: _("Importance"),
						dataIndex: "importance",
						align: "center",
						width: 55,
						renderer: Zarafa.common.ui.grid.Renderers.importance
					}, {
						header: "%",
						tooltip: _("% Completed"),
						dataIndex: "percent_complete",
						align: "center",
						width: 80,
						renderer: Zarafa.common.ui.grid.Renderers.percentage
					}, {
						header: _('Owner'),
						tooltip: _("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);
	},

	/**
	 * Render the due date in the form "d/m/Y" and add color red if
	 * due date is already reached
	 *
	 * @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
	 */
	dueDateRenderer: function (value, metaData, record)
	{
		var dateNow = new Date();
		var dateDue = record.get("duedate");
		metaData.attr = "";

		if (!dateDue) {
			value = _("none");
		} else if (dateDue < dateNow.clearTime().add(Date.DAY, 1)) {
			value = _("Today");
			metaData.attr = "style='color: #F00;'";
		} else if (dateDue < dateNow.clearTime().add(Date.DAY, 2)) {
			value = _("Tomorrow");
		} else {
			value = dateDue.format(_("d/m/Y"));
		}

		return String.format("{0}", value);
	},

	/**
	 * 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 title="' + Ext.util.Format.htmlEncode(owner) + '">' + Ext.util.Format.htmlEncode(initials) + '</span>';
	},

	/**
	 * 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 in a colored square naming
	 * the first letter of the category and its full name in a tooltip.
	 *
	 * @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)
	{
		var valueSubject = record.get("subject"); // Subject to be displayed
		var valueCategories = Zarafa.widgets.folderwidgets.TasksWidget.superclass.renderCategories(record);

		// Add color red if due date is reached
		var dateNow = new Date();
		var dateDue = record.get("duedate");
		var color = "";

		if (dateDue && (dateDue < dateNow.clearTime().add(Date.DAY, 1))) {
			color = 'style="color:#F00;"';
		}

		rowParams.body = String.format('<div class="folderwidget-task-row" {2}>{1}{0}</div>', valueSubject, valueCategories, color);

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

	/**
	 * 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'),
		widgetConstructor : Zarafa.widgets.folderwidgets.TasksWidget
	}));
});
