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

/**
 * Undo editor action.
 * @private
 */
Ext.define('Ametys.plugins.cms.editor.basicactions.Undo', {
	singleton: true,
	
	/**
	 * Execute the 'undo' action
	 * @param {Ametys.cms.editor.EditorButtonController} controller The controller calling the function
	 */
	apply: function(controller)
	{
		var field = controller.getCurrentField();
		field.getEditor().execCommand('Undo');
		field.getEditor().focus();
	},
	
	/**
	 * Enable/disable and toggle/untoggle controller according the current selection
	 * @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 current selected node. Can be null.
	 */
	selectionListener: function (controller, field, node)
	{
		if (!node || !field || !field.getEditor().undoManager.hasUndo())
		{
			controller.setDisabled(true);
		}
	}
});

/**
 * Redo editor action.
 * @private
 */
Ext.define('Ametys.plugins.cms.editor.basicactions.Redo', {
	singleton: true,
	
	/**
	 * Execute the 'redo' action
	 * @param {Ametys.cms.editor.EditorButtonController} controller The controller calling the function
	 */
	apply: function(controller)
	{
		var field = controller.getCurrentField();
		field.getEditor().execCommand('Redo');
		field.getEditor().focus();
	},
	
	/**
	 * Enable/disable and toggle/untoggle controller according the current selection
	 * @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 current selected node. Can be null.
	 */
	selectionListener: function (controller, field, node)
	{
		if (!node || !field || !field.getEditor().undoManager.hasRedo())
		{
			controller.setDisabled(true);
		}
	}
});

/**
 * Cut editor action.
 * @private
 */
Ext.define('Ametys.plugins.cms.editor.basicactions.Cut', {
	singleton: true,
	
	/**
	 * Execute the 'cut' action
	 * @param {Ametys.cms.editor.EditorButtonController} controller The controller calling the function
	 */
	apply: function(controller)
	{
		var editor = controller.getCurrentField().getEditor();

		editor.focus();
		try
		{
			if (Ext.isGecko) 
			{
				netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
				editor.getDoc().execCommand("cut", false, null);
			}
			else
			{
				editor.getDoc().execCommand("cut", false, null);
			}
		}
		catch (e)
		{
			Ametys.log.ErrorDialog.display({
				title: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_TEXT}}",
				text: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_DESC_CTRLX}}",
				details: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_DESC_EXPL}}" + e,
				category: Ext.getClassName(this)
			});
		}
	},
	
	/**
	 * Enable/disable and toggle/untoggle controller according the current selection
	 * @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 current selected node. Can be null.
	 */
	selectionListener: function (controller, field, node)
	{
		if (!node || !field || field.getEditor().selection.isCollapsed())
		{
			controller.setDisabled(true);
		}
	}
});

/**
 * Copy editor action.
 * @private
 */
Ext.define('Ametys.plugins.cms.editor.basicactions.Copy', {
	singleton: true,
	
	/**
	 * Execute the 'copy' action
	 * @param {Ametys.cms.editor.EditorButtonController} controller The controller calling the function
	 */
	apply: function(controller)
	{
		var editor = controller.getCurrentField().getEditor();
		editor.focus();
		try
		{
			if (Ext.isGecko) 
			{
				netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
				editor.getDoc().execCommand("copy", false, null);
			}
			else
			{
				editor.getDoc().execCommand("copy", false, null);
			}
		}
		catch (e)
		{
			Ametys.log.ErrorDialog.display({
				title: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_TEXT}}",
				text: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_DESC_CTRLC}}",
				details: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_DESC_EXPL}}" + e,
				category: Ext.getClassName(this)
			});
		}
	},
	
	/**
	 * Enable/disable and toggle/untoggle controller according the current selection
	 * @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 current selected node. Can be null.
	 */
	selectionListener: function (controller, field, node)
	{
		if (!node || !field || field.getEditor().selection.isCollapsed())
		{
			controller.setDisabled(true);
		}
	}
});

/**
 * Paste editor action.
 * @private
 */
Ext.define('Ametys.plugins.cms.editor.basicactions.Paste', {
	singleton: true,
	
	/**
	 * Execute the 'paste' action
	 * @param {Ametys.cms.editor.EditorButtonController} controller The controller calling the function
	 */
	apply: function(controller)
	{
		var editor = controller.getCurrentField().getEditor();
		editor.focus();
		try
		{
			if (Ext.isGecko) 
			{
				netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
				editor.getDoc().execCommand("paste", false, null);
			}
			else
			{
				if (!editor.getDoc().execCommand("paste", false, null) && !Ext.isIE)
                {
                    throw "Paste failed";
                }
			}
		}
		catch (e)
		{
			Ametys.log.ErrorDialog.display({
				title: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_TEXT}}",
				text: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_DESC_CTRLV}}",
				details: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_DESC_EXPL}}" + e,
				category: Ext.getClassName(this)
			});
		}
	}
});

