/*
 * #dependsFile client/zarafa/core/mapi/ObjectType.js
 * #dependsFile client/zarafa/core/mapi/MessageFlags.js
 * #dependsFile client/zarafa/core/data/Record.js
 * #dependsFile client/zarafa/core/data/RecordFactory.js
 * #dependsFile client/zarafa/core/data/IPMRecipientStore.js
 * #dependsFile client/zarafa/core/data/IPMAttachmentStore.js
 */
Ext.namespace('Zarafa.core.data');

/**
 * @class Zarafa.core.data.IPMRecordFields
 * Array of {@link Ext.data.Field field} configurations for the
 * {@link Zarafa.core.data.IPMRecord IPMRecord} object.
 * These fields will be available in all 'IPM' and 'REPORT.IPM' type messages.
 * @private
 */
Zarafa.core.data.IPMRecordFields = [
	{name: 'body', type: 'string'},
	{name: 'html_body', type: 'string'},
	{name: 'isHTML', type:'boolean', defaultValue: false},
	{name: 'entryid'},
	{name: 'creation_time', type:'date', dateFormat:'timestamp', defaultValue: null, sortDir : 'DESC'},
	{name: 'icon_index', type: 'int', defaultValue: -1},
	{name: 'access', type: 'number', defaultValue: Zarafa.core.mapi.Access.ACCESS_READ},
	{name: 'message_class', type: 'string'},
	{name: 'message_flags', type: 'int', defaultValue: Zarafa.core.mapi.MessageFlags.MSGFLAG_UNSENT | Zarafa.core.mapi.MessageFlags.MSGFLAG_READ},
	{name: 'read_receipt_requested', type: 'boolean', defaultValue: false},
	{name: 'parent_entryid'},
	{name: 'store_entryid'},
	{name: 'subject'},
	{name: 'object_type', type: 'int', defaultValue: Zarafa.core.mapi.ObjectType.MAPI_MESSAGE},
	{name: 'normalized_subject'},
	{name: 'last_modification_time', type:'date', dateFormat:'timestamp', defaultValue: null, sortDir : 'DESC'},
	{name: 'last_verb_execution_time', type:'date', dateFormat:'timestamp', defaultValue: null},
	{name: 'last_verb_executed', type: 'int'},
	{name: 'hasattach', type: 'boolean', defaultValue: false},
	{name: 'display_to'},
	{name: 'display_cc'},
	{name: 'display_bcc'},
	{name: 'sent_representing_name'},
	{name: 'sent_representing_email_address'},
	{name: 'sent_representing_username'},
	{name: 'sent_representing_address_type'},
	{name: 'sent_representing_entryid'},
	{name: 'sent_representing_search_key'},
	{name: 'sent_representing_presence_status'}, // Note: this field will not be filled by the back-end
	{name: 'sender_name'},
	{name: 'sender_email_address'},
	{name: 'sender_username'},
	{name: 'sender_address_type'},
	{name: 'sender_entryid'},
	{name: 'sender_search_key'},
	{name: 'sender_presence_status'}, // Note: this field will not be filled by the back-end
	{name: 'message_size', type: 'int'},
	{name: 'categories'},
	{name: 'deleted_on', type:'date', dateFormat:'timestamp', defaultValue: null},
    {name: 'deferred_send_time', type: 'date', dateFormat: 'timestamp', defaultValue: null},
	// required when converting attachment record to ipm record for opening embedded messages
	{name: 'attach_num', defaultValue: null}
];

// Register these properties as base for the IPM message class, this will ensure that every IPM.* class
// will have these properties. We also install the MAPI_MESSAGE object type in case we receive a broken
// message which only has an object_type but doesn't have a message class.
Zarafa.core.data.RecordFactory.addFieldToMessageClass('IPM', Zarafa.core.data.IPMRecordFields);
Zarafa.core.data.RecordFactory.addFieldToMessageClass('REPORT.IPM', Zarafa.core.data.IPMRecordFields);
Zarafa.core.data.RecordFactory.addFieldToObjectType(Zarafa.core.mapi.ObjectType.MAPI_MESSAGE, Zarafa.core.data.IPMRecordFields);

Zarafa.core.data.RecordFactory.addListenerToMessageClass('IPM', 'createphantom', function(record) {
	// Phantom records must always be marked as opened (they contain the full set of data)
	record.afterOpen();
});

Zarafa.core.data.RecordFactory.addListenerToObjectType(Zarafa.core.mapi.ObjectType.MAPI_MESSAGE, 'createphantom', function(record) {
	// Phantom records must always be marked as opened (they contain the full set of data)
	record.afterOpen();
});

