/*
 *  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 for edition only. 
 * It can call a configured function when the selection changed in inline editor.
 */
Ext.define('Ametys.cms.editor.EditorFieldController', {
	extend: 'Ametys.ribbon.element.ui.FieldController',
	
	/**
	 * @cfg {String} selection-target-id Not supported
	 * @private
	 */
	/**
	 * @cfg {String} selection-subtarget-id Not supported
	 * @private
	 */
	/**
	 * @cfg {String} selection-subsubtarget-id Not supported
	 * @private
	 */
	/**
	 * @cfg {String} selection-subsubsubtarget-id Not supported
	 * @private
	 */
	
	/**
	 * @cfg {String} selection-listener The function name to call when the selection has changed.
	 */
	/**
	 * @property {String} _selectionListener See #cfg-selection-listener.
	 * @private
	 */
	
	/**
	 * @cfg {String} selection-node-type Specify this configuration to obtain a button that enable/disable depending on the type of current HTML node in edition mode. The string is a CSS selector which go from the current selection up to the required element: so it can be the tag name of the HTML element that have to match the current selection. A leading '!' will reverse the condition.  
	 */
	/**
	 * @cfg {String} selection-node-attribute Specify this configuration to obtain a button that enable/disable depending on the current HTML node attributes. The string is list of regexp separated by ';' that have to match the attributes of current HTML element. A leading '!' will reverse the regexp condition. 
	 */
	
	/**
	 * @property {String} _selectionNodeType See #cfg-selection-node-type.
	 * @private
	 */
	/**
	 * @property {Boolean} _reversedSelectionNodeType See #cfg-selection-node-type.
	 * @private
	 */
	
	/**
	 * @property {String[]} _selectionNodeAttributes See #cfg-selection-node-attribute. The leading '!' is transmitted to {@link #_reversedSelectionNodeAttributes}
	 * @private
	 */
	/**
	 * @property {String[]} _reversedSelectionNodeAttributes The leading '!' from {@link #cfg-selection-node-attribute} converted to true.
	 * @private
	 */
	/**
	 * @property {String} _currentField The current selected field.
	 * @private
	 */
	/**
	 * @property {String} _currentNode The current selected node.
	 * @private
	 */
	
	constructor: function (config)
	{
		this.callParent(arguments);
		
		this._selectionListener = this.getInitialConfig('selection-listener');

		this._selectionNodeType = null;
		this._reversedSelectionNodeType = false;
		this._selectionNodeAttributes = [];
		this._reversedSelectionNodeAttributes = [];
		this._currentField = null;
		this._currentNode = null;

		var nodeType = this.getInitialConfig("selection-node-type") || this.getInitialConfig("node-type"); 
		if (nodeType)
		{
			var i = nodeType.indexOf('!');
			if (i == 0)
			{
				this._selectionNodeType = nodeType.substring(1);
				this._reversedSelectionNodeType = true;
			}
			else
			{
				this._selectionNodeType = nodeType;
			}

			var attributes = this.getInitialConfig('selection-node-attribute') || this.getInitialConfig('attribute');
			
			var attributesAsArray = attributes != null ? attributes.split(";") : [];
			for (var j=0; j < attributesAsArray.length; j++)
			{
				var attribute = attributesAsArray[j];
				
				var i = attribute.indexOf('!');
				if (i == 0)
				{
					this._reversedSelectionNodeAttributes.push(attribute.substring(1));
				}
				else
				{
					this._selectionNodeAttributes.push(attribute);
				}
			}
		}
		
		Ametys.message.MessageBus.on(Ametys.message.Message.SELECTION_CHANGED, this._onSelectionChanged, this);
	},
	
	_onSelectionChanged: function(message)
	{
		var me = this;
		function getMatchingParentNode(editor, node)
		{
			if (me._reversedSelectionNodeType)
			{
				var n = node;
				while (n != null)
				{
					if (editor.dom.is(n, me._selectionNodeType))
					{
						return null;
					}
					else
					{
						n = n.parentNode;
					}
				}
				return node;
			}
			else
			{
				return editor.dom.getParent(node, me._selectionNodeType);	
			}
		}
		
		message = message || Ametys.message.MessageBus.getCurrentSelectionMessage();
		
		this._currentField = null;
		this._currentNode = null;

		var targets = message.getTargets();

		var formTarget = message.getTarget('form');
		var fieldTarget = message.getTarget('field');
		
		if (formTarget != null && fieldTarget != null && formTarget.getParameters().object != null && !formTarget.getParameters().object.destroyed && fieldTarget.getParameters().name != null)
	    {
		    this._currentField = formTarget.getParameters().object.findField(fieldTarget.getParameters().name);
	    }

	    var editor = this._currentField && typeof this._currentField.getEditor == "function" ? this._currentField.getEditor() : null;
		
		var nodeTarget = message.getTarget('node');
		
		this._currentNode = nodeTarget != null && editor != null && editor.dom != null ? getMatchingParentNode(editor, nodeTarget.getParameters()['object']) : null;
		
		var enable = this._currentNode != null;
		
		while (this._currentNode != null)
		{
			for (var j=0; j < this._selectionNodeAttributes.length; j++)
			{
				if (this._currentNode.getAttribute(this._selectionNodeAttributes[j]) == null)
				{
					enable = false;
				}
			}
			
			for (var j=0; j < this._reversedSelectionNodeAttributes.length; j++)
			{
				if (this._currentNode.getAttribute(this._reversedSelectionNodeAttributes[j]) != null)
				{
					enable = false;
				}
			}
			
			if (enable)
			{
				break;
			}
			
			this._currentNode = getMatchingParentNode(editor, this._currentNode.parentNode);
		}
		
		this.setDisabled(!enable);
		
		this._updateAdditionalDescription(targets, this._currentNode != null);

		if (this._selectionListener)
		{
			Ametys.executeFunctionByName (this._selectionListener, null, null, this, this._currentField, this._currentNode);
		}
	},

	/**
	 * Update the additional description.
	 * @param {Ametys.message.MessageTarget[]} targets The list of message targets
	 * @param {boolean} match True if the current selection matches the button configuration
	 * @private
	 */
	_updateAdditionalDescription: function (targets, match)
	{
		if (targets.length === 0)
        {
        	this.setAdditionalDescription(this.getInitialConfig("selection-description-empty") || this.getInitialConfig("no-selection-description"));
        }
        else if (!match)
        {
			this.setAdditionalDescription(this.getInitialConfig("selection-description-nomatch"));
        }
        else
        {
        	this.setAdditionalDescription("");
        }
	},

	/**
	 * Retrieve the currently selected field
	 * @return {Ametys.cms.form.widget.RichText} The current field
	 */
	getCurrentField: function ()
	{
	    return this._currentField;
	},
	
	/**
     * Retrieve the currently selected node
     * @return {HTMLElement} The current node
     */
	getCurrentNode: function ()
	{
	    return this._currentNode;
	}
});