Ext.namespace('Zarafa.common');

/**
 * @class Zarafa.common.Actions
 * Common actions which can be used within {@link Ext.Button buttons}
 * or other {@link Ext.Component components} with action handlers.
 * @singleton
 */
Zarafa.common.Actions = {
	/**
	 * The internal 'iframe' which is hidden from the user, which is used for downloading
	 * attachments. See {@link #doOpen}.
	 * @property
	 * @type Ext.Element
	 */
	downloadFrame : undefined,
	/**
	 * Open a {@link Zarafa.common.dialogs.CopyMoveContentPanel CopyMoveContentPanel} for
	 * copying or moving {@link Zarafa.core.data.IPMRecord records} to the
	 * preferred destination folder.
	 *
	 * @param {Zarafa.core.data.IPMRecord} records The record which must be copied or moved.
	 * @param {Object} config (optional) Configuration object to create the ContentPanel
	 */
	openCopyMoveContent : function(records, config)
	{
		config = Ext.applyIf(config || {}, {
			modal : true
		});
		var componentType = Zarafa.core.data.SharedComponentType['common.dialog.copymoverecords'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, records, config);
	},

	/**
	 * Opens a {@link Zarafa.common.recurrence.dialogs.RecurrenceContentPanel RecurrenceContentPanel} for configuring
	 * the recurrence of the given {@link Zarafa.core.data.IPMRecord record}.
	 *
	 * @param {Zarafa.core.data.IPMRecord} records The record for which the recurrence must be configured.
	 * @param {Object} config Configuration object
	 */
	openRecurrenceContent : function(records, config)
	{
		if (Array.isArray(records) && !Ext.isEmpty(records)) {
			records = records[0];
		}

		config = Ext.applyIf(config || {}, {
			autoSave : true,
			modal : true
		});

		var componentType = Zarafa.core.data.SharedComponentType['common.dialog.recurrence'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, records, config);
	},

	/**
	 * Opens the {@link Zarafa.common.categories.ui.CategoriesContextMenu CategoriesContextMenu} for
	 * the given {@link Zarafa.core.data.IPMRecord records}.
	 *
	 * @param {Zarafa.core.data.IPMRecord} records The record, or records for which the categories
	 * menu will be shown.
	 * @param {Array} position An array with the [x, y] position where the menu will be shown.
	 */
	openCategoriesMenu : function(records, position)
	{
		Zarafa.core.data.UIFactory.openContextMenu(Zarafa.core.data.SharedComponentType['common.contextmenu.categories'], records, {
			position : position
		});
	},

	/**
	 * Opens the {@link Zarafa.common.flags.ui.FlagsMenu FlagsMenu} for
	 * the given {@link Zarafa.core.data.IPMRecord records}.
	 *
	 * @param {Zarafa.core.data.IPMRecord} records The record, or records for which the flags
	 * menu will be shown.
	 * @param {Array} position An array with the [x, y] position where the menu will be shown.
	 * @param {Boolean} shadowEdit True to create copy of this record and push it to ShadowStore.
	 */
	openFlagsMenu : function(records, position, shadowEdit)
	{
		if (!Ext.isArray(records)) {
			records = [ records ];
		}

		var component = Zarafa.core.data.SharedComponentType['common.contextmenu.flags'];
		Zarafa.core.data.UIFactory.openContextMenu(component, records, {
			position : position,
			shadowEdit : shadowEdit,
			store : records[0].getStore()
		});
	},

	/**
	 * Opens a {@link Zarafa.common.flag.dialogs.CustomFlagContentPanel CustomFlagContentPanel} for set
	 * the custom flag and reminder to given {@link Zarafa.core.data.IPMRecord records}.
	 *
	 * @param {Zarafa.core.data.IPMRecord} records The record, or records for which the custom
	 * flag or/and reminder going to set.
	 * @param {Object} config (optional) Configuration object for creating the ContentPanel
	 */
	openCustomFlagContent : function(records, config)
	{
		config = Ext.applyIf(config || {}, {
			modal : true,
			resizable : false
		});
		var componentType = Zarafa.core.data.SharedComponentType['common.flags.dialogs.customflag'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, records, config);
	},

	/**
	 * Opens a {@link Zarafa.common.categories.dialogs.CategoriesContentPanel CategoriesContentPanel} for configuring
	 * the categories of the given {@link Zarafa.core.data.IPMRecord records}.
	 *
	 * @param {Zarafa.core.data.IPMRecord} records The record, or records for which the categories
	 * must be configured
	 * @param {Object} config (optional) Configuration object for creating the ContentPanel
	 */
	openCategoriesContent : function(records, config)
	{
		if (!Array.isArray(records)) {
			records = [ records ];
		}

		config = Ext.applyIf(config || {}, {
			autoSave : true,
			modal : true
		});

		// Callback function added in config object if
		// selected records is belongs to search store.
		var store = records[0].getStore();
		if(Ext.isFunction(store.isAdvanceSearchStore) && store.isAdvanceSearchStore()) {
			config.callback = function() {
				Ext.each(records, function(record){
					var foundRecord = this.record.find(function(rec){
						return rec.get('entryid') === record.get('entryid');
					});
					record.applyData(foundRecord);
				}, this);
			};
		}

		var componentType = Zarafa.core.data.SharedComponentType['common.dialog.categories'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, records, config);
	},

	/**
	 * Opens a {@link Zarafa.common.categories.dialogs.NewCategoryPanel NewCategoryPanel} for creating
	 * a new category.
	 *
	 * @param {Object} config (optional) Configuration object for creating the NewCategoryPanel
	 */
	openNewCategoryContent : function(config)
	{
		config = Ext.applyIf(config || {}, {
			modal : true
		});
		var componentType = Zarafa.core.data.SharedComponentType['common.categories.dialogs.newcategory'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config);
	},

	/**
	 * Opens a {@link Zarafa.common.categories.dialogs.RenameCategoryPanel RenameCategoryPanel} for renaming
	 * a standard category.
	 *
	 * @param {Object} config (optional) Configuration object for renaming the {@link Zarafa.common.categories.dialogs.RenameCategoryPanel RenameCategoryPanel}.
	 */
	openRenameCategoryContent : function(config)
	{
		config = Ext.applyIf(config || {}, {
			modal : true
		});
		var componentType = Zarafa.core.data.SharedComponentType['common.categories.dialogs.renamecategory'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config);
	},

	/**
	 * Opens a {@link Zarafa.common.attachment.dialogs.AttachItemContentPanel Attach Item Content Panel} which is used
	 * to attach an item as embedded attachment to a message.
	 *
	 * @param {Zarafa.core.data.MAPIRecord} record record that will be used to add embedded attachment
	 * @param {Object} config (optional) Configuration object for creating the ContentPanel
	 */
	openAttachItemSelectionContent : function(record, config)
	{
		config = Ext.applyIf(config || {}, {
			modal : true
		});

		var componentType = Zarafa.core.data.SharedComponentType['common.attachment.dialog.attachitem'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, record, config);
	},

	/**
	 * Opens a {@link Zarafa.core.ui.widget.WidgetContentPanel}
	 * for inserting widgets into the {@link Zarafa.core.ui.widget.WidgetPanel}
	 * @param {Object} config (optional) Configuration object for creating the ContentPanel
	 */
	openWidgetsContent : function(config)
	{
		var componentType = Zarafa.core.data.SharedComponentType['common.dialog.widgets'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config);
	},

	/**
	 * Will open the View ContentPanel for a recipient, before opening the recipient it will
	 * first check the exact type of the recipient to see if it is an AddressBook item
	 * or personal contact. If either of those two the record is converted to assure the
	 * correct panel is opened.
	 * @param {Zarafa.core.data.IPMRecipientRecord} recipient The recipient which must be opened
	 * @param {Object} config configuration object.
	 */
	openViewRecipientContent : function(recipient, config)
	{
		if (recipient.isResolved()) {
			if (recipient.isPersonalContact()) {
				// A personal contact needs to be converted to a contact so the correct panel can be shown.
				recipient = recipient.convertToContactRecord();
				// FIXME: We put the abRecord into the ShadowStore to be able
				// to open it, and obtain all details. However, we also need to
				// find a point where we can remove it again.
				container.getShadowStore().add(recipient);
			} else if (recipient.isPersonalDistList()) {
				// A personal distlist needs to be converted to a distlist so the correct dialog can be shown.
				recipient = recipient.convertToDistListRecord();
				// FIXME: We put the abRecord into the ShadowStore to be able
				// to open it, and obtain all details. However, we also need to
				// find a point where we can remove it again.
				container.getShadowStore().add(recipient);
			} else if (!recipient.isOneOff()) {
				// A addressbook item needs to be converted to a AddressBook record so the correct dialog is shown.
				recipient = recipient.convertToABRecord();
				// FIXME: We put the abRecord into the ShadowStore to be able
				// to open it, and obtain all details. However, we also need to
				// find a point where we can remove it again.
				container.getShadowStore().add(recipient);
			}

			config = Ext.applyIf(config || {}, { manager : Ext.WindowMgr });
			Zarafa.core.data.UIFactory.openViewRecord(recipient, config);
		}
	},

	/**
	 * Opens a {@link Zarafa.common.delegates.dialogs.DelegatePermissionContentPanel DelegatePermissionContentPanel} for editing
	 * delegate permissions of a single delegate
	 * @param {Zarafa.common.delegates.data.DelegateRecord} delegateRecord record that should be opened in
	 * {@link Zarafa.common.delegates.dialogs.DelegatePermissionContentPanel DelegatePermissionContentPanel}.
	 * @param {Object} config configuration object that should be passed to {@link Zarafa.common.delegates.dialogs.DelegatePermissionContentPanel DelegatePermissionContentPanel}.
	 */
	openDelegatePermissionContent : function(record, config)
	{
		if(!record) {
			// can not continue without a record
			return;
		}

		config = config || {};
		Ext.apply(config, {
			modal : true
		});

		Zarafa.core.data.UIFactory.openCreateRecord(record, config);
	},

 	/**
	 * Opens a {@link @link Zarafa.common.sendas.dialogs.SendAsEditContentPanel SendAsEditContentPanel} for editing
	 * user name and email address of a sendAs.
	 * @param {Ext.data.Record} record record that should be opened in
	 * {@link Zarafa.common.sendas.dialogs.SendAsEditContentPanel SendAsEditContentPanel}.
	 * @param {Object} config configuration object that should be passed to {@link Zarafa.common.sendas.dialogs.SendAsEditContentPanel SendAsEditContentPanel}.
	 */
	openSendAsRecipientContent : function(record, config)
	{
		if(!record) {
			// can not continue without a record
			return;
		}
		var componentType = Zarafa.core.data.SharedComponentType['common.sendas.dialog.sendaseditcontentpanel'];

		config = config || {};
		Ext.apply(config, {
			modal : true
		});

		Zarafa.core.data.UIFactory.openLayerComponent(componentType, record, config);
	},

	/**
	 * Opens a {@link Zarafa.common.rules.dialogs.RulesEditContentPanel RulesEditContentPanel} for editing
	 * a single {@link Zarafa.common.rules.data.RulesRecord RulesRecord}.
	 * @param {Zarafa.common.rules.data.RulesRecord} record record to edit in
	 * {@link Zarafa.common.rules.dialogs.RulesEditContentPanel RulesEditContentPanel}.
	 * @param {Object} config config object that will be passed to {@link Zarafa.core.data.UIFactoryLayer UIFactoryLayer}.
	 */
	openRulesEditContent : function(record, config)
	{
		if(!record) {
			// can not continue without a record
			return;
		}

		config = Ext.apply(config || {}, {
			modal : true
		});

		Zarafa.core.data.UIFactory.openCreateRecord(record, config);
	},

	/**
	 * Will create an object of {@link Zarafa.common.attachment.ui.AttachmentDownloader AttachmentDownloader}
	 * and call {@link Zarafa.common.attachment.ui.AttachmentDownloader#downloadMessage} method to start download the message as file
	 * by setting the dialogFrame's location to the download URL of the given {@link Zarafa.core.data.IPMRecord records}.
	 * @param {Zarafa.core.data.IPMRecord} records The record, or records which user want to save as file.
	 * @param {Boolean} allAsZip (optional) True to downloading all the attachments as ZIP
	 */
	openSaveEmlDialog : function(records, allAsZip)
	{
		records = [].concat(records);

		var downloadComponent;
		if(!allAsZip){
			for (var i = 0; i < records.length; i++) {
				var record = records[i];
				// Create separate iframe for each url to handle requests individually
				downloadComponent = new Zarafa.common.attachment.ui.AttachmentDownloader();
				downloadComponent.downloadItem(record.getDownloadMessageUrl(false));
			}
		} else {
			downloadComponent = new Zarafa.common.attachment.ui.AttachmentDownloader();
			downloadComponent.downloadMessageAsZip(records);
		}
	},

	/**
	 * Opens a PrintDialog for printing the contents of the given {@link Zarafa.core.data.IPMRecord records}.
	 *
	 * @param {Zarafa.core.data.IPMRecord} records The record, or records for which the print will be displayed.
	 * @param {Object} config (optional) Configuration object
	 */
	openPrintDialog: function(records, config)
	{
		if (Ext.isEmpty(records)) {
			return;
		} else if (Array.isArray(records)) {
			if (records.length > 1) {
				Ext.MessageBox.alert(_('Print'), _('Printing of multiple items has not been implemented.'));
				return;
			} else {
				// We only need the first record
				records = records[0];
			}
		}

		var openHandler = function (store, record) {
			if (store) {
				if (this !== record) {
					return;
				}
				store.un('open', openHandler, record);
			}

			var componentType = Zarafa.core.data.SharedComponentType['common.printer.renderer'];
			var component = container.getSharedComponent(componentType, record);
			if (component) {
				var renderer = new component();
				renderer.print(record);
			} else  {
				if (record instanceof Zarafa.core.data.MAPIRecord) {
					Ext.MessageBox.alert(_('Print'), _('Printing of this item is not yet available') + '\n' + _('Item type: ') + record.get('message_class'));
				} else {
					Ext.MessageBox.alert(_('Print'), _('Printing of this view is not yet available'));
				}
			}
		};

		if (records instanceof Zarafa.core.data.MAPIRecord && !records.isOpened()) {
			records.getStore().on('open', openHandler, records);
			records.open();
		} else {
			openHandler(undefined, records);
		}
	},

	/**
	 * Opens a {@link Zarafa.common.checknames.dialogs.CheckNamesContentPanel CheckNamesContentPanel}
	 *
	 * @param {Array} array of checkNames
	 * @param {Zarafa.core.data.IPMRecipientRecord} recipientrecord
	 * @param {Object} config (optional) Configuration object for creating the content panel
	 */
	openCheckNamesContent : function(checkNamesData, recipientRecord, config)
	{
		var componentType = Zarafa.core.data.SharedComponentType['common.dialog.checknames'];
		config = Ext.applyIf(config || {}, {
			checkNamesData : checkNamesData,
			modal: true
		});
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, recipientRecord, config);
	},

	/**
	 * Opens a {@link Zarafa.common.reminder.dialogs.ReminderContentPanel remindercontentpanel}
	 * @param {Zarafa.common.reminder.ReminderRecord} records Records for which the reminder content panel will be displayed.
	 * @param {Object} config (optional) Configuration object
	 */
	openReminderContent : function(records, config)
	{
		var componentType = Zarafa.core.data.SharedComponentType['common.dialog.reminder'];
		var component = container.getSharedComponent(componentType, records);

		config = Ext.applyIf(config || {}, {
			modal : false,
			manager : Ext.WindowMgr
		});

		// check if panel is already open
		var contentPanelInstances = Zarafa.core.data.ContentPanelMgr.getContentPanelInstances(component);

		// there can be no reminder dialog or only one reminder dialog
		// multiple reminder dialogs are not allowed
		if(contentPanelInstances.getCount() === 0) {
			// create a new reminder dialog, if there are any reminders to show
			if(records.length > 0) {
				Zarafa.core.data.UIFactory.openLayerComponent(componentType, records, config);
			}
		} else if (contentPanelInstances.getCount() === 1) {
			// we already have a reminder dialog open, use it
			var reminderDialog = contentPanelInstances.first();

			if(records.length > 0) {
				// there are reminders to show, so give focus to existing reminder dialog
				reminderDialog.focus();
			} else {
				// no reminders to show, close existing dialog
				reminderDialog.close();
			}
		}
	},

	/**
	 * Function will first convert the {@link Zarafa.common.reminder.ReminderRecord ReminderRecord} to an
	 * {@link Zarafa.core.data.IPMRecord IPMRecord} based on its message_class property and then pass that
	 * {@link Zarafa.core.data.IPMRecord IPMRecord} to {@link Zarafa.core.ui.ContentPanel ContentPanel} to open it.
	 * @param {Zarafa.common.reminder.data.ReminderRecord|Zarafa.common.reminder.data.ReminderRecord[]} record
	 * The reminder record/records which should be opened.
	 * @param {Object} config configuration object.
	 */
	openReminderRecord: function(record, config)
	{
		config = config || {};

		if(Array.isArray(record)){
			Ext.each(record, this.openReminderRecord, this);
			return;
		}

		// convert reminder record to proper ipmrecord
		record = record.convertToIPMRecord();

		// we will always open the record into a dialog because reminders are also displayed in a dialog
		Ext.applyIf(config, {
			manager : Ext.WindowMgr,
			modal : true,
			showModalWithoutParent : true
		});

		if(record) {
			Zarafa.core.data.UIFactory.openViewRecord(record, config);
		}
	},

	/**
	 * Opens a {@link Zarafa.common.dialogs.MessageBox.select MessageBox} for
	 * selecting if either a recurrence or the entire series must be opened for the Recurring
	 * Message.
	 *
	 * @param {Function} handler The handler which is invoked with the selected value
	 * from the dialog. This function only takes one argument and is either 'recurrence_occurence'
	 * when the single-occurence was selected or 'recurrence_series' when the series was selected.
	 * @param {Object} scope (optional) The scope on which the handler must be invoked.
	 */
	// TODO: Merge with deleteRecurringSelectionContentPanel
	openRecurringSelectionContent : function(record, handler, scope)
	{
		var title = _('Recurring Message');
		var text =  _('This is a recurring message. Do you want to open only this occurrence or the series?');

		if (record.isMessageClass('IPM.Appointment', true)) {
			if (record.get('meeting') == Zarafa.core.mapi.MeetingStatus.NONMEETING) {
				title = _('Recurring Appointment');
				text =  _('This is a recurring appointment. Do you want to open only this occurrence or the series?');
			} else {
				title = _('Recurring Meeting Request');
				text =  _('This is a recurring meeting request. Do you want to open only this occurrence or the series?');
			}
		} else if (record.isMessageClass('IPM.TaskRequest', true)) {
			title = _('Recurring Task Request');
			text =  _('This is a recurring task request. Do you want to open only this occurrence or the series?');
		}

		Zarafa.common.dialogs.MessageBox.select(
			title, text, handler, scope, [{
				boxLabel: _('Open this occurrence'),
				id : 'recurrence_occurence',
				name: 'select',
				checked: true
			},{
				boxLabel: _('Open the series'),
				id : 'recurrence_series',
				name: 'select'
			}]
		);
	},

	/**
	 * Opens a {@link Zarafa.common.dialogs.MessageBox.select MessageBox} for
	 * selecting if either a recurrence or the entire series must be paste for the Recurring
	 * appointment/meeting.
	 *
	 * @param {Function} handler The handler which is invoked with the selected value
	 * from the dialog. This function only takes one argument and is either 'recurrence_occurence'
	 * when the single-occurence was selected or 'recurrence_series' when the series was selected.
	 * @param {Object} scope (optional) The scope on which the handler must be invoked.
	 */
	copyRecurringSelectionContent : function(record, handler, scope)
	{
		var title = _('Paste Recurring {0}');
		var text =  _('This is a recurring {0}. Do you want to paste only this occurrence or the series?');

		var msgText = _('message');
		if (record.isMessageClass('IPM.Appointment', true)) {
			msgText = record.isMeeting() ? _('meeting request') : _('appointment');
		}

		title = String.format(title,Ext.util.Format.capitalize(msgText));
		text =  String.format(text, msgText);

		Zarafa.common.dialogs.MessageBox.select(
			title, text, handler, scope, [{
				boxLabel: _('Paste this occurrence only'),
				id : 'recurrence_occurence',
				name: 'select',
				checked: true,
				showButtonText : 'ok',
				hideButtonText : 'next'
			},{
				boxLabel: _('Paste the series...'),
				id : 'recurrence_series',
				name: 'select',
				showButtonText : 'next',
				hideButtonText : 'ok'
			}],
			undefined,
			[{
				text : _('Ok'),
				name : 'ok'
			}, {
				text : _('Cancel'),
				name : 'cancel'
			}]
		);
	},

	/**
	 * Opens a {@link Zarafa.common.dialogs.MessageBox.select MessageBox} for
	 * selecting if either a recurrence or the entire series must be deleted.
	 *
	 * @param {Function} handler The handler which is invoked with the selected value
	 * from the dialog. This function only takes one argument and is either 'recurrence_occurence'
	 * when the single-occurence was selected or 'recurrence_series' when the series was selected.
	 * @param {Object} scope (optional) The scope on which the handler must be invoked.
	 */
	// TODO: Merge with openRecurringSelectionContentPanel
	deleteRecurringSelectionContent : function(record, handler, scope)
	{
		var title = _('Recurring Message');
		var text =  _('This is a recurring message. Do you want to delete only this occurrence or the series?');

		if (record.isMessageClass('IPM.Appointment', true)) {
			if (record.get('meeting') == Zarafa.core.mapi.MeetingStatus.NONMEETING) {
				title = _('Recurring Appointment');
				text =  _('This is a recurring appointment. Do you want to delete only this occurrence or the series?');
			} else {
				title = _('Recurring Meeting Request');
				text =  _('This is a recurring meeting request. Do you want to delete only this occurrence or the series?');
			}
		} else if (record.isMessageClass('IPM.TaskRequest', true)) {
			title = _('Recurring Task Request');
			text =  _('This is a recurring task request. Do you want to delete only this occurrence or the series?');
		}

		Zarafa.common.dialogs.MessageBox.select(
			title, text, handler, scope, [{
				boxLabel: _('Delete this occurrence'),
				id : 'recurrence_occurence',
				name: 'select',
				checked: true
			},{
				boxLabel: _('Delete the series'),
				id : 'recurrence_series',
				name: 'select'
			}]
		);
	},

	/**
	 * Opens a {@link Zarafa.common.dialogs.MessageBox.select MessageBox} for
	 * selecting if either a update need to be send to meeting Organizer or silently deleted items.
	 *
	 * @param {Function} handler The handler which is invoked with the selected value
	 * from the dialog. This function only takes one argument and is either 'sendResponseOnDelete'
	 * when the delete and response was selected or 'onResponseOnDelete' when the delete without response was selected.
	 * @param {Object} scope (optional) The scope on which the handler must be invoked.
	 */
	// TODO: may be Merge with deleteRecurringSelectionContentPanel
	deleteMeetingRequestConfirmationContent : function(record, handler, scope)
	{
		var title = _('Confirm Delete');
		var acceptedText = _('This "{0}" meeting was already accepted.');
		var noResponsedText = _('You have not responded to the meeting request "{0}".');

		var text;
		if(record.get('responsestatus') == Zarafa.core.mapi.ResponseStatus.RESPONSE_NOT_RESPONDED){
			text = String.format(noResponsedText, record.get('subject'));
		}else{
			text = String.format(acceptedText, record.get('subject'));
		}

		Zarafa.common.dialogs.MessageBox.select(
			title, text, handler, scope, [{
				boxLabel: _('Delete and send a response to the meeting organizer'),
				id : 'sendResponseOnDelete',
				name: 'select',
				checked: true
			},{
				boxLabel: _('Delete without sending'),
				id : 'noResponseOnDelete',
				name: 'select'
			}]
		);
	},

	/**
	 * Deletes all {@link Zarafa.core.data.IPMRecord records} from the {@link Zarafa.core.data.IPMStore store}.
	 * If the records are deleted from the To-do list the deleting is delegated to
	 * {@link Zarafa.task.Actions.deleteRecordsFromTodoList} otherwise it is delegated to {#doDeleteRecords}
	 *
	 * @param {Array} records The array of records which must be deleted.
	 * @param {Boolean} askOcc (private) False to prevent a dialog to appear to ask if the occurence or series must
	 * be deleted
	 * @param {Boolean} softDelete (optional) true to directly soft delete record(s) skipping deleted-items
	 * folder, false otherwise
	 *
	 * FIXME: This introduces Calendar-specific and To-do list (Task)-specific actions into the Common Context,
	 * but there is no clean solution for this at this time. But we need to split this up into context-specific
	 * actions while maintaining this single-entrypoint for deleting records.
	 */
	deleteRecords : function(records, askOcc, softDelete)
	{
		if (Ext.isEmpty(records)) {
			return;
		}
		if (!Array.isArray(records)) {
			records = [ records ];
		}

		// Check if the records are deleted from the todolist
		var recordsFolderEntryid = records[0].getStore().entryId;
		var folder = container.getHierarchyStore().getFolder(recordsFolderEntryid);
		if ( folder && folder.isTodoListFolder() ){
			Zarafa.task.Actions.deleteRecordsFromTodoList(records);
		} else {
			this.doDeleteRecords(records, askOcc, softDelete);
		}
	},

	/**
	 * Deletes all {@link Zarafa.core.data.IPMRecord records} from the {@link Zarafa.core.data.IPMStore store}.
	 * If any of the given {@link Zarafa.core.data.IPMRecord records} is an recurring item, then
	 * a {@link Zarafa.common.dialogs.MessageBox.select MessageBox} will be prompted which lets the user
	 * select between the series or the single occurence.
	 * All given {@link Zarafa.core.data.IPMRecord records} must be located in the same
	 * {@link Zarafa.core.data.IPMStore store}.
	 *
	 * @param {Array} records The array of records which must be deleted.
	 * @param {Boolean} askOcc (private) False to prevent a dialog to appear to ask if the occurence or series must
	 * be deleted
	 * @param {Boolean} softDelete (optional) true to directly soft delete record(s) skipping deleted-items
	 * folder, false otherwise
	 *
	 * FIXME: This introduces Calendar-specific actions into the Common Context, but there is no clean solution
	 * for this at this time. But we need to split this up into context-specific actions while maintaining this
	 * single-entrypoint for deleting records.
	 */
	doDeleteRecords : function(records, askOcc, softDelete)
	{
		var store;
		var saveRecords = [];

		for (var i = 0, len = records.length; i < len; i++) {
			var record = records[i];
			store = record.getStore();

			// Check if the item is recurring, and if we need to ask the user
			// if the occurence or series must be deleted
			var deleteRecurring = Ext.isFunction(record.isRecurringOccurence) && record.isRecurringOccurence() && askOcc !== false;

			// Meeting and task requests are always deleted as normal,
			// we don't care for the recurring state of the record.
			var messageClass = record.get('message_class');
			if (Zarafa.core.MessageClass.isClass(messageClass, 'IPM.Schedule.Meeting', true) ||
				Zarafa.core.MessageClass.isClass(messageClass, 'IPM.TaskRequest', true)) {
					deleteRecurring = false;
			}

			if (deleteRecurring) {
				// Deleting an recurring series requires a confirmation dialog.
				this.deleteRecurringItem(record);
			} else if (Ext.isFunction(record.isMeeting) && record.isMeeting() && !record.isAppointmentInPast() && !record.isMeetingCanceled()) {
				// delete action on future meeting items
				if (record.isMeetingSent()) {
					// We are the organizer of the meeting, so lets ask if the recipients should be notified.
					Ext.MessageBox.show({
						title: _('Kopano WebApp'),
						msg : _('A cancellation message will be sent to all recipients, do you wish to continue?'),
						icon: Ext.MessageBox.WARNING,
						fn: this.cancelInvitation,
						scope: record,
						buttons: Ext.MessageBox.YESNO
					});
				} else if (record.isMeetingResponseRequired() && !record.isCopied()) {
					// We are the attendee of the meeting, lets ask if we should inform the organizer
					this.deleteMeetingRequestConfirmationContent(record, this.declineInvitation, record);
				} else {
					// We are neither, we don't care, just delete the thing
					store.remove(record);
					saveRecords.push(record);
				}
			} else if (record.isMessageClass('IPM.TaskRequest') || (Ext.isFunction(record.isTaskReceived) && record.isTaskReceived())) {
				// If task is assigned task by assigner and it is not completed then
				// ask for the user conformation like "Delete", "Mark complete and delete"
				// or "Mark decline and delete" and if task is already completed then we dont
				// require any confirmation from user.
				if (!record.get('complete')) {
					this.deleteAssignedTaskConfirmationContent(record, this.declineTask, record);
				} else {
					store.remove(record);
					saveRecords.push(record);
				}
			} else {
				// normal delete action
				store.remove(record);
				saveRecords.push(record);
			}
		}

		if(!Ext.isEmpty(saveRecords)) {
			// Check if records are required to be soft deleted
			if (softDelete === true) {
				Ext.each(saveRecords, function(saveRecord) {
					saveRecord.addMessageAction('soft_delete', true);
				}, this);
			}
			store.save(saveRecords);
		}

		// If number of delete records equal to total loaded records then show load mask until server send success response.
		if(store.totalLoadedRecord) {
			if (records.length === store.totalLoadedRecord) {
				store.showLoadMask();
			}
		}
	},

	/**
	 * Function delete an assigned task and sends decline/complete task response message to assigner.
	 * function was triggered in scope of the {@link Zarafa.task.TaskRecord TaskRecord}.
	 *
	 * @param {String} buttonClicked The ID of the button pressed,
	 * here, one of: ok cancel.
	 * @param {Ext.form.Radio} radio The Radio which was selected by the user.
	 * @private
	 */
	declineTask : function (buttonClicked, radio)
	{
		if (buttonClicked === 'ok') {
			this.deleteIncompleteTask(radio.id);
		}
	},

	/**
	 * Opens a {@link Zarafa.common.dialogs.MessageBox.select MessageBox} for
	 * selecting if decline and delete, complete and delete task response
	 * need to be sent to task assigner or silently deleted items.
	 *
	 * @param {Function} handler The handler which is invoked with the selected value
	 * from the dialog. This function only takes one argument and is either 'declineAndDelete'
	 * when the delete task and send decline response to assignor , 'completeAndDelete'
	 * when the delete task and send complete task response to assigner or 'delete'
	 * when the delete task without sending any response to assigner.
	 *
	 * @param {Object} scope (optional) The scope on which the handler must be invoked.
	 */
	deleteAssignedTaskConfirmationContent : function (record, handler, scope)
	{
		var title = _('Delete Incomplete Task');
		var text = _('The task "{0}" has not been completed. What do you want to do?');

		text = String.format(text, record.get('subject'));

		Zarafa.common.dialogs.MessageBox.select(
			title, text, handler, scope, [{
				boxLabel: _('Decline and delete'),
				id : 'declineAndDelete',
				name: 'select',
				checked: true
			},{
				boxLabel: _('Mark complete and delete'),
				id : 'completeAndDelete',
				name: 'select'
			},{
				boxLabel: _('Delete'),
				id : 'delete',
				name: 'select'
			}]
		);
	},

	/**
	 * Function which prompt user with deleting for recurring Meeting or normal recurring
	 * appointment and also manages sending response to meeting organizer.
	 *
	 * @param {Ext.data.Record} record that must be deleted
	 * @private
	 */
	deleteRecurringItem : function(record){
		Zarafa.common.Actions.deleteRecurringSelectionContent(record, function(button, radio) {
			if (button != 'ok') {
				return;
			}

			if (radio.id != 'recurrence_series') {
				record = record.convertToOccurenceRecord();
			} else {
				record = record.convertToSeriesRecord();
			}
			container.getShadowStore().add(record);

			Zarafa.common.Actions.deleteRecords(record, false);
		}, this);
	},

	/**
	 * Function cancels Meeting invitation and sends Meeting Cancellation message.
	 *
	 * @param {String} buttonClicked The ID of the button pressed,
	 * @param {String} text Value of the input field, not useful here
	 * @private
	 */
	cancelInvitation : function(buttonClicked, text)
	{
		if (buttonClicked == 'yes') {
			// Here scope is record so this refers to Appointment Record.
			this.cancelInvitation();
		}
	},

	/**
	 * Function declines a Meeting invitation and sends Meeting Decline message.
	 *
	 * @param {String} buttonClicked The ID of the button pressed,
	 * here, one of: ok cancel.
	 * @param {Ext.form.Radio} radio The Radio which was selected by the user.
	 * @private
	 */
	declineInvitation : function(buttonClicked, radio)
	{
		if (buttonClicked == 'ok') {
			// Here scope is record so this refers to Appointment Record.
			var sendUpdateFlag = (radio.id == 'sendResponseOnDelete') ? true: false;
			this.declineMeeting(sendUpdateFlag);
		}
	},

	/**
	 * Opens a {@link Zarafa.common.restore.ui.RestoreContentPanel restoreContentPanel}
	 *
	 * @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder folder that is loaded for the new context
	 * @param {Object} config (optional) Configuration object for creating the content panel
	 */
	openRestoreContent : function(folder, config)
	{
		var componentType = Zarafa.core.data.SharedComponentType['common.dialog.restoreitems'];
		config = Ext.applyIf(config || {}, {
			folder : folder
		});
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config);
	},

	/**
	 * Opens a {@link Zarafa.addressbook.dialogs.ABUserSelectionContentPanel ABUserSelectionContentPanel}
	 *
	 * @param {Object} config Configuration object. For AB this normally includes:
	 * 	callback - Callback function to be called with the user selected in the ContentPanel
	 * 	hideContactsFolders - Restriction that has to be applied on the hierarchy of the Addressbook
	 * 	listRestriction - Restriction that has to be applied on the contents of the Addressbook
	 */
	openABUserSelectionContent : function(config)
	{
		var componentType = Zarafa.core.data.SharedComponentType['addressbook.dialog.abuserselection'];
		config = Ext.applyIf(config || {}, {
			modal : true
		});

		Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config);
	},

	/**
	 * Opens a {@link Zarafa.addressbook.dialog.ABMultiUserSelectionContentPanel ABMultiUserSelectionContentPanel}
	 *
	 * @param {Object} config Configuration object for the dialog
	 */
	openABUserMultiSelectionContent : function(config)
	{
		config = config || {};

		Ext.applyIf(config, {
			modal : true,
			convert : function(user) { return user; }
		});

		var componentType = Zarafa.core.data.SharedComponentType['addressbook.dialog.abmultiuserselection'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config);
	},

	/**
	 * Mark the given messages as read or unread. When a read receipt was requested
	 * for this message, the setttings are consulted to see if we must automatically
	 * send the receipt or not, or if we should ask the user.
	 *
	 * @param {Zarafa.core.data.IPMRecord/Array} records The record or records which must
	 * be marked as read.
	 * @param {Boolean} read (optional) False to mark the messages as unread, otherwise
	 * the message will be marked as read.
	 */
	markAsRead : function(records, read)
	{
		records = !Array.isArray(records) ? [ records ] : records;
		read = !Ext.isDefined(read) ? true : read;

		var saveRecords = [];

		for (var i = 0, len = records.length; i < len; i++) {
			var record = records[i];

			// If the read status already matches the desired state,
			// we don't need to do anything.
			if (read === record.isRead()) {
        continue;
      }

      if (read === true && record.needsReadReceipt()) {
					switch (container.getSettingsModel().get('zarafa/v1/contexts/mail/readreceipt_handling')) {
						case 'never':
							record.setReadFlags(read);
							// Never send a read receipt.
							record.addMessageAction('send_read_receipt', false);

							saveRecords.push(record);
							break;
						case 'always':
							record.setReadFlags(read);
							// Always send a read receipt.
							record.addMessageAction('send_read_receipt', true);

							saveRecords.push(record);
							break;
						case 'ask':
						/* falls through*/
						default:
							const store = record.getStore();
							// Ask if a read receipt must be send.
							Ext.MessageBox.confirm(_('Kopano WebApp'), _('The sender of this message has asked to be notified when you read this message. Do you wish to notify the sender?'),
								// This function will execute when user provide some inputs,
								// So other external changes should not affect the record.
								function(buttonClicked) {
									// If the mailgrid has reloaded, retrieve the newly updated record.
									var record = this;
									if (!record.getStore()) {
										record = store.getById(record.id);
									}
									record.setReadFlags(read);
									record.addMessageAction('send_read_receipt', buttonClicked !== 'no');
									record.save();
								}, record);
							break;
					}
				} else {
				record.setReadFlags(read);
				saveRecords.push(record);
				}
			}

		if (!Ext.isEmpty(saveRecords)) {
			saveRecords[0].store.save(saveRecords);
		}
	},

	/**
	 * Will start the download by setting the dialogFrame's location to the download URL of the file.
	 *
	 * @param {Zarafa.core.data.IPMAttachmentRecord} records The record of the file to be downloaded
	 * @param {Boolean} allAsZip (optional) True to downloading all the attachments as ZIP
	 */
	downloadAttachment : function(record, allAsZip)
	{
		if(!this.downloadFrame) {
			this.downloadFrame = new Zarafa.common.attachment.ui.AttachmentDownloader();
		}

		this.downloadFrame.checkForEmbeddedAttachments(record, allAsZip);
	},

	/**
	 * Opens a {@link Zarafa.common.rules.dialogs.RulesWordsEditContentPanel}
	 *
	 * @param {Object} config Configuration object for the dialog
	 */
	openRulesWordsEditContent : function(config)
	{
		var componentType = Zarafa.core.data.SharedComponentType['common.rules.dialog.ruleswordsedit'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config);
	},

	/**
	 * Function is used to download attachments, for embedded message attachments additionally it will
	 * convert the {@link Zarafa.core.data.IPMAttachmentRecord IPMAttachmentRecord} to {@link Zarafa.core.data.IPMRecord IPMRecord}
	 * and then will pass it to {@link Zarafa.core.ui.ContentPanel ContentPanel} to open it.
	 * @param {Zarafa.core.data.IPMAttachmentRecord} record The attachment record which should be opened.
	 * @param {Object} config configuration object.
	 */
	openAttachmentRecord: function(record, config)
	{
		if(record.isEmbeddedMessage()) {
			// if we are going to open embedded message then we need to first convert it into mail record
			record = record.convertToIPMRecord();
		}

		if(record) {
			Zarafa.core.data.UIFactory.openViewRecord(record, config);
		}
	},

	/**
	 * Raised a dialog to choose destination folder to import attachments.
	 * @param {Zarafa.core.data.IPMAttachmentRecord} record The attachment record which should be imported.
	 * @param {Object} config configuration object.
	 */
	importToFolder: function(record, config)
	{
		config = Ext.applyIf(config || {}, {
			modal : true
		});
		var componentType = Zarafa.core.data.SharedComponentType['common.attachment.dialog.importtofolder'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, record, config);
	},

	/**
	 * Open a Panel in which the {@link Zarafa.core.data.IPMRecord record}
	 * can be viewed, or further edited.
	 *
	 * @param {Zarafa.core.data.IPMRecord} records The records to open
	 * @param {Object} config (optional) Configuration object used to create
	 * the Content Panel.
	 */
	openMessageContent : function(records, config)
	{
		Ext.each(records, function(record) {
			if (record.isUnsent() && !record.isFaultyMessage()) {
				Zarafa.core.data.UIFactory.openCreateRecord(record, config);
			} else {
				if(record.isMessageClass('IPM.TaskRequest', true)) {
					record = Zarafa.core.data.RecordFactory.createRecordObjectByMessageClass('IPM.Task', {
						entryid : record.get('entryid'),
						store_entryid : record.get('store_entryid'),
						parent_entryid : record.get('parent_entryid'),
						task_goid : record.get('task_goid')
					}, record.get('entryid'));
					record.addMessageAction('open_task', true);
				}
				Zarafa.core.data.UIFactory.openViewRecord(record, config);
			}
		});
	}
};