/*
 *  Copyright 2013 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 controls a ribbon button enable if the selection is a content, a page or both only.
 * @private
 */
Ext.define('Ametys.plugins.web.tag.TagController', {
	extend: 'Ametys.web.controller.WebButtonController',
	
	/**
	 * @property {String[]} [_pageIds=[]] List of identifiers of pages concerned by the action of the controller
	 * @private
	 */
	
	/**
	 * @property {String[]} [_contentIds=[]] List of identifiers of contents concerned by the action of the controller
	 * @private
	 */
	
	constructor: function(config)
	{
		this.callParent(arguments);
		
		this._contentIds = [];
		this._pageIds = [];
		
		Ametys.message.MessageBus.on(Ametys.message.Message.LOCK_CHANGED, this._onModified, this);
		Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onModified, this);
	},
	
	/**
	 * Get the matching targets that match tag conditions
	 * @return {Ametys.message.MessageTarget[]} targets filling all conditions.
	 */
	getAllRightTargets: function ()
	{
		var matchingTargets = Ametys.message.MessageBus.getCurrentSelectionMessage().getTargets(Ametys.message.MessageTarget.PAGE);
		matchingTargets = Ext.Array.merge(matchingTargets, Ametys.message.MessageBus.getCurrentSelectionMessage().getTargets(Ametys.message.MessageTarget.CONTENT))
		var me = this;
		
		return Ext.Array.filter (matchingTargets, function (target) {
			return Ext.Array.contains(me._pageIds, target.getParameters().id) || Ext.Array.contains(me._contentIds, target.getParameters().id)
		});
	},

	/**
	 * Listener when the lock has changed or a page or content has been modified
	 * Will update the state of the button effectively upon the current selection.
	 * @param {Ametys.message.Message} message The lock changed or modified message.
	 * @protected
	 */
	_onModified: function (message)
	{
		if (this.updateTargetsInCurrentSelectionTargets (message))
		{
    		this.refresh();
		}
	},
	
	updateState: function()
	{
		this.disable();
		this.setAdditionalDescription('');
		
		this._getStatus();
	},
	
	/**
	 * Get the status of current contents and/org pages targets
	 * @private
	 */
	_getStatus: function ()
	{
		var pageTargets = Ametys.message.MessageTargetHelper.findTargets(this.getMatchingTargets(), Ametys.message.MessageTarget.PAGE)
		var contentTargets = Ametys.message.MessageTargetHelper.findTargets(this.getMatchingTargets(), Ametys.message.MessageTarget.CONTENT)
		
		if (pageTargets.length == 0 && contentTargets.length == 0)
		{
			this.setAdditionalDescription(this.getInitialConfig('selection-description-empty'));
		}
		else if (pageTargets.length > 1)
		{
			// Page multi-selection is forbidden
			this.setAdditionalDescription(this.getInitialConfig('selection-description-page-multiselectionforbidden'));
		}
		else if (pageTargets.length == 0 && contentTargets.length > 1)
		{
			// Content multi-selection is forbidden
			this.setAdditionalDescription(this.getInitialConfig('selection-description-content-multiselectionforbidden'));
		}
		else if (pageTargets.length == 1 && (contentTargets.length > 1 || contentTargets.length == 0))
		{
            var description = contentTargets.length > 1 ? this.getInitialConfig('selection-description-content-multiselectionforbidden') : '';

            var status = this._calculateStatus(pageTargets[0], null);
            this._getStatusCb(status, description);
		}
		else if (contentTargets.length == 1)
		{
            var status = this._calculateStatus(pageTargets[0], contentTargets[0]);
            this._getStatusCb(status, "");
		}
	},

    /**
     * Simulation of getStatus server side, done only client-side.
     * @param {Object} pageTarget page target see Ametys.ribbon.element.ui.CommonController#getMatchingTargets
     * @param {Object} contentTarget content target see Ametys.ribbon.element.ui.CommonController#getMatchingTargets
     * @return {Object} a map of arrays representing contents in different states that will be sent to _getStatusCb
     */
    _calculateStatus: function(pageTarget, contentTarget)
    {
        var page = null;
        if (pageTarget)
        {
            page = pageTarget.getParameters().page;
        }
        var content = null;
        if (contentTarget)
        {
            content = contentTarget.getParameters().content;
        }

        var result = {};
        result["nomodifiable-pages"] = [];
        result["noright-pages"] = [];
        result["allright-pages"] = [];

        result["nomodifiable-contents"] = [];
        result["locked-contents"] = [];
        result["noright-contents"] = [];
        result["allright-contents"] = [];

        var enabledOnModifiableOnly = this.getConfig("enabled-on-modifiable-only") == "true";
        var enabledOnRightOnly = this.getConfig("enabled-on-right-only") == "true";
        var enabledOnUnlockOnly = this.getConfig("enabled-on-unlock-only") == "true";

        var error = false;
        if (page != null)
        {
            if (enabledOnModifiableOnly && !page.getIsModifiable())
            {
                var i18nStr = this.getConfig("nomodifiable-page-description");
                var description = Ext.String.format(i18nStr, page.getTitle());
                var contentParam = this._getPageDefaultParameters(page);
                contentParam["description"] = description;
                result["nomodifiable-pages"].push(contentParam);
                error = true;
            }

            if (enabledOnRightOnly && !this.hasRightOnAny([pageTarget]))
            {
                var i18nStr = this.getConfig("noright-page-description");
                var description = Ext.String.format(i18nStr, page.getTitle());
                var contentParam = this._getPageDefaultParameters(page);
                contentParam["description"] = description;
                result["noright-pages"].push(contentParam);
                error = true;
            }

            if (!error)
            {
                var i18nStr = this.getConfig("allright-page-description");
                var description = Ext.String.format(i18nStr, page.getTitle());
                var contentParam = this._getPageDefaultParameters(page);
                contentParam["description"] = description;
                result["allright-pages"].push(contentParam);
            }
        }

        error = false;

        if (content != null)
        {
            if (enabledOnModifiableOnly && !content.getIsTaggable())
            {
                var i18nStr = this.getConfig("nomodifiable-content-description");
                var description = Ext.String.format(i18nStr, content.getTitle());
                var contentParam = this._getContentDefaultParameters(content);
                contentParam["description"] = description;
                result["nomodifiable-contents"].push(contentParam);
                error = true;
            }

            if (enabledOnUnlockOnly && content.getLocked())
            {
                var i18nStr = this.getConfig("locked-content-description");
                var lockOwner = content.getLockOwner();
                var fullName = lockOwner != null ? lockOwner.fullname : "";
                var login = lockOwner != null ? lockOwner.login : "Anonymous";
                var description = Ext.String.format(i18nStr, content.getTitle(), fullName, login);
                var contentParam = this._getContentDefaultParameters(content);
                contentParam["description"] = description;
                result["locked-contents"].push(contentParam);
                error = true;
            }

            if (enabledOnRightOnly && !this.hasRightOnAny([contentTarget]))
            {
                var i18nStr = this.getConfig("noright-content-description");
                var description = Ext.String.format(i18nStr, content.getTitle());
                var contentParam = this._getContentDefaultParameters(content);
                contentParam["description"] = description;
                result["noright-contents"].push(contentParam);
                error = true;
            }

            if (!error)
            {
                var i18nStr = this.getConfig("allright-content-description");
                var description = Ext.String.format(i18nStr, content.getTitle());
                var contentParam = this._getContentDefaultParameters(content);
                contentParam["description"] = description;
                result["allright-contents"].push(contentParam);
            }
        }

        return result;
    },


    /**
     * create a json object representing a page
     * @private
     * @param {Ametys.web.page.Page} page The page to analyse
     * @return {Object} containing id and title
     */
    _getPageDefaultParameters : function(page)
    {
        var pageParams = {};
        pageParams["id"] = page.getId();
        pageParams["title"] = page.getTitle();
        return pageParams;
    },

    /**
     * create a json object representing a content
     * @private
     * @param {Ametys.cms.content.Content} content The content to analyse
     * @return {Object} containing id and title
     */
    _getContentDefaultParameters : function(content)
    {
        var contentParams = {};
        contentParams["id"] = content.getId();
        contentParams["title"] = content.getTitle();
        return contentParams;
    },

	/**
	 * @private
	 * Callback function called after retrieving the lock state of content targets
	 * @param {Object} params The JSON result 
     * @param {String[]} params.allright-pages The identifiers of pages that are ok
     * @param {String[]} params.nomodifiable-pages The identifiers of pages that are not modifiable
     * @param {String[]} params.noright-pages The identifiers of pages that the user cannot modify because of missing rights
     * @param {String[]} params.allright-contents The identifiers of content that are ok
     * @param {String[]} params.nomodifiable-contents The identifiers of content that are not modifiable
     * @param {String[]} params.locked-contents The identifiers of content that are locked
     * @param {String[]} params.noright-contents The identifiers of content that the user cannot modify because of missing rights
     * @param {String} description Additionnal description provided from #_getStatus
	 */
	_getStatusCb: function (params, description)
	{
		// Update tooltip description
		this._updateTooltipDescription(description || '', params);
		
		this._contentIds = [];
		this._pageIds = [];
		
		var allRightPages = params['allright-pages'];
		if (allRightPages.length > 0)
		{
			for (var i=0; i < allRightPages.length; i++)
			{
				this._pageIds.push(allRightPages[i].id)
			}
			this.enable();
		}
		
		var allRightContents = params['allright-contents'];
		if (allRightContents.length > 0)
		{
			for (var i=0; i < allRightContents.length; i++)
			{
				this._contentIds.push(allRightContents[i].id)
			}
			this.enable();
		}
		
	},
	
	/**
	 * @protected
	 * Update the tooltip description according state of the current selection
	 * @param description The initial description. Can be empty.
	 * @param params The JSON result received
	 */
	_updateTooltipDescription: function (description, params)
	{
		description = this._handlingMultiple (description, 'allright-content', params['allright-contents']);
		description = this._handlingMultiple(description, "locked-content", params['locked-contents']);
		description = this._handlingMultiple(description, "noright-content", params['noright-contents']);
		description = this._handlingMultiple(description, "nomodifiable-content", params['nomodifiable-contents']);
		
		description = this._handlingMultiple(description, "allright-page", params['allright-pages']);
		description = this._handlingMultiple(description, "nomodifiable-page", params['nomodifiable-pages']);
		description = this._handlingMultiple(description, "noright-page", params['noright-pages']);
		
		this.setAdditionalDescription (description);
	},
	
	/**
	 * @private
	 * Add text to description
	 * @param description The initial description to concatenate. Can be empty.
	 * @param {String} prefix The parameters prefix to used to retrieve the start and end description. The start and end description are retrieved from initial configuration with [prefix]-start-description and [prefix]-end-description
	 * @param {Object[]} pages The concerned pages or contents. If empty, no text will be concatenated
	 */
	_handlingMultiple: function(description, prefix, pages)
	{
		if (pages.length > 0)
		{
			if (description != "")
			{
				description += "<br/><br/>";
			}
			
			description += this.getInitialConfig(prefix + "-start-description");
			for (var i=0; i < pages.length; i++)
			{
				if (i != 0) 
				{
					description += ", ";
				}
				description += pages[i].description;
			}
			description += this.getInitialConfig(prefix + "-end-description");
		}
		
		return description;
	},
	
    areSameTargets: function(target1, target2)
    {
        var match = this.callParent(arguments);
        
        if (match && target1.getId() == Ametys.message.MessageTarget.PAGE)
        {
            // In case of page targets, the possible content subtargets must be compared as well.
            var contentTargets1 = target1.getSubtargets(Ametys.message.MessageTarget.CONTENT);
            var contentTargets2 = target2.getSubtargets(Ametys.message.MessageTarget.CONTENT);
            
            match = contentTargets1.length == contentTargets2.length;
            
            if (match && contentTargets1.length > 0)
            {
                var cids1 = Ext.Array.sort(Ext.Array.map(contentTargets1, function(t) {
                    return t.getParameters().id;
                }));
                var cids2 = Ext.Array.sort(Ext.Array.map(contentTargets2, function(t) {
                    return t.getParameters().id;
                }));
                
                match = Ext.Array.equals(cids1, cids2);
            }
        }
        
        return match;
    },
    
    isNoModifiable: function (targets)
    {
        var modifiable = this.callParent(arguments);
        if (!modifiable)
        {
            // no modifiable selected pages, check contents
            for (var i=0; i < targets.length; i++)
	        {
	            var contentTargets = targets[i].getSubtargets(Ametys.message.MessageTarget.CONTENT);
                for (var j=0; j < contentTargets.length; j++)
		        {
		            if (contentTargets[j].getParameters().isModifiable)
		            {
		                // at least one content modifiable
		                return true;
		            }
		        }
	        }
        }
        
        return modifiable;
    }
});