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

/**
 * HTML Expert singleton class.
 * @private
 */
Ext.define('Ametys.plugins.cms.editor.HTMLExpert', {
	singleton: true,
	
	/**
	 * @private
	 * @property {Ametys.window.DialogBox} _box The HTML expert edition dialog box containing the script editor.
	 */
	/**
	 * @private
	 * @property {Ametys.form.field.Code} _scriptEditor the script editor which is a {@link Ext.form.field.TextArea} managing a CodeMirror instance.
	 */
	/**
	 * @private
	 * @property {HTMLElement} _currentNode the current HTML expert node to edit.
	 */
	
	/**
	 * Action function for the 'insert html expert' button controller.
	 * @param {Ametys.cms.editor.EditorButtonController} controller The controller calling this function
	 */
	insert: function(controller)
	{
		var id = Ext.id();
		if (controller.getCurrentField())
	    {
		    var editor = controller.getCurrentField().getEditor();
		    editor.focus();
		    editor.execCommand('mceBeginUndoLevel');
		    editor.execCommand('mceInsertContent', false, Ametys.plugins.cms.editor.htmlexpert.HTMLExpertConvertor.createHTML(id, '', null, true));
		    editor.execCommand('mceEndUndoLevel');
		    
		    var elt = editor.dom.doc.getElementById(id);
		    editor.execCommand('mceSelectNode', false, elt);
		    elt.removeAttribute('id');
	    }
	},
	
	/**
	 * Enable/disable controller and set input value according the selected image
	 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller
	 * @param {Ametys.cms.form.widget.RichText} field The current field. Can be null
	 * @param {HTMLElement} node The current selected node. Can be null.
	 */
	selectionListener: function(controller, field, node)
	{
		if (node)
		{
			var value = decodeURIComponent(node.getAttribute('htmlexpert').substring(1));
			controller.setValue(value);
		}
		else
		{
			controller.setValue('');
		}
		
		this._currentNode = node;
	},
	
	/**
	 * Set the HTML expert code when pressing ENTER or ESC key.
	 * @param {Ext.form.field.TextArea} input The textearea
	 * @param {Ext.event.Event} e The event object
	 */
	setHtmlExpertOnSpecialKey: function (input, e)
	{
		if (e.getKey() == e.ENTER) 
		{
			e.preventDefault();
			e.stopPropagation();
			this.setHtmlExpert(input.getValue());
		}	
		else if (e.getKey() == e.ESC) 
		{
			e.preventDefault();
			e.stopPropagation();
			this.setHtmlExpert(this._currentNode != null ? decodeURIComponent(this._currentNode.getAttribute("htmlexpert").substring(1)) : "");
		}
	},
	
	/**
	 * Set the HTML expert code when the input loses the focus
	 * @param {Ext.form.field.TextArea} input The text area
	 */
	setHtmlExpertOnBlur: function (input)
	{
		var htmlExpertNode = this._currentNode;
		var htmlExpertValue = decodeURIComponent(htmlExpertNode.getAttribute("htmlexpert").substring(1));
		var inputValue = input.getValue();
		
		if (htmlExpertValue != inputValue)
		{
			this.setHtmlExpert(inputValue);
		}
	},
	
	/**
	 * Set the HTML expert code
	 * @param {String} value The raw value
	 */
	setHtmlExpert: function (value)
	{
		var htmlexpert = encodeURIComponent(value);
		this._currentNode.setAttribute("htmlexpert", "-" + htmlexpert);
		
		// FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
		tinyMCE.activeEditor.execCommand('mceAddUndoLevel');
	},
	
	/**
	 * Edit the source HTML code
	 * @param {Ametys.ribbon.element.RibbonUIController} controller The UI controller holding this function
	 */
	edit: function (controller)
	{
		this._createBoxIfRequired(controller);
		this._controller = controller;
		
		// Show the box before setting the value because the ribbon has to be blurred in order to update its value 
		this._box.show();

		var htmlexpert = this._currentNode;
		if (htmlexpert)
		{
			var htmlexpert = decodeURIComponent(htmlexpert.getAttribute("htmlexpert").substring(1));

			this._scriptEditor.setValue(htmlexpert);
			this._scriptEditor.focus(1);
		}
	},
	
	/**
	 * Create the {@link #_box} window if needed.
	 * @param {Ametys.ribbon.element.RibbonUIController} controller The UI controller holding the #edit function
	 */
	_createBoxIfRequired: function (controller)
	{
		if (this._box == null)
		{
			var northItems = [{
				xtype: 'component',
				cls: 'hint',
				html: "{{i18n CONTENT_EDITION_HTMLEXPERT_DIALOGBOX_HINT}}"
			}]
			
			this._toolbar = this._getToolbar (controller);
			if (this._toolbar != null)
			{
				northItems.push(this._toolbar);
			}
			
			var ratio = 0.80;
            var width = window.innerWidth * ratio;
			var height = window.innerHeight * ratio;

			this._scriptEditor = Ext.create ("Ametys.form.field.Code", {
				mode: 'htmlmixed',
				scrollable: true,
                flex: 1
			});
			
			this._box = Ext.create('Ametys.window.DialogBox', {
				title: "{{i18n CONTENT_EDITION_HTMLEXPERT_DIALOGBOX_TITLE}}",
				iconCls: 'ametysicon-computing10',
				layout: {
				    type: 'vbox',
				    align: 'stretch'
				},
				border: false,
				scrollable: false,
				cls: 'ametys-dialogbox htmlexpert-dialog',
				
				width: width,
				height: height,
				
				defaultType: 'container',
				defaults: {
					cls: 'ametys'
				},
				
				items: [{
							layout: 'anchor',
							items: northItems
						},
						this._scriptEditor,
						{
							xtype: 'component',
							cls: 'hint',
							html: Ext.String.format("{{i18n CONTENT_EDITION_HTMLEXPERT_DIALOGBOX_HINT2}}", controller.helpURL)
						}
				],
				
				defaultFocus: 'htmlexpert-popup-source',
				closeAction: 'hide',
				
				buttons : [{
					text :"{{i18n CONTENT_EDITION_HTMLEXPERT_DIALOGBOX_OK}}",
					handler : Ext.bind(this._saveHtmlExpert, this)
				}, {
					text :"{{i18n CONTENT_EDITION_HTMLEXPERT_DIALOGBOX_CANCEL}}",
					handler: Ext.bind(function() { this._box.hide(); tinyMCE.activeEditor.focus(); }, this)
				}]
			});
		}
	},
	
	/**
	 * Get the toolbar
	 * @param {Ametys.ribbon.element.RibbonUIController} controller The UI controller holding the #edit function
	 * @return {Object} The toolbar configuration object or null.
	 * @private
	 */
	_getToolbar : function (controller)
	{
		var toolbarBtnCfg = controller.getInitialConfig("toolbar-buttons");
		
		if (toolbarBtnCfg && toolbarBtnCfg.length > 0)
		{
			var toolbarBtn = [];
			
			for (var i = 0; i < toolbarBtnCfg.length ; i++)
			{
				var btn = Ametys.createObjectByName(toolbarBtnCfg[i].classname, null, Ext.apply(toolbarBtnCfg[i].config, {id: toolbarBtnCfg[i].id, pluginName: toolbarBtnCfg[i].pluginName}));
				toolbarBtn.push(btn.createUI());
			}
			
			return {
				xtype: 'toolbar',
				items: toolbarBtn
			}
		}
		else
		{
			return null;
		}
	},
	
	/**
	 * This function is called when pressing 'ok' button of dialog box #_box
	 * Saves the HTML expert code
	 * @private
	 */
	_saveHtmlExpert: function()
	{
		var htmlexpertNode = this._currentNode;
		if (htmlexpertNode)
		{
			var value = this._scriptEditor.getValue();
			
			var htmlexpert = encodeURIComponent(value);
			htmlexpertNode.setAttribute("htmlexpert", "-" + htmlexpert);
			
			// Set controller field value
			if (this._controller)
			{
				this._controller.setValue(value)
			}

			this._box.hide(); 
			// FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
			tinyMCE.activeEditor.focus();
			
			// Trigger cache update
			tinyMCE.activeEditor.execCommand('mceAddUndoLevel');
		}
	},
	
	// FIXME refactor? HTMLExpert button might have a similar method
	insertAtCaret: function(text)
	{
		this._scriptEditor.getCM().replaceSelection(text);
	}
});