Ext.namespace('Zarafa.calendar');

/**
 * @class Zarafa.calendar.AppointmentStore
 * @extends Zarafa.core.data.ListModuleStore
 * @xtype zarafa.appointmentstore
 * 
 * The AppointmentStore class provides a way to connect to the 'appointmentlistmodule' in the server back-end to an 
 * Ext.grid.GridPanel object. It provides a means to retrieve appointment listings asynchronously.
 * The store has to be initialised with a store Id, which corresponds (somewhat confusingly) to
 * a MAPI store id. The AppointmentStore object, once instantiated, will be able to retrieve and list
 * appointments from a single specific store only.
 */
Zarafa.calendar.AppointmentStore = Ext.extend(Zarafa.core.data.ListModuleStore, {
	/**
	 * @constructor
	 * @param {Object} config configuration params that should be used to create instance of this store.
	 */
	constructor : function(config)
	{
		config = config || {};

		// Apply default settings.
		Ext.applyIf(config, {
			preferredMessageClass : 'IPM.Appointment',
			defaultSortInfo : {
				field : 'startdate',
				direction : 'desc'
			}
		});

		Zarafa.calendar.AppointmentStore.superclass.constructor.call(this, config);
		this.on('update', this.onUpdate, this);
	},

	/**
	 * With recurring appointments, each occurence has the same entryid.
	 * For those cases, we cannot use the entryid as unique property, instead
	 * we combine it with the basedate.
	 * @param {Ext.data.Record} record The record for which the key is requested
	 * @return {String} The key by which the record must be saved into the {@link Ext.util.MixedCollection}.
	 * @protected
	 */
	getRecordKey : function(record)
	{
		// Check isRecurringOccurence exists because the user could have moved a non-appointment message to
		// the calendar
		if ( Ext.isDefined(record.isRecurringOccurence) && record.isRecurringOccurence()) {
			return record.id + '' + record.get('basedate');
		} else {
			return record.id;
		}
	},

	/**
	 * Event handler fired by the {@link Ext.data.Store} when a record is being removed
	 * from the store. This adds a special case to the default behavior of the {@link Ext.data.Store#destroyRecord}
	 * especially for recurring appointments. When the appointment is recurring, and no basedate is provided, we
	 * manually fire a {@link Ext.data.Store#remove} event for each occurence which is currently loaded. This will
	 * ensure that the UI can remove every occurence from the UI while we send a single record to the server to
	 * remove the entire series from the server.
	 * 
	 * @param {Ext.data.Store} store The store from where the record is removed
	 * @param {Ext.data.Record} record The record record which was removed
	 * @param {Number} index The index of the record where the record was removed
	 * @private
	 */
	destroyRecord : function(store, record, index)
	{
		// Don't remove a record which is already removed
		if (this.removed.indexOf(record) > -1) {
			return;
		}

		// Special case for deleting recurrences
		if (record.isRecurring && record.isRecurring()) {
			// Search for all occurences which belong to this recurrence.
			// Note that 'record' is already removed from the store,
			// so we don't risk of adding it again into the array.
			var deleteOccurences = [];
			this.each(function(r) {
				if (r.id === record.id) {
					deleteOccurences.push(r);
				}
			});

			// Now remove every occurence from the store, before removing we
			// push it into the 'removed' array to make sure that when we arrive
			// inside this function again, the first if-statement will return.
			for (var i = 0, len = deleteOccurences.length; i < len; i++) {
				var occur = deleteOccurences[i];
				this.removed.push(occur);
				this.remove(occur);
				this.removed.remove(occur);
			}

			// All occurences have been deleted. Continue as usual...
		}

		Zarafa.calendar.AppointmentStore.superclass.destroyRecord.call(this, store, record, index);
	},

	/**
	 * Filter a list of {@link Zarafa.core.data.IPMRecord records} by checking if the record
	 * belongs to this Store. This comparison is based on checking if the entryid of the given
	 * records match the entryid of the records inside the store. If the records are being
	 * {@link Zarafa.core.data.JsonReader#realize realized} by the {@link Zarafa.core.data.JsonReader JsonReader}
	 * then we check if the parent_entryid matches this store.
	 *
	 * What will be returned is an object containing the records as present inside the store,
	 * and the data objects which should be applied to them (the data from the records of the
	 * store that triggered the event).
	 *
	 * @param {Zarafa.core.data.IPMRecord|Array} records The record or records to filter
	 * @param {Ext.data.Api.actions} action The API action for which the updates are looked for.
	 * @return {Object} Object containing the key 'records' containing the array of records inside
	 * this store, and the key 'updatedRecords' containing the array of records which should be 
	 * applied to those records.
	 * @private
	 */
	getRecordsForUpdateData : function(records, action)
	{
		if (!Ext.isDefined(records) || action === Ext.data.Api.actions.create) {
			return Zarafa.calendar.AppointmentStore.superclass.getRecordsForUpdateData.apply(this, arguments);
		}

		var results = { records: [],  updatedRecords : [] };

		if (!Array.isArray(records)) {
			records = [ records ];
		}

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

			if (!record.isMessageClass('IPM.Appointment') && !record.isMessageClass('IPM.OLE.CLASS.{00061055-0000-0000-C000-000000000046}')) {
				continue;
			} else if (record.isRecurringOccurence()) {
				// The appointment is an occurence, this means we are
				// updating an exception. Find the exact occurence which
				// was changed.
				var index = this.findBy(function(rec) {
					return Zarafa.core.EntryId.compareEntryIds(record.get('entryid'), rec.get('entryid')) &&
						   Ext.isDate(record.get('basedate')) && Ext.isDate(rec.get('basedate')) &&
						   (record.get('basedate').getTime() === rec.get('basedate').getTime());
				});

				if (index >= 0) {
					// The update is for an occurence, make sure we add
					// the 'basedate' as identifier.
					results.records.push(this.getAt(index));
					results.updatedRecords.push(record);
				}

			} else if (record.isRecurring()) {
				// if the record is a series, we must search for
				// the occurences (except for exceptions).
				var index = -1;
				do {
					index = this.findBy(function(rec) {
						return ((action === Ext.data.Api.actions.destroy) || !rec.isRecurringException()) &&
							   Zarafa.core.EntryId.compareEntryIds(record.get('entryid'), rec.get('entryid'));
					}, undefined, index + 1);
				
					if (index >= 0) {
						var update = this.getAt(index);
						var clonedRec = record.copy();

						// The 'basedate' property must not be altered by the series,
						// as this value will the different for each occurence.
						var basedate = update.get('basedate');
						if (Ext.isDate(basedate)) {
							clonedRec.set('basedate', basedate.clone());
						}
						// The 'recurrence' property must not be altered by the series,
						// as occurences have a different value for this property.
						clonedRec.set('recurrence', update.get('recurrence'));

						// Update time and duration properties correctly according to the series information.
						clonedRec.set('startdate', update.get('startdate').clearTime(true).add(Date.MINUTE, record.get('recurrence_startocc')));
						clonedRec.set('duedate', update.get('startdate').clearTime(true).add(Date.MINUTE, record.get('recurrence_endocc')));
						clonedRec.set('commonstart', clonedRec.get('startdate').clone());
						clonedRec.set('commonend', clonedRec.get('duedate').clone());
						clonedRec.set('duration', (clonedRec.get('duedate') - clonedRec.get('startdate')) / (60 * 1000));

						results.records.push(update);
						results.updatedRecords.push(clonedRec);
					}
				} while(index != -1);
			} else {
				// For non-recurring appointments we should simply look
				// for the appointment where the entyid matches.
				var storeRec = this.getById(record.get('entryid'));

				if (storeRec) {
					results.records.push(storeRec);
					results.updatedRecords.push(record);
				}
			}
		}

		return results;
	},

	/**
	 * Notification handler called by {@link #onNotify} when
	 * a {@link Zarafa.core.data.Notifications#objectDeleted objectDeleted}
	 * notification has been recieved. This will remove the 
	 * {@link Zarafa.calendar.AppointmentRecord appointment} Or  {@link Zarafa.calendar.MeetingRequestRecord meeting}from the
	 * {@link Zarafa.calendar.AppointmentStore store} and after removing store gets reload.
	 * 
	 * @param {Zarafa.core.data.Notifications} action The notification action
	 * @param {Ext.data.Record/Array} records The record or records which have been affected by the notification.
	 * @param {Object} data The data which has been recieved from the PHP-side which must be applied
	 * to the given records.
	 * @param {Number} timestamp The {@link Date#getTime timestamp} on which the notification was received
	 * @param {Boolean} success The success status, True if the notification was successfully recieved.
	 * @private
	 */
	onNotifyObjectdeleted : function(action, records, data, timestamp, success)
	{
		Zarafa.calendar.AppointmentStore.superclass.onNotifyObjectdeleted.apply(this, arguments);
		this.reload();
	},

	/**
	 * Event handler triggered when store was updated also it should open the copied meeting record
	 * into tab for further adding.
	 *
	 * @param {Zarafa.calendar.AppointmentStore} store which gets updated.
	 * @param {Zarafa.calendar.AppointmentRecord} record which is created in store.
	 * @param {string} operation write Action that ocurred. Can be one of
	 * {@link Ext.data.Record.EDIT EDIT}, {@link Ext.data.Record.REJECT REJECT} or
	 * {@link Ext.data.Record.COMMIT COMMIT}
	 */
	onUpdate : function (store, record, operation)
	{
		if(operation === Ext.data.Record.COMMIT && record.getActionResponse('resources_pasted') && record.isMeeting()) {
			Zarafa.core.data.UIFactory.openViewRecord(record);
		}
	}
});

Ext.reg('zarafa.appointmentstore', Zarafa.calendar.AppointmentStore);