/*
 *  Copyright 2014 Anyware Services
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * This class is the representation of a content.
 */
Ext.define(
	"Ametys.cms.content.Content", 
	{
		config: {
			/**
			 * @cfg {String} id The unique id of the content
			 */
			/**
			 * @method getId Get the #cfg-id
			 * @return {String} The id
			 */
			/** @ignore */
			id: null,
			/**
			 * @cfg {String} title The title of the content
			 */
			/**
			 * @method getTitle Get the #cfg-title
			 * @return {String} The title
			 */
			/** @ignore */
			title: null,
            
            /**
             * @cfg {Object} titleVariants The title of each language if title is a multilingual string. Null otherwise.
             */
            /**
             * @method getTitleVariants Get the #cfg-titleVariants
             * @return {Object} The title variants for each languages. Null if title is not multilingual.
             */
            /** @ignore */
            titleVariants: null,
            
			/**
			 * @cfg {String} path The path in the repository of the content
			 */
			/**
			 * @method getPath Get the #cfg-path
			 * @return {String} The path
			 */
			/** @ignore */
			path: null,
			/**
			 * @cfg {String} lang The lang of the content
			 */
			/**
			 * @method getLang Get the #cfg-lang
			 * @return {String} The lang
			 */
			/** @ignore */
			lang: null,
			/**
			 * @cfg {String} name The name of the content
			 */
			/**
			 * @method getName Get the #cfg-name
			 * @return {String} The name
			 */
			/** @ignore */
			name: null,
			/**
			 * @cfg {String[]} types The content types
			 */
			/**
			 * @method getTypes Get the #cfg-types
			 * @return {String[]} The types
			 */
			/** @ignore */
			types: [],
			/**
			 * @cfg {String[]} mixins The mixins
			 */
			/**
			 * @method getMixins Get the #cfg-mixins
			 * @return {String[]} The mixins
			 */
			/** @ignore */
			mixins: [],
			/**
			 * @cfg {String} workflowName The identifier of the workflow used for this content
			 */
			/**
			 * @method getWorkflowName Get the #cfg-workflowName
			 * @return {String} The workflow name
			 */
			/** @ignore */
			workflowName: null,
			/**
			 * @cfg {String} exists True if the content still exists
			 */
			/**
			 * @method getExists Get the #cfg-exists
			 * @return {String} The exits status
			 */
			/** @ignore */
			exists: false,
			/**
			 * @cfg {Number[]} workflowSteps The arrays of current workflow steps
			 */
			/**
			 * @method getWorkflowSteps Get the #cfg-workflowSteps
			 * @return {Number[]} The worfklow steps
			 */
			/** @ignore */
			workflowSteps: [],
			/**
			 * @cfg {Number[]} availableActions The arrays of current available action
			 */
			/**
			 * @method getAvailableActions Get the #cfg-availableActions
			 * @return {Number} The available actions
			 */
			/** @ignore */
			availableActions: [],
			/**
			 * @cfg {boolean} isModifiable True if this content can be modified. False if it read-only: this is not about rights. Can be false for a virtual content dynamically created by reading a RSS or a database... but basically not stored in JCR
			 */
			/**
			 * @method getIsModifiable Get the #cfg-isModifiable
			 * @return {boolean} The is modifiable
			 */
			/** @ignore */
			isModifiable: false,
			/**
			 * @cfg {boolean} isSimple True if this content is a simple content type.
			 */
			/**
			 * @method getIsSimple Get the #cfg-isSimple
			 * @return {boolean} The is simple
			 */
			/** @ignore */
			isSimple: false,
            /**
             * @cfg {boolean} isReferenceTable True if this content is a reference table's entry.
             */
            /**
             * @method getIsReferenceTable Get the #isReferenceTable
             * @return {boolean} The is reference table
             */
            /** @ignore */
            isReferenceTable: false,
			/**
			 * @cfg {String} parent The parent of this content (if defined). Always null for a content which is not reference table entry
			 */
			/**
			 * @method getParent Get the #cfg-parent
			 * @return {String} The parent
			 */
			/** @ignore */
			parent: null,
			/**
			 * @cfg {String[]} rights List of the id of the rights the current user have on this content
			 */
			/**
			 * @method getRights Get the #cfg-rights
			 * @return {String[]} The rights
			 */
			/** @ignore */
			rights: [],
			/**
			 * @cfg {Boolean} locked True if the content is locked
			 */
			/**
			 * @method getLocked Get the #cfg-locked
			 * @return {Boolean} The locked state
			 */
			/** @ignore */
			locked: false,
			/**
			 * @cfg {String} lockOwner The login of lock owner. Can be null is the content is not locked.
			 */
			/**
			 * @method getLockOwner Get the #cfg-lockOwner
			 * @return {String} The lock owner
			 */
			/** @ignore */
			lockOwner: null,
			/**
			 * @cfg {Boolean} canUnlock On a locked content, `true` if the current user can unlock it.
			 */
			/**
			 * @method canUnlock Get the #cfg-lockOwner
			 * @return {Boolean} On a locked content, `true` if the current user can unlock it. 
			 */
			/** @ignore */
			canUnlock: false,
			
			/**
			 * @cfg {String} lastContributor The login of last contributor.
			 */
			/**
			 * @method getLastContributor Get the #cfg-lastContributor
			 * @return {String} The last contributor
			 */
			/** @ignore */
			lastContributor: null,
			
			/**
			 * @cfg {String} lastModified The last modified date as String.
			 */
			/**
			 * @method getLastModified Get the #cfg-lastModified
			 * @return {String} The last modified
			 */
			/** @ignore */
			lastModified: null,
			
			/**
			 * @cfg {String} creationDate The creation date as String
			 */
			/**
			 * @method getCreationDate Get the #cfg-creationDate
			 * @return {String} The creation date
			 */
			/** @ignore */
			creationDate: null,

			/**
			 * @cfg {Object} additionalData The additional data
			 */
			/**
			 * @method getAdditionalData Get the #cfg-additionalData
			 * @return {Object} The additional data
			 */
			additionalData: {},
			
			/**
			 * @cfg {Number} contentWidth The width of content. Determines the fixed width of rich texts when editing the content. Set to 0 to not fix width.
			 */
			/**
			 * @method getContentWidth Get the #cfg-contentWidth
			 * @return {Number} The width of content or 0 is the width is not fixed. 
			 */
			/** @ignore */
			contentWidth: 0,

            /**
             * @cfg {boolean} isTaggable True if this content can be tagged.
             */
            /**
             * @method getIsTaggable Get the #cfg-isTaggable
             * @return {boolean} The page is taggable
             */
            /** @ignore */
            isTaggable: false,

            /**
             * @cfg {String} scheduledArchivingDate the scheduled archiving date as String (or null if none).
             */
            /**
             * @method getScheduledArchivingDate Get the #cfg-scheduledArchivingDate
             * @return {String} The scheduled archiving date as String (or null if none)
             */
            /** @ignore */
            scheduledArchivingDate: null,

            /**
             * @cfg {Number} reportsCount the number of reports.
             */
            /**
             * @method getReportsCount Get the #cfg-reportsCount
             * @return {Number} The number of reports
             */
            /** @ignore */
            reportsCount: 0,
			
			/**
			 * @cfg {String} [messagetTarget=Ametys.message.MessageTarget#CONTENT]
			 */
			messageTarget: null
		},
		
        /** @property {String[]} _ancestorsOrSelfTypes The ancestors or self types */
        _ancestorsOrSelfTypes: null,
        /** @property {String[]} _ancestorsOrSelfMixins The ancestors or self mixins */
        _ancestorsOrSelfMixins: null,

		/**
		 * Creates a content instance
		 * @param {Object} config See configuration doc.
		 */
		constructor: function (config)
		{
			this.initConfig(config);
			
			this._messageTarget = config.messageTarget || Ametys.message.MessageTarget.CONTENT;
		},
		
		/**
		 * Determines if the current user has the specified right on the content
		 * @param {String} rightId The identifier of the right to check
		 * @return {boolean} True or false
		 */
		hasRight: function(rightId)
		{
			return Ext.Array.contains(this._rights, rightId);
		},
		
		/**
		 * Try to lock the content.
		 * If the content was already locked, the callback is called with 'true' as argument
		 * @param {Function} callback The method to call
		 * @param {boolean} callback.success true if the lock was a success, else false (and a message was already displayed to the user)
         * @param {boolean} force true to force lock without checking current state
		 */
		lock: function(callback, force)
		{
			if (force === true || !this._locked)
			{    
				// Try to lock the content
				Ametys.data.ServerComm.callMethod({
					role: "org.ametys.cms.lock.LockContentManager",
					methodName: "unlockOrLock",
					parameters: [[this._id], 'lock'],
					callback: {
						scope: this,
						handler: this._lockCB,
						arguments: {callback: callback}
					},
					errorMessage: {
						msg: "{{i18n CONTENT_LOCK_ERROR}}",
						category: 'Ametys.cms.content.Content'
					}
				});
			}
			else if (Ext.isFunction(callback))
			{
				callback(true);
			}
		},
		
		/**
		 * Try to unlock the content.
		 * If the content was already locked, the callback is called with 'true' as argument
		 * @param {Function} callback The method to call
		 * @param {boolean} callback.success true if the lock was a success, else false (and a message was already displayed to the user)
         * @param {boolean} force true to force unlock without checking current state
		 */
		unlock: function(callback, force)
		{
			if (force === true || this._locked)
			{
				// Try to unlock the content
				Ametys.data.ServerComm.callMethod({
					role: "org.ametys.cms.lock.LockContentManager",
					methodName: "unlockOrLock",
					parameters: [[this._id], 'unlock'],
					callback: {
						scope: this,
						handler: this._lockCB,
						arguments: {callback: callback}
					},
					errorMessage: {
						msg: "{{i18n CONTENT_LOCK_ERROR}}",
						category: 'Ametys.cms.content.Content'
					}
				});
			}
			else if (Ext.isFunction(callback))
			{	
			     callback(true);
			}
		},
		
		/**
		 * @private
		 * Callback of #lock
		 * @param {Object[]} response the server response
		 * @param {String} response.mode the mode ( unlock or lock )
		 * @param {String[]} response.unlocked-contents-id the ids of the unlocked contents
		 * @param {String[]} response.unlocked-contents the unlocked contents
		 * @param {String[]} response.still-locked-contents the contents that are still locked
		 * @param {String[]} response.fail-locked-contents the contents that failed to be locked
		 * @param {String[]} response.locked-contents-id the ids of the locked contents
		 * @param {String[]} response.locked-contents the locked contents
		 * @param {String[]} response.already-locked-contents the contents already locked
		 * @param {String[]} response.fail-unlocked-contents the contents that failed to be unlocked
		 * @param {Object[]} args the callback arguments
		 * @param {Function} args.callback the callback to call
		 */
		_lockCB: function(response, args)
		{

			var callback = args['callback'];
			var unlockMode = response['mode'] == 'unlock';
			if (unlockMode)
			{
				var success = true;
				var unlockedContents = response['unlocked-contents'];
				var stillLockedContents = response['still-locked-contents'];
				var failLockedContents = response['fail-unlocked-contents'];
				
				var msg = "";
				if (stillLockedContents != "" || failLockedContents != "")
				{
					if (unlockedContents != "")
					{
						msg += "{{i18n CONTENT_UNLOCK_ACTION_UNLOCKED_CONTENTS}}" + unlockedContents;
					}
					
					if (stillLockedContents != "")
					{
						if (msg != '')
						{
							msg + "<br/><br/>";
						}
						msg += "{{i18n CONTENT_UNLOCK_ACTION_STILL_LOCKED_CONTENTS}}" + stillLockedContents;
					}
					
					if (failLockedContents != "")
					{
						if (msg != '')
						{
							msg + "<br/><br/>";
						}
						msg += "{{i18n CONTENT_UNLOCK_ACTION_FAILED_CONTENTS}}" + failLockedContents;
					}
					
					Ametys.log.ErrorDialog.display({
						title: "{{i18n CONTENT_UNLOCK_ACTION}}",
						text: msg,
						details: "",
						category: "Ametys.plugins.cms.content.controller.LockController.act"
					});
					
					success = false;
				}
				
				var unlockedContentIds = response["unlocked-contents-id"].split(',');
				if (unlockedContentIds.length > 0 && unlockedContentIds[0])
				{
                    this._locked = false;
                    
					Ext.create("Ametys.message.Message", {
						type: Ametys.message.Message.LOCK_CHANGED,
						
						targets: {
							id: this._messageTarget,
							parameters: { ids: unlockedContentIds }
						}
					});
				}
				
				if (Ext.isFunction(callback))
				{
					callback(success);
				}
			}
			else
			{
				var success = true;
				var lockedContents = response['locked-contents'];
				var alreadyLockedContents = response['already-locked-contents'];
				var failUnlockedContents = response['fail-locked-contents'];
				
				if (alreadyLockedContents != "" || failUnlockedContents != "")
				{
					var msg = "";
					if (lockedContents != "")
					{
						msg += "{{i18n CONTENT_LOCK_ACTION_LOCKED_CONTENTS}}"  + lockedContents;
					}
					
					if (alreadyLockedContents != "")
					{
						if (msg != '')
						{
							msg + "<br/><br/>";
						}
						msg += "{{i18n CONTENT_LOCK_ACTION_ALREADY_LOCKED_CONTENTS}}" + alreadyLockedContents;
					}
					
					if (failUnlockedContents != "")
					{
						if (msg != '')
						{
							msg + "<br/><br/>";
						}
						msg += "{{i18n CONTENT_LOCK_ACTION_FAILED_CONTENTS}}" + failUnlockedContents;
					}
					
					Ametys.log.ErrorDialog.display({
						title: "{{i18n CONTENT_LOCK_ACTION}}",
						text: msg,
						details: "",
						category: "Ametys.plugins.cms.content.controller.LockController.act"
					});
					
					success = false;
				}
				
				var lockedContentIds = response['locked-contents-id'].split(',');
				if (lockedContentIds.length > 0 && lockedContentIds[0])
				{
                    this._locked = true;
                    this._lockOwner = response['lockOwner'];
                    
					Ext.create("Ametys.message.Message", {
						type: Ametys.message.Message.LOCK_CHANGED,
						
						targets: {
							id: this._messageTarget,
							parameters: { ids: lockedContentIds }
						}
					});
				}
				
				if (Ext.isFunction(callback))
				{
					callback(success);
				}
			}
		},
		
		/**
		 * Get the tab of ancestors content types and itself
		 * @return {String[]} The tab of ancestors content types and itself
		 */
		getAncestorsOrSelfTypes: function()
		{
			if (!this._ancestorsOrSelfTypes)
			{
                this._ancestorsOrSelfTypes = [];
				Ext.Array.each(this._types, function(contentId) 
				{
					var contentType = Ametys.cms.content.ContentTypeDAO.getContentType(contentId);
					contentType.getAncestorsOrSelf().each(function(ancestorContent) 
					{
						if (!Ext.Array.contains(this._ancestorsOrSelfTypes, ancestorContent.get("id")))
						{
							this._ancestorsOrSelfTypes.push(ancestorContent.get("id"));
						}
					}, this);
				}, this);
			}
			
			return this._ancestorsOrSelfTypes;
		},
		
		/**
		 * Get the tab of ancestors mixins and itself
		 * @return {String[]} The tab of ancestors mixins and itself
		 */
		getAncestorsOrSelfMixins: function()
		{
			if (!this._ancestorsOrSelfMixins)
			{
                this._ancestorsOrSelfMixins = [];
				Ext.Array.each(this._mixins, function(contentId) 
				{
					var contentType = Ametys.cms.content.ContentTypeDAO.getContentType(contentId);
					contentType.getAncestorsOrSelf().each(function(ancestorContent) 
					{
						if (!Ext.Array.contains(this._ancestorsOrSelfMixins, ancestorContent.get("id")))
						{
							this._ancestorsOrSelfMixins.push(ancestorContent.get("id"));
						}
					}, this);
				}, this);
			}
			
			return this._ancestorsOrSelfMixins;
		},
		
		/**
		 * Get the content's properties
		 * @return {Object} The content's properties
		 */
		getProperties: function (initialProperty)
		{
			return Ext.apply ({
					id: this._id,
					name: this._name,
					title: this._title,
					path: this._path,
					lang: this._lang,
					types: this._types,
					ancestorsOrSelfTypes: this.getAncestorsOrSelfTypes(), 
					ancestorsOrSelfMixins: this.getAncestorsOrSelfMixins(), 
					mixins: this._mixins,
					workflowName : this._workflowName,
                    workflowSteps : this._workflowSteps,
					isModifiable : this._isModifiable,
                    isReferenceTable: this._isReferenceTable,
                    isSimple: this._isSimple,
                    parent: this._parent,
					rights: this._rights,
					availableActions: this._availableActions,
					additionalData: this._additionalData,
					contentWidth: this._contentWidth,
                    reportsCount: this._reportsCount
				}, initialProperty
			);
		}
	}
);