/*
 *  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 representing the consistency state of a content
 * @private
 */
Ext.define('Ametys.plugins.cms.content.controller.ConsistencyController', {
	extend: 'Ametys.ribbon.element.ui.button.OpenToolButtonController',
	
    statics: {
        /**
         * @private
         * @readonly
         * @property {Number} CACHE_DURATION=60000 Duration in milliseconds of the consistency cache by the client 
         */
        CACHE_DURATION: 60000,
        /**
         * @private
         * @property {Object} cache Caching results to avoid too many requests
         * cache keys are content ids
         * cache values are an object with "date" that is the last results date for this content ; "type" contains the key (fully-ok-contents, mainly-ok-contents or not-ok-contents) and "result" that is the results object 
         */
        cache: {}
    },
    
	/**
	 * @cfg {String} consistency-ok-icon-decorator The CSS glass for icon decorator when the consistency of selected contents is ok
	 */
	/**
	 * @property {String} _okIconDecorator See #cfg-consistency-ok-icon-decorator
	 * @private
	 */
	
	/**
	 * @cfg {String} consistency-warning-icon-decorator The CSS glass for icon decorator when the consistency check has failed for at least one of selected contents.
	 */
	/**
	 * @property {String} _warnIconDecorator See #cfg-consistency-warning-icon-decorator
	 * @private
	 */
	
	/**
	 * @cfg {String} consistency-error-icon-decorator The CSS glass for icon decorator when the consistency check has failed for all selected contents.
	 */
	/**
	 * @property {String} _errorIconDecorator See #cfg-consistency-error-icon-decorator
	 * @private
	 */
	
	/**
	 * @cfg {String} consistency-okbut-icon-decorator The CSS glass for icon decoratorwhen the consistency check succeed but some links can not be checked.
	 */
	/**
	 * @property {String} _okButIconDecorator See #cfg-consistency-okbut-icon-decorator
	 * @private
	 */
    
	
	constructor: function(config)
	{
		this.callParent(arguments);
        
		this._okIconDecorator = this.getInitialConfig("consistency-ok-icon-decorator");
		this._warnIconDecorator = this.getInitialConfig("consistency-warning-icon-decorator");
		this._errorIconDecorator = this.getInitialConfig("consistency-error-icon-decorator");
		this._okButIconDecorator = this.getInitialConfig("consistency-okbut-icon-decorator");
		
		Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onEdited, this);
        Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._onDeleted, this);
	},
    
	/**
	 * Listener when the content has changed.
	 * Will update the lock state of the buttons effectively upon the current selection.
     * Will remove contents modified from cache
	 * @param {Ametys.message.Message} message The MODIFIED message.
	 * @protected
	 */
	_onEdited: function (message)
	{
		if (this.updateTargetsInCurrentSelectionTargets (message))
		{
    		this.refresh();
		}
        
        this._removeFromCache(message);
	},
    
    /**
     * Listener when the contet was removed.
     * Will remove contents modified from cache
     * @param {Ametys.message.Message} message The DELETED message.
     * @protected
     */
    _onDeleted: function(message)
    {
        this._removeFromCache(message);
    },

    /**
     * @private
     * Remove contents corresponding to the message from cache
     * @param {Ametys.message.Message} message A bus message
     */
    _removeFromCache: function(message)
    {
        var targets = this._getMatchingSelectionTargets(message);
        for (var i = 0; i < targets.length; i++)
        {
            var contentId = targets[i].getParameters().id;
            Ametys.plugins.cms.content.controller.ConsistencyController.cache[contentId] = null;
        }
    },
	
	updateState: function()
	{
		this.enable();
		this._checkConsistency(this.getMatchingTargets());
	},
	
	/**
	 * Get the lock state of given targets
	 * @param {Ametys.message.MessageTarget[]} targets The content targets
	 * @private
	 */
	_checkConsistency: function (targets)
	{
        var now = new Date().getTime()
        
		var contentIds = [];
        var cachedContentIds = [];
		for (var i=0; i < targets.length; i++)
		{
            var contentId = targets[i].getParameters().id;
            
            // Avoid cached
            if (!Ametys.plugins.cms.content.controller.ConsistencyController.cache[contentId] 
                || Ametys.plugins.cms.content.controller.ConsistencyController.cache[contentId].date + Ametys.plugins.cms.content.controller.ConsistencyController.CACHE_DURATION < now)
            {
			     contentIds.push(contentId);
            }
            else
            {
                cachedContentIds.push(contentId);
            }
		}

        if (contentIds.length > 0)
        {
            this.serverCall ('checkConsistency', [contentIds, true], Ext.bind(this._checkConsistencyCb, this, [cachedContentIds], 1), { priority: Ametys.data.ServerComm.PRIORITY_LONG_REQUEST, errorMessage: true, refreshing: true });
        }
        else
        {
            this._checkConsistencyCb(null, cachedContentIds);
        }
	},
	
	/**
	 * @private
	 * Callback function called after checking consistency of content targets
	 * @param {Object} params The JSON result
     * @param {Object} options The cb options
     * @param {String[]} options.cachedContentIds The id of the contents that are to handle and that already are in the cache
	 */
	_checkConsistencyCb: function (params, cachedContentIds)
	{
        // Put params in cache
        var now = new Date().getTime()
        Ext.Object.each(params || {}, function(key, value) {
            for (var i = 0; i < value.length; i++)
            {
                var contentId = value[i].id;
                Ametys.plugins.cms.content.controller.ConsistencyController.cache[contentId] = {
                    "date": now,
                    "type": key,
                    "result": Ext.clone(value[i])
                }
            }
        })
        
        // Merge cached infos
        var mergedParams = Ext.clone(params || { "mainly-ok-contents": [], "not-ok-contents": [], "fully-ok-contents": [], "no-right-contents": []});
        
        var oldestInfo = 0;
        for (var i = 0; i < cachedContentIds.length; i++)
        {
            var contentId = cachedContentIds[i];
            
            // Test to ensure that cache entry was not removed in the meanwhile
            if (Ametys.plugins.cms.content.controller.ConsistencyController.cache[contentId])
            {
                mergedParams[Ametys.plugins.cms.content.controller.ConsistencyController.cache[contentId].type].push(Ext.clone(Ametys.plugins.cms.content.controller.ConsistencyController.cache[contentId].result))
                if (oldestInfo == 0 || oldestInfo > Ametys.plugins.cms.content.controller.ConsistencyController.cache[contentId].date)
                {
                    oldestInfo = Ametys.plugins.cms.content.controller.ConsistencyController.cache[contentId].date
                }
            }
        }
        
        // Apply result
		this._updateTooltipDescription("", mergedParams, oldestInfo);
		this._updateIcon (mergedParams);
	},
	
	/**
	 * @private
	 * Update the tooltip description after checking consistency of content targets
	 * @param {String} description The initial description
	 * @param {Object} params The JSON result 
     * @param {Number} oldestInfo The oldest information taken from cache
	 */
	_updateTooltipDescription: function (description, params, oldestInfo)
	{
		var fullyOkContents = params["fully-ok-contents"];
		var mainlyOkContents = params["mainly-ok-contents"];
		var notOkContents = params["not-ok-contents"];
		var noRightContents = params["no-right-contents"];
		
		if (fullyOkContents.length > 0)
		{
			description = this._getContentsDescription(description, fullyOkContents, "ok");
		}
		if (mainlyOkContents.length > 0)
		{
			description = this._getContentsDescription(description, mainlyOkContents, "okbut");
		}
		if (notOkContents.length > 0)
		{
			description = this._getContentsDescription(description, notOkContents, "error");
		}
		if (noRightContents.length > 0)
		{
            description = this._getContentsDescription(description, noRightContents, "no-right");
        }
		
        if (oldestInfo != 0)
        {
            description += "<br/><br/>{{i18n CONTENT_CONSISTENCY_DESCRIPTION_FROMCACHE}}".replace(/\{0\}/g, Ext.Date.format(new Date(oldestInfo), Ext.Date.patterns.ShortTime)); 
        }
		this.setDescription (description);
	},
	
	/**
	 * Completes the contents description
	 * @param {String} description The starting description
	 * @param {Object} contents The contents in a JSON object 
	 * @param {String} type The type
	 * @returns {String} The completed description
	 * @private
	 */
	_getContentsDescription : function (description, contents, type)
	{
		if (description != "")
		{
			description += "<br/><br/>";
		}
		
		description += this.getInitialConfig("consistency-" + type + "-content-start-description");
		for (var i=0; i < contents.length; i++)
		{
			if (i != 0) 
			{
				description += ", ";
			}
			description += contents[i].title 
                           + ((contents[i].description != contents[i].title) ? " (" + contents[i].description + ")" : "");
		}
		description += this.getInitialConfig("consistency-" + type + "-content-end-description");
		return description;
	},
	
	/**
	 * @private
	 * Update the icons after checking consistency of content targets
	 * @param {Object} params The JSON result 
	 */
	_updateIcon: function (params)
	{
		var fullyOkContents = params["fully-ok-contents"];
		var mainlyOkContents = params["mainly-ok-contents"];
		var notOkContents = params["not-ok-contents"];
		
		if (notOkContents.length > 0 && fullyOkContents.length == 0)
		{
			this.setIconDecorator(this._errorIconDecorator);
		}
		else if (notOkContents.length > 0 && fullyOkContents.length > 0)
		{
			this.setIconDecorator(this._warnIconDecorator);
		}
		else if (mainlyOkContents.length > 0)
		{
			this.setIconDecorator(this._okButIconDecorator);
		}
		else if (fullyOkContents.length > 0)
		{
			this.setIconDecorator(this._okIconDecorator);
		}
	},
	
	disable:function()
	{
        this.setIconDecorator();
        this.setDescription(this.getInitialConfig('description'));
        this.callParent(arguments);
    } 
});