/**
 * Text Paste editor action (without tags).
 * @private
 */
Ext.define('Ametys.plugins.cms.editor.basicactions.TxtPaste', {
	singleton: true,

	/**
	 * @private
	 * @property {Object} _hiddenTextAreaStyle The css style to hide the textare which will received the txt
	 */
	_hiddenTextAreaStyle : {
		position : 'absolute',
		left : -10000,
		top : 0,
		width : 1,
		height : 1,
		overflow : 'hidden',
		display: 'none'
	},
	
	/**
	 * Execute the 'paste as text' action
	 * @param {Ametys.cms.editor.EditorButtonController} controller The controller calling the function
	 */
	apply: function(controller)
	{
		var pastetext = '';
		var field = controller.getCurrentField();
		
		if (Ext.isGecko) 
		{
			try
			{
				netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
			}
			catch (e)
			{
				this._manualTxtPaste(field);
				return;
			}
			
			var clip = Components.classes["@mozilla.org/widget/clipboard;1"].getService(Components.interfaces.nsIClipboard);
			if (!clip) return false;
		 
			var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
			if (!trans) return false;
			trans.addDataFlavor("text/unicode");
		 
			clip.getData(trans, clip.kGlobalClipboard);
		 
			var str = {};
			var strLength = {};
			
			trans.getTransferData("text/unicode", str, strLength);
			if (str) str = str.value.QueryInterface(Components.interfaces.nsISupportsString);
			if (str) pastetext = str.data.substring(0, strLength.value / 2);

		}
		else if (Ext.isIE)
		{
			pastetext = window.clipboardData.getData("Text");
		}	
		else
		{
			// paste in a textarea input (this is the secret to remove tags properly)
			// create or get a textarea input
			var textarea = Ext.getDom('txtpaste');
			if (!textarea)
			{
				Ext.getBody().createChild({tag: 'textarea', id:'txtpaste', style: this._hiddenTextAreaStyle});
				textarea = Ext.getDom('txtpaste');
			}
			
			// initialize the textarea input
			textarea.value = '';
			textarea.style.display = '';
			textarea.focus();
			textarea.select();
			
			try
			{
				textarea.createTextRange().execCommand('paste', false, null);
			}
			catch (e)
			{
				this._manualTxtPaste(field);
				return;
			}
			finally
			{
				// recycle the textarea
				textarea.style.display = 'none';
			}
			
			pastetext = textarea.value;
		}

		this._finishTxtPaste(field, pastetext);
	},
	
	/**
	 * @private
	 * This method is called after paste is done (normal or text paste)
	 * @param {Ametys.cms.form.widget.RichText} field The field to paste into
	 * @param {String} txt The text pasted
	 */
	_finishTxtPaste: function (field, txt)
	{
		// paste in the html editor
		field.getEditor().execCommand('mceInsertContent', false, "<p>" + txt.replace(/\n/g, "</p><p>") + "</p>");

		field.getEditor().focus();
	},

	/**
	 * When an automatic paste failed due to security options, this method does show a dialog box to allow a manual paste
	 * @param {Ametys.cms.form.widget.RichText} field The field to paste into
	 * @private
	 */
	_manualTxtPaste: function(field)
	{
		if (!this._box)
		{
			this._box = Ext.create('Ametys.window.DialogBox', {
				title: "{{i18n CONTENT_EDITION_BASICACTION_PASTE_WITHOUT_STYLE}}",
				icon: Ametys.getPluginResourcesPrefix('cms') + "/img/content/edition/clipboard/paste_txt_16.png",
				
				layout: 'form',
				width: 300,
				height: 180,
				scrollable: true,
				
				defaults: {
					cls: 'ametys',
					labelAlign: '130',
					labelSeparator: '',
					labelWidth: 130
				},
				
				items: [{
					xtype: 'textarea',
					id: 'manualtxtpaste',
					listeners: {
						'blur': function(textarea) { textarea.focus(true, 100); }
					},
					style: this._hiddenTextAreaStyle
				}, {
					xtype: 'component',
					cls: 'hint',
					html: "{{i18n CONTENT_EDITION_BASICACTION_INFO_COPY}}"
				}],
				
				defaultFocus: 'manualtxtpaste',
				closeAction: 'hide',
				
				buttons: [{
					text: "{{i18n CONTENT_EDITION_BASICACTION_CANCEL}}",
					handler: Ext.bind(function() {this._box.hide();}, this)
				}]
			});
		}
		
		this._box.show();

		var textarea = this._box.down('textarea');
		textarea.reset();
		textarea.getEl().dom.style.display = '';
		textarea.getEl().dom.style.top = '0px';
		textarea.getEl().dom.style.left = '-10000px';
		textarea.getEl().dom.style.position = 'absolute';
		textarea.focus(true);
		
		this._checkTxtArea(field);
	},

	/**
	 * @private
	 * This method is watching the textera to detect the paste event
	 * @param {Ametys.cms.form.widget.RichText} field The field to paste into
	 */
	_checkTxtArea: function(field)
	{
		if (this._box.isHidden())
		{
			return;
		}
		
		var value = this._box.down('textarea').getValue();
		if (value)
		{
			this._box.hide();
			this._finishTxtPaste(field, value); 
		}
		else
		{
			Ext.defer(this._checkTxtArea, 10, this, [field]);
		}
	}
});

