/*
 *  Copyright 2015 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 abstract mother class for forms layouts.
 */
Ext.define('Ametys.plugins.forms.content.Layout', {
	
	singleton: true,
	
	/**
	 * @private
	 * @property {Object} _layoutActions the actions on the layout 
	 */
	
	/**
	 * @private
	 * @property {String[]} _handledLayoutClass the CSS class handled for the layout
	 */
	
	constructor: function(config)
	{
		this._layoutActions = {};
		this._handledLayoutClass = [];
		
		this.callParent(arguments);
	},
	
	/**
	 * Register the layout of the button
	 * @param {String} css the css value for layout
	 */
	registerLayoutCSS: function(css)
	{
        this._handledLayoutClass.push(css);
	},

        /**
     * Register the layout of the button
     * @param {Ametys.ribbon.element.ui.ButtonController} controller the controller having layout informations ('value' and 'create-action' keys)
     */
    registerLayoutAction: function(controller)
    {
        var value = controller.getInitialConfig('value');
        var createAction = controller.getInitialConfig('create-action');

        this._layoutActions[value] = createAction;
    },

	/**
	 * Enable/disable the layout menu according to the selection
	 * @param {Ametys.cms.editor.EditorButtonController} controller The button's controller
	 * @param {Ametys.cms.form.widget.RichText} field The current field. Can be null
	 * @param {HTMLElement} node The currently selected node. Can be null.
	 */
	layoutMenuListener: function(controller, field, node)
	{
		Ametys.plugins.forms.content.Forms._currentNode = (field != null && node != null && field.getEditor() != null) ? Ametys.plugins.forms.content.Forms._getForm(field.getEditor(), field.getEditor().selection.getNode()) : null; 
		controller.setDisabled(Ametys.plugins.forms.content.Forms._currentNode == null);
	},

	/**
	 * Enable/disable the layout buttons according to the selection
	 * @param {Ametys.cms.editor.EditorButtonController} controller The button's controller
	 * @param {Ametys.cms.form.widget.RichText} field The current field. Can be null
	 * @param {HTMLElement} node The currently selected node. Can be null.
	 */
	layoutListener: function(controller, field, node)
	{
		var toggled = false;

		if (node != null && field != null)
		{
			var specificNode = Ametys.plugins.forms.content.Forms._getForm(controller.getCurrentField().getEditor(), node);
			if (specificNode != null)
			{
				var layout = specificNode.getAttribute('layout');
				if (layout == null) 
				{
					toggled = controller.getInitialConfig('default');
				}
				else
				{
					toggled = layout == controller.getInitialConfig('value');
				}
			}
		}
		
		controller.toggle(toggled);
	},
	
	/**
	 * Get the CSS class of handled layout
	 * @return {String[]} The handled CSS class in a Array
	 */
	getHandledLayoutClass: function ()
	{
		return this._handledLayoutClass;
	},
	
	/**
	 * Get the current layout
	 * @param {HTMLElement} node the node to get the layout from
	 */
	_getCurrentLayout: function(node)
	{
        // FIXME replace tinymce.activeEditor
		var form = Ametys.plugins.forms.content.Forms._getForm(tinymce.activeEditor, node || tinyMCE.activeEditor.selection.getNode());
		var layout = form.getAttribute("layout");
		return layout || Ametys.form.widget.RichText.RichTextConfiguration.getTag("div", tinyMCE.activeEditor.getParam('category')).getAttribute("layout").defaultValue;
	},
	
	/**
	 * Clean the current layout
	 */
	_cleanLayout: function()
	{
		var me = this;
		
		if (Ametys.plugins.forms.content.Forms._currentNode == null)
		{
			return;
		}	

		function formClean(parentNode, before, node)
		{
			var nodeIsOk = me._isNodeOk(node);
			if (node.childNodes.length > 0)
			{
				var nextNode = node.childNodes[0];
				while (nextNode != null)
				{
					formClean(nodeIsOk ? node : parentNode, nodeIsOk ? nextNode : before, nextNode);
					nextNode = nextNode.nextSibling;
				}
			}
			
			if (nodeIsOk && node.parentNode != parentNode && parentNode != null)
			{
				parentNode.insertBefore(node, before);
			}
		}

		function formDestroy(node)
		{
			if (node.tagName == null)
			{
				return null;
			}
			
			if (!me._isNodeOk(node))
			{
				node.parentNode.removeChild(node);
				return true;
			}
			else
			{
				for (var k = 0; k < node.childNodes.length; k++)
				{
					if (formDestroy(node.childNodes[k]))
					{
						k--;
					}
				}
				return false;
			}
		}

		var formNode = Ametys.plugins.forms.content.Forms._currentNode;
		formClean(null, null, formNode);

		formDestroy(formNode);
	},
	
	/**
	 * Is the node correct ?
	 * @param {HTMLElement} node the node to check
	 * @return true if the node is correct, false otherwise
	 */
	_isNodeOk: function(node)
	{
		return (/^div$/i.test(node.tagName)
				&& node.getAttribute("form") == "form") 
			|| /^label$/i.test(node.tagName) 
			|| /^legend$/i.test(node.tagName)
			|| this._isNodePartiallyOk(node);
	},

	/**
	 * Is the node partially correct ?
	 * @param {HTMLElement} node the node to check
	 * @return true if the node is partially correct, false otherwise
	 */
	_isNodePartiallyOk: function(node)
	{
		return /^fieldset$/i.test(node.tagName)
			|| (/^img$/i.test(node.tagName) 
				&& node.getAttribute("marker") == "marker" 
				&& node.getAttribute("form") == "form");
	},
	
	/**
	 * Set the given layout on the selected node
	 * @param {String} layoutValue the value of the layout to set
	 * @param {String} cssClass the CSS class of the layout
	 * @return true if the layout was changed, false otherwise
	 */
	_setLayoutOnNode: function(layoutValue, cssClass)
	{
		if (Ametys.plugins.forms.content.Forms._currentNode != null)
		{
			if (Ametys.plugins.forms.content.Forms._currentNode.getAttribute("layout") != layoutValue)
			{
				Ametys.plugins.forms.content.Forms._currentNode.setAttribute("layout", layoutValue);
				
				for (var i=0; i < this._handledLayoutClass.length; i++)
	            {
	                // remove old css class
	                tinyMCE.activeEditor.dom.removeClass(Ametys.plugins.forms.content.Forms._currentNode, this._handledLayoutClass[i]);
	            }
	            
	            // set new css class
	            tinyMCE.activeEditor.dom.addClass(Ametys.plugins.forms.content.Forms._currentNode, cssClass);
	            
				return true;
			}
		}
		
		return false;
	},

	/**
	 * Go to the first leaf child node
	 * @param {HTMLElement} element the root element
	 * @return the first leaf child node or the given node
	 */
	_goToFirst: function(element)
	{
		if (element == null)
		{
			return null;
		}
		else if (element.childNodes.length == 0)
		{
			return element;
		}
		else
		{
			return this._goToFirst(element.childNodes[0]);
		}
	},

	/**
	 * Go to the last leaf child node
	 * @param {HTMLElement} element the root element
	 * @return the last leaf child node or the given node
	 */
	_goToLast: function(element)
	{
		if (element == null)
		{
			return null;
		}
		else if (element.childNodes.length == 0)
		{
			return element;
		}
		else
		{
			return this._goToLast(element.childNodes[element.childNodes.length - 1]);
		}
	},
	
	/**
	 * Retrieve the previous node
	 * @param {HTMLElement} element the root element
	 * @return the previous node or the given node
	 */
	_findPrevious: function(element)
	{
		var elt;
		if (element == null || (/^div$/i.test(element.tagName) && element.getAttribute("form") == "form"))
		{
			elt = null;
		}
		else if (element.previousSibling != null)
		{
			elt = this._goToLast(element.previousSibling);
		}
		else
		{
			elt = element.parentNode;
		}

		if (elt == null || this._isNodePartiallyOk(elt))
		{
			return elt;
		}
		else
		{
			return this._findPrevious(elt);
		}
	},
	
	/**
	 * Retrieve the next node
	 * @param {HTMLElement} element the root element
	 * @return the next node or the given node
	 */
	_findNext: function(element)
	{
        // FIXME replace tinymce.activeEditor by controller.getCurrentField().getEditor()
		var elt;
		if (element == null || Ametys.plugins.forms.content.Forms._getForm(tinymce.activeEditor, element) == null)
		{
			elt = null;
		}
		else if (element.nextSibling != null)
		{
			elt = this._goToFirst(element.nextSibling);
		}
		else
		{
			elt = element.parentNode;
		}

		if (elt == null || this._isNodePartiallyOk(elt))
		{
			return elt;
		}
		else
		{
			return this._findNext(elt);
		}
	}
});