Ext.namespace('Zarafa.core.data');

/**
 * @class Zarafa.core.data.IPFStore
 * @extends Zarafa.core.data.MAPIStore
 * @xtype zarafa.ipfstore
 *
 * The {@link Zarafa.core.data.IPFStore IPFStore} represents the collection of MAPIStores.
 */
Zarafa.core.data.IPFStore = Ext.extend(Zarafa.core.data.MAPIStore, {
	/**
	 * @cfg {Boolean} standalone If true, the {@link Zarafa.core.data.IPFStore IPFStore}
	 * will not be hooked into the {@link Zarafa.core.data.IPFStoreMgr IPFStoreMgr}. This will prevent
	 * listening to events coming from the {@link Zarafa.core.data.IPFStoreMgr IPFStoreMgr}.
	 * Defaults to false.
	 */
	standalone : false,
	/**
	 * @cfg {Boolean} serveronly If true, the {@link Zarafa.core.data.IPFStore IPFStore}
	 * will be hooked into the {@link Zarafa.core.data.IPFStoreMgr IPFStoreMgr}, but will
	 * only do that for events which are triggered by a serverside change (e.g. a write
	 * event coming from the server, after a successful save).
	 * Defaults to false.
	 */
	serveronly : false,

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

		this.addEvents(
			/**
			 * @event beforenotify
			 * Event fired before a {@link #onNotify notification} is handled.
			 * 
			 * @param {Zarafa.core.data.IPFStore} store The store which fired the event
			 * @param {Zarafa.core.data.Notifications} notification 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.
			 * @return {Boolean} false to cancel the notification handling
			 */
			'beforenotify',
			/**
			 * @event notify
			 * Event fired after a {@link #onNotify notification} is handled.
			 * 
			 * @param {Zarafa.core.data.IPFStore} store The store which fired the event
			 * @param {Zarafa.core.data.Notifications} notification 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.
			 */
			'notify'
		);

		Zarafa.core.data.IPFStore.superclass.constructor.call(this, config);

		// Register store with the store manager
		if (!this.standalone) {
			Zarafa.core.data.IPFStoreMgr.register(this, this.serveronly);
		}
	},

	/**
	 * Initialize all events which Zarafa.core.data.IPFStore IPFStore} will listen to.
	 * @private
	 */
	initEvents : function()
	{
		Zarafa.core.data.IPFStore.superclass.initEvents.call(this);

		if (!this.standalone) {
			if (!this.serveronly) {
				Zarafa.core.data.IPFStoreMgr.on('beforerecordsave', this.onExternalSave, this);
			}
			Zarafa.core.data.IPFStoreMgr.on('afterrecordwrite', this.onExternalWrite, this);
		}
	},

	/**
	 * Filter a list of {@link Zarafa.core.data.IPFRecord 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.IPFRecord|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)
	{
		var results = { records: [],  updatedRecords : [] };

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

			var isFavoritesStoreInstance = this instanceof Zarafa.common.favorites.data.MAPIFavoritesSubStore;

			if (action === Ext.data.Api.actions.create) {
				for (var i = 0, len = records.length; i < len; i++) {
					var rec = records[i];
					var storeRec = this.containsStoreInLastLoad(rec.get('store_entryid'));

					// Notify Zarafa.hierarchy.data.MAPIFavoritesSubStore when record/folder is Favorites folder.
					if (rec.isFavoritesFolder()) {
						// Requires separate check here because there are many cases
						// where record was favorites marked but store is not favorites store in that case don't do
						// anything.
						if (isFavoritesStoreInstance) {
							results.records.push(rec.copy());
							results.updatedRecords.push(rec);
						}
					} else if (rec.store !== this && storeRec && !isFavoritesStoreInstance) {
						results.records.push(rec.copy());
						results.updatedRecords.push(rec);
					}
				}
			} else {
				for (var i = 0, len = records.length; i < len; i++) {
					var rec = records[i];
					var storeRec = this.getById(rec.get('entryid'));

					if (storeRec !== this && storeRec && !rec.isFavoritesFolder() && !isFavoritesStoreInstance) {
						results.records.push(storeRec);
						results.updatedRecords.push(rec);
					}
				}
			}
		}

		return results;
	},

	/**
	 * Event handler which is raised when a {@link Zarafa.core.data.IPFRecord IPFRecord} is about
	 * to be saved by a different {@link Zarafa.core.data.IPFStore IPFStore}. However the same
	 * {@link Zarafa.core.data.IPFRecord IPFRecord} may have been listed in multiple
	 * {@link Zarafa.core.data.IPFStore IPFStores} at the same time. Thus check if the saved
	 * {@link Zarafa.core.data.IPFRecord IPFRecord} is also listed in this store, and if that is the
	 * case, update it here as well. This way we don't have to wait until a reload from the
	 * server occurs, and the {@link Zarafa.core.data.IPFRecord IPFRecord} is updated on all stores
	 * instantly.
	 *
	 * @param {Zarafa.core.data.IPFStore} store The {@link Zarafa.core.data.IPFStore store} to which the
	 * {@link Zarafa.core.data.IPFRecord record} belongs.
	 * @param {Object} data An object containing the data that is to be saved.
	 * The object will contain a key for each appropriate action, with an array
	 * of updated data for each record.  
	 */
	onExternalSave : function(store, data)
	{
		var result;

		if (store === this) {
			return;
		}

		result = this.getRecordsForUpdateData(data[Ext.data.Api.actions.open], Ext.data.Api.actions.open);
		if (!Ext.isEmpty(result.records)) {
			this.onNotify(Zarafa.core.data.Notifications.objectModified, result.records, result.updatedRecords);
		}

		result = this.getRecordsForUpdateData(data[Ext.data.Api.actions.update], Ext.data.Api.actions.update);
		if (!Ext.isEmpty(result.records)) {
			this.onNotify(Zarafa.core.data.Notifications.objectModified, result.records, result.updatedRecords);
		}

		result = this.getRecordsForUpdateData(data[Ext.data.Api.actions.create], Ext.data.Api.actions.create);
		if (!Ext.isEmpty(result.records)) {
			this.onNotify(Zarafa.core.data.Notifications.objectCreated, result.records, result.updatedRecords);
		}

		result = this.getRecordsForUpdateData(data[Ext.data.Api.actions.destroy], Ext.data.Api.actions.destroy);
		if (!Ext.isEmpty(result.records)) {
			this.onNotify(Zarafa.core.data.Notifications.objectDeleted, result.records, result.updatedRecords);
		}
	},

	/**
	 * Event handler which is raised when a {@link Zarafa.core.data.IPFRecord IPFRecord} has been
	 * updated from the server. This {@link Zarafa.core.data.IPFRecord IPFRecord} may have been listed in
	 * multiple {@link Zarafa.core.data.IPFStore IPFStores} at the same time. Thus we need to
	 * check if this {@link Zarafa.core.data.IPFRecord IPFRecord} is also listed in this store, and if that
	 * is the case, update it here as well. This way we don't have to wait until a reload from the
	 * server occurs for this specific store.
	 *
	 * @param {Zarafa.core.data.IPFStore} store The {@link Zarafa.core.data.IPFStore store} to which the
	 * {@link Zarafa.core.data.IPFRecord record} belongs.
	 * @param {String} action [Ext.data.Api.actions.create|update|destroy|open]
	 * @param {Object} data The 'data' picked-out out of the response for convenience.
	 * @param {Ext.Direct.Transaction} res
	 * @param {Zarafa.core.data.IPFrecord} record The most recent version of the record
	 * which came from the server.
	 */
	onExternalWrite : function(store, action, data, res, record)
	{
		if (store === this) {
			return;
		}

		var obj = {};
		if (Array.isArray(record)) {
			obj[action] = record;
		} else {
			obj[action] = [ record ];
		}

		this.onExternalSave(store, obj);
	},

	/**
	 * Checks whether any of the stores that were included in the parameters during the last load, 
	 * matches the supplied entryid argument.
	 *
	 * The IPFStore can currently not load data directly from the server, hence this
	 * function will always return false as it cannot determine which store it is currently
	 * displaying. Subclasses should override this function to return accurate information.
	 *
	 * @param {String|Array} entryidList Entryid of the folder
	 * @return {Boolean} Returns true when entryid matches, false when it does not.
	 */
	containsStoreInLastLoad: function(entryidList)
	{
		return false;
	},

	/**
	 * Notification handler which is called automatically by the
	 * {@link Zarafa.hierarchy.data.HierarchyNotificationResponseHandler NotificationResponseHandler}
	 * when a notification has been recieved for the hierarchy.
	 *
	 * @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.
	 */
	onNotify : function(action, records, data, timestamp, success)
	{
		if (this.fireEvent('beforenotify', this, action, records, data, timestamp, success) !== false) {
			var handler = this['onNotify' + Ext.util.Format.capitalize(action)];
			if (Ext.isFunction(handler)) {
				handler.call(this, action, records, data, timestamp, success);
			}
			this.fireEvent('notify', this, action, records, data, timestamp, success);
		}
	},

	/**
	 * Destroys the store.
	 */
	destroy : function()
	{   
		if (!this.standalone) {
			Zarafa.core.data.IPFStoreMgr.unregister(this, this.serveronly);

			Zarafa.core.data.IPFStoreMgr.un('beforerecordsave', this.onExternalSave, this);
			Zarafa.core.data.IPFStoreMgr.un('afterrecordwrite', this.onExternalWrite, this);
		}
		Zarafa.core.data.IPFStore.superclass.destroy.call(this);
	}
});

Ext.reg('zarafa.ipfstore', Zarafa.core.data.IPFStore);
