/*
 *  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 helper provides methods to wrap selected text in inline editor by a defined tag
 * See #actionDirect and #action methods
 */
Ext.define('Ametys.cms.editor.InsertElementHelper', {
	singleton: true,
	
	/**
	 * @property {Ametys.window.DialogBox} _box The insert element popup window containing a form.
	 * @private
	 */
	
	/**
	 * Listener for accessibility basic editor buttons
	 * @param {Ametys.cms.editor.EditorButtonController} controller The controller
	 * @param {Ametys.cms.form.widget.RichText} field The current field. Can be null
	 * @param {HTMLElement} node The editor current node
	 * @param {String} tagName The tagname to check
	 * @param {String} attributeName The name of an attribute to check. Can be null.
	 * @param {String} description The button description to state on enabled state
	 * @param {String} disabledDescription The button description to state on disabled state
	 */
	listener: function(controller, field, node, tagName, attributeName, description, disabledDescription)
	{
		var off = !node;
		var state = !off;
		var disabled = off;
		
		if (!off)
		{
			var nodesel = field.getEditor().selection.getNode();
			var elt = field.getEditor().dom.getParent(nodesel, tagName);
			var sel = field.getEditor().selection.getContent();
			
			disabled = (/<p[> \/]|<td[> \/]|<th[> \/]|<li[> \/]|<div[> \/]/.test(sel)) || (sel == '' && !elt) || nodesel != null && field.getEditor().dom.hasClass(nodesel, 'mceNonEditable');
			state = elt != null  && elt.nodeName.toLowerCase() == tagName && (!attributeName || elt.getAttribute(attributeName) != null);
		}

		controller.toggle(state);
		controller.setDisabled(off || disabled);
		controller.setAdditionalDescription((off || disabled) ? disabledDescription : '');
	},
	
	/**
	 * Will set/remove a tag in the current richtext
	 * 
	 *     Ametys.cms.editor.InsertElementHelper({ tagName: 'b', attributes: {
	 *     		'class': 'mycss',
	 *          'title': 'my tool tip',
	 *          'marker': 'marker'
	 *     		}
	 *     });
	 *     
	 * Remember that for a tag to be handled with its attributes, you have to register it with the RichTextConfigurationExtensionPoint.
	 * To have the css ok, include it with the RichTextConfigurationExtensionPoint.
	 * Also consider onSetContent and onGetContent of the RichTextConfigurationExtensionPoint to transform a html technical tag (such as span) to a semantic tag when richtext is saved
	 * 
	 * @param {Object} [config] The object to insert
	 * @param {String} [config.tagName=span] The name of the tag to create or update
	 * @param {String} [config.field] The field into which the tag is to be inserted
	 * @param {Object} [config.attributes] Attributes to add. The key is the name of the attribute, and the value if the value of the attributes
	 */
	actionDirect: function(config)
	{
		config = config || {};
		config.tagName = config.tagName || "span";
		config.attributes = config.attributes || {};
		var field = config.field;

		var elt = field.getEditor().selection.getNode();
		elt = field.getEditor().dom.getParent(elt, function(n) {
			if (n.nodeName.toLowerCase() === config.tagName)
			{
				for(attributeName in config.attributes)
				{			
					var attributeValue = config.attributes[attributeName];
					if (n.getAttribute(attributeName) !== attributeValue )
					{
						return false; // TagName: OK, attributeValues : KO
					}						
				}
				return true; // TagName: OK, attributeValues : OK
			}
			else
			{
				return false;	// TagName: KO
			}
		});

		if (!elt)
		{
			// Insert element
			field.getEditor().execCommand('mceBeginUndoLevel');
			var text = field.getEditor().selection.getContent();
			
			var attributePart = '';
			for (var attName in config.attributes)
			{
				attributePart += ' ' + attName + '="' + config.attributes[attName] + '"';
			}
			
			field.getEditor().execCommand('mceInsertContent', false, '<' + config.tagName + attributePart + '>' + text + '</' + config.tagName + '>');
			field.getEditor().execCommand('mceEndUndoLevel');
		}
		else
		{
			// Remove element
			field.getEditor().execCommand('mceBeginUndoLevel');
			tinyMCE.execCommand('mceRemoveNode', false, elt);
			field.getEditor().execCommand('mceEndUndoLevel');
		}
	},
	
	/**
	 * Action listener for accessibility basics extension.
	 * @param {Ametys.cms.form.widget.RichText} field The current field
	 * @param {String} icon The path to the icon of the dialog box
	 * @param {String} title The dialog box title
	 * @param {String} fieldLabel The label of the field
	 * @param {String} defaultValue The default value when creating
	 * @param {String} description The dialog box introduction text
	 * @param {String} postDescription The dialog box end description
	 * @param {String} tagName The name of the tag to create or update
	 * @param {String} attributeName The name of the attribute on the tag that will be filled with the user input
	 * @param {String} [className] A class name to add on the tag
	 */
	action: function(field, icon, title, fieldLabel, defaultValue, description, postDescription, tagName, attributeName, className)
	{
		var node = field.getEditor().dom.getParent(field.getEditor().selection.getNode(), tagName);
		
		var elt = null;
		if (node && node.nodeName.toLowerCase() == tagName)
		{
			elt = node;
		}

		var params = {
			field: field,
			elt: elt,
			icon: icon,
			title: title,
			description: description,
			postDescription: postDescription,
			defaultValue: defaultValue,
			tagName: tagName,
			fieldLabel: fieldLabel,
			attributeName: attributeName,
			className: className
		};
		
		this._act(params);
	},
	
	/**
	 * @private
	 * Open a dialog box with an input. The current selection will be surrounded with a html element with a particular title value
	 * @param {Object} params Configuration parameters. See {@link #method-action}
	 */
	_act: function(params)
	{
		this._box = Ext.create('Ametys.window.DialogBox', {
			title: params.title,
			icon: params.icon,
			
			width: 420,
			
			defaultType: 'component',
			defaults: {
				cls: 'ametys',
				labelAlign: 'right',
				labelSeparator: '',
				labelWidth: 100
			},
			
			items: [{
				xtype: 'component',
				cls: 'a-text',
				html: params.description
			}, {
				xtype: 'textfield',
				fieldLabel: params.fieldLabel,
				name: 'accessibility-title',
				id: 'accessibility-title',
				inputWidth: 240,
				allowBlank: true
			}, {
				xtype: 'component',
				cls: 'a-text',
				html: params.postDescription
			}],
			
			selectDefaultFocus: true,
			defaultFocus: 'accessibility-title',
			closeAction: 'destroy',
			
			referenceHolder: true,
			defaultButton: 'validate',
			
			buttons : [{
				reference: 'validate',
				text: "{{i18n CONTENT_EDITION_ACCESSIBILITY_INSERT_ELEMENT_DIALOG_OK}}",
				handler: Ext.bind(this._ok, this, [params])
			},{
				text: "{{i18n CONTENT_EDITION_ACCESSIBILITY_INSERT_ELEMENT_DIALOG_CANCEL}}",
				handler: Ext.bind(this._cancel, this)
			}]
		});
	

		var field = this._box.down('#accessibility-title');
		field.setValue(params.elt ? params.elt.getAttribute(params.attributeName) : params.defaultValue);

		this._box.show();
	},
	
	/**
	 * This function is called after pressing 'Ok' the button of dialog box #_box
	 * @param {Object} params Configuration parameters. See {@link #method-action}
	 */
	_ok: function(params)
	{
		//tinyMCE.focus();
		var editor = params.field.getEditor();

		var value = this._box.down('#accessibility-title').getValue();
		
		if (value != '' && !params.elt)
		{
			// Insert element
			// FIXME CMS-1026 replace special characters
			editor.execCommand('mceBeginUndoLevel');
			var text = editor.selection.getContent();
			
			var classAttribute = '';
			if (params.className)
			{
				classAttribute += ' class="' + params.className + '"';
			}
			
			editor.execCommand('mceInsertContent', false, '<' +params.tagName + ' ' +params.attributeName + '="' + value + '"' + classAttribute + '>' + text + '</' +params.tagName + '>');
			editor.execCommand('mceEndUndoLevel');
		}
		else if (value != '' && params.elt)
		{
			// Update element
			editor.execCommand('mceBeginUndoLevel');
			editor.dom.setAttrib(params.elt, params.attributeName, value);
			editor.execCommand('mceEndUndoLevel');
		}
		else if (params.elt != null)
		{
			// Remove element
			editor.execCommand('mceBeginUndoLevel');
			tinyMCE.execCommand('mceRemoveNode', false, params.elt);
			editor.execCommand('mceEndUndoLevel');
		}
		
		this._box.close();
		this._box = null;
	},

	/**
	 * This function is called after pressing 'Cancel' the button of dialog box #_box
	 * Closes the dialog box
	 */
	_cancel: function()
	{
		this._box.close();
		this._box = null;
	}

});