/**
 * @class Zarafa.core.data.IPMRecord
 * @extends Zarafa.core.data.MAPIRecord
 *
 * An extension to the {@link Zarafa.core.data.MAPIRecord Record} specific to IPM Messages.
 *
 * By default, using {@link Zarafa.core.data.IPMRecord.set set} will set a property for the given
 * {@link Zarafa.core.data.IPMRecord record}. However, additional functions have been provided
 * for the manipulation of recipients, attachments and message actions.
 */
Zarafa.core.data.IPMRecord = Ext.extend(Zarafa.core.data.MAPIRecord, {
	/**
	 * When event propagation is enabled, events about {@link Zarafa.core.data.IPMRecord record}
	 * from the {@link Zarafa.core.data.IPMStore store} will be propagated through a new event
	 * to other {@link Zarafa.core.data.IPMStore stores} by the {@link Zarafa.core.data.IPMStoreMgr IPMStoreMgr}
	 * This should be temporarily be disabled when the {@link Zarafa.core.data.IPMStore store}
	 * is handling an event from {@link Zarafa.core.data.IPMStoreMgr IPMStoreMgr} and will raise
	 * a similar event which will be used by the {@link Zarafa.core.data.IPMStoreMgr IPMStoreMgr} again
	 * (which results in circular events).
	 *
	 * This property can be changed by the function {@link #setEventPropagation} and the status
	 * can be requested using {@link #hasEventPropagation}.
	 *
	 * @property
	 * @type Boolean
	 */
	eventPropagation : true,

	/**
	 * The base array of ID properties which is copied to the {@link #idProperties}
	 * when the record is being created.
	 * @property
	 * @type Array
	 * @private
	 */
	baseIdProperties : [ 'entryid', 'store_entryid', 'parent_entryid', 'attach_num' ],

	/**
	 * Compare this {@link Zarafa.core.data.IPMRecord record} instance with another one to see
	 * if they are the same IPM Message from the server (i.e. The entryid matches).
	 *
	 * @param {Zarafa.core.data.IPMRecord} record The IPMRecord to compare with
	 * @return {Boolean} True if the records are the same.
	 */
	equals : function(record)
	{
		// Simplest case, do we have the same object...
		if (this === record) {
			return true;
		}

		// Check if record definition is changed, then consider it as different record
		if(this.fields !== record.fields) {
			return false;
		}

		// When only one of the 2 records is a phantom, then the
		// records can impossibly be equal...
		if (this.phantom !== record.phantom) {
			return false;
		}

		// For phantom records, no entryid exists. Since both objects thus only
		// contain on the client-side and cannot be represented by 2 different
		// objects, comparison on the id-field only is sufficient.
		if (this.phantom) {
			return this.id == record.id;
		} else {
			var equal = Zarafa.core.EntryId.compareEntryIds(this.get('entryid'), record.get('entryid'));
			if(equal) {
				// compare attach_num as well if embedded message
				if(this.isSubMessage() && record.isSubMessage()) {
					equal = this.get('attach_num').equals(record.get('attach_num'));
				} else if (this.isSubMessage() || record.isSubMessage()) {
					equal = false;
				}
			}

			return equal;
		}
	},

	/**
	 * This will determine if the provided className matches the message_class
	 * on this record. This comparison is done case-insensitive. See {@link Zarafa.core.MessageClass#isClass}
	 * for further details.
	 *
	 * @param {String/Array} className The class name which must be compared to the message_class
	 * @param {Boolean} baseOnly (optional) True when only the start of the message_class needs
	 * to match with the className (So isMessageClass('IPM', true) will return true when the
	 * actual message_class is 'IPM.Note'). Defaults to false.
	 * @return True when the given className matches the message_class.
	 */
	isMessageClass : function(className, baseOnly)
	{
		return Zarafa.core.MessageClass.isClass(this.get('message_class'), className, baseOnly);
	},

	/**
	 * Utility function to get sender string from {@link Zarafa.core.data.IPMRecord IPMRecord},
	 * Fuction will also check if delegate is present then it will generate a different string.
	 * @return {String} string which can be shown in ui as from address.
	 */
	getSenderString : function()
	{
		var sender = '';
		var senderEntryId = this.get('sender_entryid');
		var sentRepresentingEntryId = this.get('sent_representing_entryid');

		if(!Ext.isEmpty(senderEntryId) && !Ext.isEmpty(sentRepresentingEntryId) && !Zarafa.core.EntryId.compareABEntryIds(senderEntryId, sentRepresentingEntryId)) {
			var delegate = !Ext.isEmpty(this.get('sender_name')) ? this.get('sender_name') : this.get('sender_email_address');
			var delegator = !Ext.isEmpty(this.get('sent_representing_name')) ? this.get('sent_representing_name') : this.get('sent_representing_email_address');

			// # TRANSLATORS: {0} indicates name/email address of secretary and {1} indicates boss' name/email address
			sender = String.format(_('{0} on behalf of {1}'), delegate, delegator);
		} else {
			sender = !Ext.isEmpty(this.get('sender_name')) ? this.get('sender_name') : this.get('sender_email_address');
		}

		return sender;
	},

	/**
	 * Helper function to get contents of body property of {@link Zarafa.core.data.IPMRecord IPMRecord}
	 * @param {Boolean} preferHTML True if the HTML body should be returned or not, false if the plain-text
	 * body should be returned.
	 * @return {String} The body property of {@link Zarafa.core.data.IPMRecord IPMRecord}.
	 */
	getBody : function(preferHTML)
	{
		var isHTML = this.get('isHTML');
		var body;

		if (preferHTML === false) {
			if (isHTML) {
				// plain-text requested, convert the body
				body = Zarafa.core.HTMLParser.convertHTMLToPlain(this.get('html_body'));
			} else {
				// Plain-text requested, simply return the basic body
				body = this.get('body');
			}
		} else if (preferHTML === true) {
			if (isHTML) {
				// html requested, simply return the basic body
				// add urls in inline images
				body = this.inlineImgOutlookToZarafa(this.get('html_body'));
			} else {
				// html requested, convert the body
				body = Zarafa.core.HTMLParser.convertPlainToHTML(this.get('body'));
			}
		} else {
			if (isHTML) {
				// add urls in inline images
				body = this.inlineImgOutlookToZarafa(this.get('html_body'));
			} else {
				body = this.get('body');
			}
		}

		return body;
	},

	/**
	 * Helper function to set contents of body property of {@link Zarafa.core.data.IPMRecord IPMRecord}.
	 * @param {String} body body that should be set in {@link Zarafa.core.data.IPMRecord IPMRecord}.
	 * @param {Boolean} isHTMLEditor flag to indicate that we should update html body or plain body
	 * based on type of editor used for modifications.
	 */
	setBody : function(body, isHTMLEditor)
	{
		// Only set the isHTML property when the
		// body has been changed. This prevents the
		// isHTML flag to be sent to client when the
		// body won't be sent.
		if(isHTMLEditor) {
			// convert inline image urls
			body = this.inlineImgZarafaToOutlook(body);

			if (this.get('html_body') !== body) {
				this.set('isHTML', true, true);
				this.set('html_body', body);
			}
		} else {
			// Sometimes record body contains \r\n and editor value always
			// contains \n bue to different line breaking earlier, condition
			// is satisfied and we again set the body in record which mark
			// the record dirty and because of that we get "Unsaved changes..." message box.
			var recordBody = Zarafa.core.HTMLParser.rlnl2nl(this.get('body'));

			if (recordBody !== body) {
				this.set('isHTML', false, true);
				this.set('body', body);
			}
		}
	},

	/**
	 * Function is used to convert inline images source string to proper format which can request the inline
	 * image through server side code. It searches for attach content id property and replaces it with full fledged
	 * url to request image data using download_attachment.php file. This function is called by {@link #getBody}
	 * when requesting html data from record.
	 * @param {String} body html body of the record which should be converted to properly retrieve inline image data.
	 * @return {String} modified data that can retrieve inline images when loaded in editor.
	 */
	inlineImgOutlookToZarafa : function(body)
	{
		var entryid;
		var store;

		var action_type = this.getMessageAction('action_type');
		var attachNum = this.get('attach_num');

		//FIXME: this would only work if all images are either added or from the original record, but not both
		if (this.phantom && (Zarafa.mail.data.ActionTypes.isSendOrForward(action_type) || action_type===Zarafa.mail.data.ActionTypes.EDIT_AS_NEW) ) {
			entryid = this.getMessageAction('source_entryid');
			store = this.getMessageAction('source_store_entryid');
		} else {
			entryid = this.get('entryid');
			store = this.get('store_entryid');
		}

		if (store && entryid) {
			body = Zarafa.core.HTMLParser.inlineImgOutlookToZarafa(body, store, entryid, attachNum);
		}

		return body;
	},

	/**
	 * Function is used to convert inline images source string to proper format which is used by outlook.
	 * It will search for image source containing attach content id property and will replace the whole source string
	 * with just the content id. This function is called by {@link #setBody} we want to save user changed data from
	 * editor the html body of record. This will actually reverse changes done by {@link inlineImgOutlookToZarafa}.
	 * @param {String} body html body from the editor which will be saved in the {@link Zarafa.core.data.IPMRecord IPMRecord}.
	 * @return {String} modified data that can be safely saved in html body of record which is understandable by outlook.
	 */
	inlineImgZarafaToOutlook : function(body)
	{
		return Zarafa.core.HTMLParser.inlineImgZarafaToOutlook(body);
	},

	/**
	 * Convenience method for determining if the message has been read or not.
	 * @return {Boolean} True if this item has been read.
	 */
	isRead : function()
	{
		// embedded message should never be considered as unread messages
		return this.isSubMessage() || (this.get('message_flags') & Zarafa.core.mapi.MessageFlags.MSGFLAG_READ) > 0;
	},

	/**
	 * Convenience method for determining if the message has been sent or not.
	 * @return {Boolean} True if this item has NOT been sent.
	 */
	isUnsent : function()
	{
		return (this.get('message_flags') & Zarafa.core.mapi.MessageFlags.MSGFLAG_UNSENT) > 0;
	},

	/**
	 * Convenience method for determining if the message is a sub message of another message.
	 * @return {Boolean} True if this message is a sub message.
	 */
	isSubMessage : function()
	{
		return !Ext.isEmpty(this.get('attach_num'));
	},

	/**
	 * Convenience method for determining if the message needs a Read Receipt to be send when marking
	 * the message as read.
	 * @return {Boolean} True if this item needs a read receipt to be send.
	 */
	needsReadReceipt : function()
	{
		return (this.get('message_flags') & Zarafa.core.mapi.MessageFlags.MSGFLAG_RN_PENDING) === Zarafa.core.mapi.MessageFlags.MSGFLAG_RN_PENDING;
	},

	/**
	 * Convenience method for setting the read flag
	 *
	 * @param {Boolean} read True to mark the record as read.
	 */
	setReadFlags : function(read)
	{
		var flags = this.get('message_flags');

		if (read === false) {
			this.set('message_flags', flags & ~Zarafa.core.mapi.MessageFlags.MSGFLAG_READ);
		} else {
			this.set('message_flags', flags | Zarafa.core.mapi.MessageFlags.MSGFLAG_READ);
		}
	},

	/**
	 * Returns whether the IPMRecord supports the use of recipients or not (See {@link #supportsSubStore}).
	 * @return {Boolean} True if recipients are supported.
	 */
	supportsRecipients: function()
	{
		return this.supportsSubStore('recipients');
	},

	/**
	 * Creates a Recipients store for the {@link Zarafa.core.data.IPMRecord IPMRecord} (See {@link #createSubStore}).
	 * @return {Zarafa.core.data.IPMRecipientStore} The new Recipient store.
	 */
	createRecipientStore : function()
	{
		return this.createSubStore('recipients');
	},

	/**
	 * Set the Recipient store for the {@link Zarafa.core.data.IPMRecord record} (See {@link #setSubStore}).
	 * @param {Zarafa.core.data.IPMRecipientStore} recipientStore The Recipient store.
	 * @return {Zarafa.core.data.IPMRecipientStore} The Recipient store.
	 */
	setRecipientStore : function(recipientStore)
	{
		return this.setSubStore('recipients', recipientStore);
	},

	/**
	 * Get the Recipients store for the {@link Zarafa.core.data.IPMRecord IPMRecord} (See {@link #getSubStore}).
	 * @return {Zarafa.core.data.IPMRecipientStore} The Recipient store.
	 */
	getRecipientStore : function()
	{
		return this.getSubStore('recipients');
	},

	/**
	 * Returns whether the IPMRecord supports the use of recipients or not (See {@link #supportsSubStore}).
	 * @return {Boolean} True if recipients are supported.
	 */
	supportsAttachments: function()
	{
		return this.supportsSubStore('attachments');
	},

	/**
	 * Creates a Folder store for the {@link Zarafa.core.data.IPMRecord IPMRecord} (See {@link #createSubStore}).
	 * @return {Zarafa.core.data.IPMAttachmentStore} The new Attachment store.
	 */
	createAttachmentStore : function()
	{
		return this.createSubStore('attachments');
	},

	/**
	 * Set the Attachment store for the {@link Zarafa.core.data.IPMRecord record} (See {@link #setSubStore}).
	 * @param {Zarafa.core.data.IPMAttachmentStore} attachmentStore The Attachmentstore.
	 * @return {Zarafa.core.data.IPMAttachmentStore} The Attachment store.
	 */
	setAttachmentStore : function(attachmentStore)
	{
		return this.setSubStore('attachments', attachmentStore);
	},

	/**
	 * Get the Attachments store for the {@link Zarafa.core.data.IPMRecord IPMRecord} (See {@link #getSubStore}).
	 * @return {Zarafa.core.data.IPMAttachmentStore} The Attachment store.
	 */
	getAttachmentStore : function()
	{
		return this.getSubStore('attachments');
	},

	/**
	 * Helper function to return names of all attachments of message.
	 * @return {String} comma seperated attachment names
	 */
	getAttachmentNames : function()
	{
		if (!this.get('hasattach') || this.get('hide_attachments')) {
			return '';
		}

		var store = this.getSubStore('attachments');
		var names = [];

		store.each(function(attach) {
			if(!attach.get('hidden')) {
				names.push(attach.get('name'));
			}
		}, this);

		return names.join('; ');
	},

	/**
	 * Function can be used to check if the current record is faulty or not,
	 * this will check if message_class property in the {@link Zarafa.core.data.IPMRecord IPMRecord}
	 * is empty/'IPM'/'MEMO'/'REPORT' or message_class which not contains 'IPM'
	 * as prefix or suffix followed by dot(.), so we will not be able to display the message
	 * properly.
	 * @return {Boolean} true if message is faulty else false.
	 */
	isFaultyMessage : function()
	{
		var messageClass = this.get('message_class');

		if(Ext.isEmpty(messageClass)) {
			return true;
		}

		// @TODO handle other non supported types
		var faultyMessages = ['IPM', 'MEMO', 'REPORT','IPM.MAIL'];
		return this.isMessageClass(faultyMessages, false);
	},

	/**
	 * Function can be used to repair a faulty message which can not be displayed properly
	 * in webapp, it will change the message class to IPM.Note so all the fields related to
	 * mail record will be populated properly.
	 */
	fixFaultyMessage : function()
	{
		if(!this.isFaultyMessage()) {
			// don't do anything if this is a normal message
			return;
		}

		this.set('message_class', 'IPM.Note');

		// send a request to server to save changes
		this.save();
	},

	/**
	 * Builds URL to download different messages as its respective file format.
	 * Email message will be downloaded as RFC822-formatted stream with eml extension.
	 * It uses {@link Zarafa.core.data.IPMRecord IPMRecord} to get store and message entryids.
	 * But, in the case of downloading all those eml messages in a ZIP, we just need to
	 * pass 'AllAsZip' argument as true in url.
	 * @param {Boolean} allAsZip (optional) True to downloading all the attachments as ZIP
	 * @return {String} URL for downloading message as file.
	 */
	getDownloadMessageUrl : function(allAsZip)
	{
		var url = container.getBaseURL();
		url = Ext.urlAppend(url, 'load=download_message');
		url = Ext.urlAppend(url, 'storeid=' + this.get('store_entryid'));

		if(!allAsZip){
			url = Ext.urlAppend(url, 'entryid=' + this.get('entryid'));
		} else {
			url = Ext.urlAppend(url, 'AllAsZip=true');
		}
		return url;
	},

	/**
	 * Function checks whether record has any visible attachments or not,
	 * Attachment like inline images and smime are invisible attachments
	 * @return {Boolean} True Function will return true if any attachment
	 * is visible; false otherwise
	 */
	hasVisibleAttachments : function()
	{
		var visible = false;
		var attachments = this.getAttachmentStore().getRange();
		Ext.each(attachments, function(attachment){
			if(attachment.get('hidden') === false){
				visible = true;
			}
		}, this);
		return visible;
	}
});

Zarafa.core.data.RecordFactory.setBaseClassToMessageClass('REPORT.IPM', Zarafa.core.data.IPMRecord);
Zarafa.core.data.RecordFactory.setSubStoreToMessageClass('REPORT.IPM', 'recipients', Zarafa.core.data.IPMRecipientStore);
Zarafa.core.data.RecordFactory.setSubStoreToMessageClass('REPORT.IPM', 'attachments', Zarafa.core.data.IPMAttachmentStore);

Zarafa.core.data.RecordFactory.setBaseClassToMessageClass('IPM', Zarafa.core.data.IPMRecord);
Zarafa.core.data.RecordFactory.setSubStoreToMessageClass('IPM', 'recipients', Zarafa.core.data.IPMRecipientStore);
Zarafa.core.data.RecordFactory.setSubStoreToMessageClass('IPM', 'attachments', Zarafa.core.data.IPMAttachmentStore);

Zarafa.core.data.RecordFactory.setBaseClassToObjectType(Zarafa.core.mapi.ObjectType.MAPI_MESSAGE, Zarafa.core.data.IPMRecord);