/**
 * Singleton class.
 * Apply a style to a node.
 * @private
 */
Ext.define('Ametys.plugins.cms.editor.basicactions.ApplyStyle', {
	singleton: true,
	
    /**
     * Apply style
     * @param {Ametys.cms.editor.EditorButtonController} controller The controller calling the function
     * @param {Boolean} state the press-state of the controller
     */
    apply: function (controller, state)
    {
        if (state)
        {
            var editor = controller.getCurrentField().getEditor();
            var node = editor.dom.getParent(editor.selection.getNode(), controller.getInitialConfig('tagname'));
            if (node != null)
            {
                var classNames = controller.getInitialConfig('css-class');
                this.applyStyle(node, editor, classNames);
            }
		}
	},
    /**
     * Apply a style directly
     * @param {HTMLElement} node The current selected node.
     * @param {Object} editor tinyMCE editor
     * @param {String} classNames class to add
     */
    applyStyle: function(node, editor, classNames)
    {
        editor.focus();
        if (node != null)
        {
            var element = Ext.get(node);
            var tag = Ametys.form.widget.RichText.RichTextConfiguration.getTag(element.dom.tagName.toLowerCase(), editor.getParam('category'))
            if (tag && tag.getAttribute("class"))
            {
                var protectedClasses = tag.getAttribute("class").technicalValues;
                if (protectedClasses != null)
                {
                    for (var i = 0; i < protectedClasses.length; i++)
                    {
                        if (editor.dom.hasClass(node, protectedClasses[i]))
                        {
                            classNames += " " + protectedClasses[i];
                        }
                    }
                }
            }

            node.setAttribute("class", classNames);
            editor.execCommand('mceAddUndoLevel');
        }
    },
	
	/**
	 * Enable/disable and toggle/untoggle controller according the current selection
	 * @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 current selected node. Can be null.
	 */
	styleControllerListener: function (controller, field, node)
	{
		var node = (field != null && node != null && field.getEditor() != null && field.getEditor().dom != null) ? field.getEditor().dom.getParent(node, controller.getInitialConfig('tagname')) : null;
		var editor = (field != null && node != null && field.getEditor() != null && field.getEditor().dom != null) ? field.getEditor() : null;
        var toggleFunction = Ext.bind(controller.toggle, controller);
        var enableFunction = function(enable) {if (enable) {controller.enable()} else {controller.disable()}};
        var style = controller.getInitialConfig('css-class');
        this.styleListener(node, editor, toggleFunction, enableFunction, style);
	},
    /**
     * Enable/disable and toggle/untoggle controller according the current selection
     * @param {HTMLElement} node The current selected node.
     * @param {Object} editor tinyMCE editor
     * @param {Function} toggleFunction function that will be called with true/false to select/unselect button
     * @param {Function} enableFunction function that will be called with true/false to enable/disable button
     * @param {String} style button style
     */
    styleListener: function(node, editor, toggleFunction, enableFunction, style)
    {
        if (node != null)
        {
            toggleFunction(editor.dom.hasClass(node, style));
            enableFunction(true);
        }
        else
        {
            toggleFunction(false);
            enableFunction(false);
        }
    }
	
});