/*
 *  Copyright 2010 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.
 */

/**
 * Simple text layout
 */
Ext.define('Ametys.plugins.forms.content.layout.SimpleText', {
	
	singleton: true,
	
	/**
	 * Unlock the simple text layout : label, linefeed, field
 	 * @param {Ametys.ribbon.element.ui.ButtonController} controller the controller calling this function
	 */
	act: function(controller)
	{
		if (!Ametys.plugins.forms.content.Layout._setLayoutOnNode(controller.getInitialConfig('value'), controller.getInitialConfig('css-class')))
		{
			return;
		}
		
		// FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
		tinyMCE.activeEditor.execCommand('mceBeginUndoLevel');
		
		var me = this;
		
		// first cleanup
		Ametys.plugins.forms.content.Layout._cleanLayout();
		
		// then rebuild
		function macrorefactor(parentNode)
		{
			var br = tinyMCE.activeEditor.dom.doc.createElement("br");
			br.setAttribute("class", "_mce_marker");
			parentNode.appendChild(br)

			while (true)
			{
				var element = parentNode.childNodes[0];
		
				if (/^br$/i.test(element.tagName) && element.className == "_mce_marker")
				{
					// the first element is a refactored one: end of the loop
					element.parentNode.removeChild(element);
					break;
				}
				// refactor
				else if (/^label$/i.test(element.tagName))
				{
					// does not handle now, but ensure it will be
					var id = element.htmlFor;
					var input = null;
					if (id != null)
					{
						input = tinyMCE.activeEditor.dom.get(id);
						if (input != null)
						{
							var inputform = Ametys.plugins.forms.content.Forms._getForm(controller.getCurrentField().getEditor(), input);
							var labelform = Ametys.plugins.forms.content.Forms._getForm(controller.getCurrentField().getEditor(), element);
							
							if (inputform != labelform)
							{
								input = null;
							}
							else
							{
								// move label after input
								input.parentNode.appendChild(element);
							}
						}
					}
		
					if (input == null)
					{
						element.parentNode.removeChild(element);
					}
				}
				else if (/^fieldset$/i.test(element.tagName))
				{
					element.parentNode.appendChild(element);
					macrorefactor(element);
				}
				else if (/^legend$/i.test(element.tagName))
				{
					element.parentNode.appendChild(element);
				}
				else if (/^img$/i.test(element.tagName))
				{
					var label = Ametys.plugins.forms.content.Components._getLabel(element);
					me._refactor(label, element);
				}
				else
				{
					Ametys.Msg.show({
						title: "{{i18n PLUGINS_FORMS_FORMS_EDITOR_LAYOUT_GALLERY_SIMPLETEXT_ERROR_TITLE}}",
						msg: "{{i18n PLUGINS_FORMS_FORMS_EDITOR_LAYOUT_GALLERY_SIMPLETEXT_ERROR_DESCRIPTION}} <br />" 
							   + "{{i18n PLUGINS_FORMS_FORMS_EDITOR_LAYOUT_GALLERY_SIMPLETEXT_ERROR_DETAILS}}<" + element.tagName + ">",
					    buttons: Ext.Msg.OK,
						icon: Ext.MessageBox.ERROR
					});
		
					throw "Unsupported element " + element.tagName + " during refactor"
				}
			}
		}
		
		macrorefactor(Ametys.plugins.forms.content.Forms._currentNode);
		
		if (Ametys.plugins.forms.content.Forms._currentNode.childNodes.length == 0)
		{
			this.create();
		}
		
		// FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
		tinyMCE.activeEditor.execCommand('mceEndUndoLevel');

		tinyMCE.activeEditor.focus();
	},
	
	/**
	 * Create the simple text layout
	 */
	create: function()
	{
		Ametys.plugins.forms.content.Forms._currentNode.innerHTML = '<p><br _moz_dirty=""/></p>';
	},
	
	/**
	 * Insert an input text in the layout
	 * @param {String} name the name of the input
	 * @param {String} id the id of the input
	 * @param {String} inputHTML the html for the input
	 * @param {String} labelHTML the html for the label
	 * @param {String} more additional html
	 */
	insertInputText: function(name, id, inputHTML, labelHTML, more)
	{
	    // FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
		tinyMCE.activeEditor.execCommand('mceBeginUndoLevel');
		
		// is there an empty place to remove
		function isGood(node)
		{
			return /^(h1|h2|h3|h4|h5|h6|p|div)$/i.test(node.tagName);
		}
		var e = tinyMCE.activeEditor.selection.getNode();
        if (/^div$/i.test(e.tagName) && e.getAttribute("form") == "form")
        {
            e = e.firstChild;
            tinyMCE.activeEditor.selection.select(e);
        }

		while (!isGood(e) 
				&& !(/^div$/i.test(e.tagName) && e.getAttribute("form") == "form"))
		{
			e = e.parentNode;
		}
		
        var willRemove = null;
		if (isGood(e))
		{
			if (e.childNodes.length == 0 
					|| (e.childNodes.length == 1 && /^br$/i.test(e.childNodes[0].tagName)))
			{
                willRemove = e;
				e = e.parentNode;
			}
		}
		
		var html = this._factor(labelHTML, inputHTML, more);
		
        tinyMCE.insertHTMLAtRoot(html, 'after');
		
        if (willRemove)
        {
            e.removeChild(willRemove);
        }
		
		tinyMCE.activeEditor.execCommand('mceEndUndoLevel');
		
		if (id != null)
		{
			tinyMCE.activeEditor.execCommand('mceSelectNode', false, tinyMCE.activeEditor.dom.doc.getElementById(id));
		}
	},
	
	/**
	 * Insert an text area in the layout
	 * @param {String} name the name of the input
	 * @param {String} id the id of the input
	 * @param {String} inputHTML the html for the input
	 * @param {String} labelHTML the html for the label
	 * @param {String} more additional html
	 */
	insertTextarea: function (name, id, inputHTML, labelHTML, more)
	{
		this.insertInputText(name, id, inputHTML, labelHTML, more);
	},
	
	/**
	 * Insert a select input in the layout
	 * @param {String} name the name of the input
	 * @param {String} id the id of the input
	 * @param {String} inputHTML the html for the input
	 * @param {String} labelHTML the html for the label
	 * @param {String} more additional html
	 */
	insertSelect: function (name, id, inputHTML, labelHTML, more)
	{
		this.insertInputText(name, id, inputHTML, labelHTML, more);
	},
	
	/**
	 * Insert a captcha in the layout
	 * @param {String} name the name of the input
	 * @param {String} id the id of the input
	 * @param {String} inputHTML the html for the input
	 * @param {String} labelHTML the html for the label
	 * @param {String} more additional html
	 */
	insertCaptcha: function (name, id, inputHTML, labelHTML, more)
	{
		this.insertInputText(name, id, inputHTML, labelHTML, more);
	},
	
	/**
	 * Insert a checkbox in the layout
	 * @param {String} name the name of the input
	 * @param {String} id the id of the input
	 * @param {String} inputHTML the html for the input
	 * @param {String} labelHTML the html for the label
	 * @param {String} more additional html
	 */
	insertInputCheckbox: function (name, id, inputHTML, labelHTML, more)
	{
		this.insertInputText(name, id, inputHTML, labelHTML, more);
	},
	
	/**
	 * Insert an input radio in the layout
	 * @param {String} name the name of the input
	 * @param {String} id the id of the input
	 * @param {String} inputHTML the html for the input
	 * @param {String} labelHTML the html for the label
	 * @param {String} more additional html
	 */
	insertInputRadio: function (name, id, inputHTML, labelHTML, more)
	{
		this.insertInputText(name, id, inputHTML, labelHTML, more);
	},
	
	/**
	 * Insert a password input in the layout
	 * @param {String} name the name of the input
	 * @param {String} id the id of the input
	 * @param {String} inputHTML the html for the input
	 * @param {String} labelHTML the html for the label
	 * @param {String} more additional html
	 */
	insertInputPassword: function (name, id, inputHTML, labelHTML, more)
	{
		this.insertInputText(name, id, inputHTML, labelHTML, more);
	},
	
    /**
     * Insert a cost field in the layout
     * @param {String} name the name of the input
     * @param {String} id the id of the input
     * @param {String} inputHTML the html for the input
     * @param {String} labelHTML the html for the label
     * @param {String} more additional html
     */
    insertInputCost: function (name, id, inputHTML, labelHTML, more)
    {
        this.insertInputText(name, id, inputHTML, labelHTML, more);
    },
    
	/**
	 * Insert a file in the layout
	 * @param {String} name the name of the input
	 * @param {String} id the id of the input
	 * @param {String} inputHTML the html for the input
	 * @param {String} labelHTML the html for the label
	 * @param {String} more additional html
	 */
	insertInputFile: function (name, id, inputHTML, labelHTML, more)
	{
		this.insertInputText(name, id, inputHTML, labelHTML, more);
	},
	
	/**
	 * Insert an hidden input in the layout
	 * @param {String} name the name of the input
	 * @param {String} id the id of the input
	 * @param {String} inputHTML the html for the input
	 */
	insertInputHidden: function(name, id, inputHTML)
	{
	    // FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
		tinyMCE.activeEditor.execCommand('mceBeginUndoLevel');
		tinyMCE.activeEditor.execCommand('mceInsertContent', false, inputHTML);
		tinyMCE.activeEditor.execCommand('mceEndUndoLevel');
		tinyMCE.activeEditor.execCommand('mceSelectNode', false, tinyMCE.activeEditor.dom.doc.getElementById(id));
	},
	
	/**
	 * Insert a fieldset in the layout
	 * @param {String} id the id of the input
	 * @param {String} inputHTML the html for the input
	 */
	insertFieldset: function(id, inputHTML)
	{
		this.insertInputText(null, id, inputHTML, null);
		// FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
		var fieldset = tinyMCE.activeEditor.dom.doc.getElementById(id);
		tinyMCE.activeEditor.execCommand('mceSelectNode', false, fieldset.childNodes[fieldset.childNodes.length - 1]);
	},
	
	/**
	 * Insert a submit input in the layout
	 * @param {String} name the name of the input
	 * @param {String} id the id of the input
	 * @param {String} inputHTML the html for the input
	 */
	insertInputSubmit: function(name, id, inputHTML)
	{
		this.insertInputText(name, id, inputHTML, null, ' style="text-align: center"');
	},
	
	/**
	 * Insert a submit input in the layout
	 * @param {String} name the name of the input
	 * @param {String} id the id of the input
	 * @param {String} inputHTML the html for the input
	 */
	insertInputReset: function(name, id, inputHTML) 
	{
		this.insertInputSubmit(name, id, inputHTML);
	},
	
	/**
	 * Move the selected input up
	 * @param {HTMLElement} input the input to move
	 */
	moveUp: function(input)
	{
		var e = input;
		
		function isGood(node)
		{
			return /^(h1|h2|h3|h4|h5|h6|p|div)$/i.test(node.tagName);
		}
		
		while (!isGood(e) 
				&& !(/^div$/i.test(e.tagName) && e.getAttribute("form") == "form"))
		{
			e = e.parentNode;
		}
		
		if (isGood(e))
		{
			var p = e.previousSibling;
			if (p != null && /^legend$/i.test(p.tagName))
			{
				p = p.previousSibling;
			}
			
			if (p == null)
			{
				if (/^fieldset$/i.test(e.parentNode.tagName))
				{
					p = e.parentNode;
				}
			}
			else if (/^fieldset$/i.test(p.tagName))
			{
				p.appendChild(e);
				return;
			}
			
			if (p != null)
			{
				p.parentNode.insertBefore(e, p);
			}
		}
	},
	
	/**
	 * Move the selected input down
	 * @param {HTMLElement} input the input to move
	 */
	moveDown: function(input)
	{
		var e = input;
		
		function isGood(node)
		{
			return /^(h1|h2|h3|h4|h5|h6|p|div)$/i.test(node.tagName);
		}
		
		while (!isGood(e) 
				&& !(/^div$/i.test(e.tagName) && e.getAttribute("form") == "form"))
		{
			e = e.parentNode;
		}
		
		if (isGood(e))
		{
			var p = e.nextSibling;
			if (p != null && /^legend$/i.test(p.tagName))
			{
				p = p.nextSibling;
			}
			
			if (p == null)
			{
				if (/^fieldset$/i.test(e.parentNode.tagName))
				{
					p = e.parentNode;
				}
			}
			else if (/^fieldset$/i.test(p.tagName))
			{
				p.insertBefore(e, p.childNodes.length > 0 ? p.childNodes[0] : null);
				return;
			}
			
			if (p != null)
			{
				p.parentNode.insertBefore(e, p.nextSibling);
			}
		}
	},
	
	/**
	 * Remove the selected input
	 * @param {HTMLElement} input the input
	 */
	remove: function(input)
	{
		if (Ametys.plugins.forms.content.Components._currentMovableNode != null)
		{
		    // FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
			tinyMCE.activeEditor.execCommand('mceBeginUndoLevel');
			
			function isGood(node)
			{
				return /^(h1|h2|h3|h4|h5|h6|p|div)$/i.test(node.tagName);
			}
			
			var e = Ametys.plugins.forms.content.Components._currentMovableNode;
			while (!isGood(e) && !(/^div$/i.test(e.tagName) && e.getAttribute("form") == "form"))
			{
				e = e.parentNode;
			}
			
			if (isGood(e))
			{
				var a = e.previousSibling || e.nextSibling || e.parentNode;
				e.parentNode.removeChild(e);
				
				if (Ametys.plugins.forms.content.Forms._currentNode.childNodes.length == 0)
				{
					this.create();
					a = Ametys.plugins.forms.content.Forms._currentNode.childNodes[0];
				}
				tinyMCE.activeEditor.execCommand('mceSelectNode', false, a);
			}
		}
	},
	
	/**
	 * Create the html based upon the parameters
	 * @param {String} labelHTML the html for the label
	 * @param {String} inputHTML the html for the input
	 * @param {String} more additional html
	 */
	_factor: function (labelHTML, inputHTML, more)
	{
		return "<p" + (more != null ? more : "") + ">"
		+ (labelHTML != null && labelHTML != "" ? labelHTML + '<br _moz_dirty=""/>' : '')
		+ inputHTML
		+ "</p>";
	},
	
	/**
	 * Insert a p as the last sibling of input
	 * Moves label and input in it (br separated)
	 * @param {String} label the label
	 * @param {HTMLElement} input the input
	 */
	_refactor: function (label, input)
	{
		// create a new paragraph
	    // FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
		var newP = tinyMCE.activeEditor.dom.doc.createElement("p");
		input.parentNode.appendChild(newP);
		if (label)
		{
			newP.appendChild(label);
			var br = tinyMCE.activeEditor.dom.doc.createElement("br");
			br.setAttribute("_moz_dirty", "");
			newP.appendChild(br);
		}
		newP.appendChild(input);
	},
});