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

/**
 * Class to handle image insertion in inline editor
 * @private
 */
Ext.define('Ametys.plugins.cms.editor.Images', {
	singleton: true,
	
	/**
	 * Insert a local image
	 * @param {Ametys.cms.editor.EditorButtonController} controller The controller calling this function
	 */
	insertLocalImage : function (controller)
	{
        var message = Ametys.message.MessageBus.getCurrentSelectionMessage();
        var contentTarget = message.getTarget(function(target) { return target.getId() == "content" });
        
        if (contentTarget != null)
        {
            var formTarget = contentTarget.getSubtarget(function(target) { return target.getId() == "form" });
            if (formTarget != null)
            {
                var form = formTarget.getParameters()['object'];
                
                var fieldTarget = formTarget.getSubtarget(function(target) { return target.getId() == "field" });
                if (fieldTarget != null)
                {
                    var field = form.findField(fieldTarget.getParameters()['name']);
            		this._insertLocalImage(field.getEditor(), controller.getInitialConfig('icon-glyph'));
                }
            }
        }
	},
	
    /**
     * Insert a local image
     * @param {tinymce.Editor} editor The richtext editor
     * @param {String} glyph The glyph to use
     * @param {String} [uploadUrl] The URL to use for upload. Can be null to use the default one.
     * @param {Object} [cropUrl] The URL object (containing plugin & url) to use for cropping. Can be null to use the default one.
     * @private
     */
    _insertLocalImage : function (editor, glyph, uploadUrl, cropUrl)
    {
        Ametys.helper.FileUpload.open({
          iconCls: glyph,
          title: "{{i18n PLUGINS_CMS_EDITOR_IMAGES_INSERT_IMAGE}}",
          helpmessage: "{{i18n PLUGINS_CMS_EDITOR_IMAGES_BROWSE_DESC}}",
          callback: afterUpload,
          autovalidate: autoValidate,
          filter: Ametys.helper.FileUpload.IMAGE_FILTER,
          uploadUrl: uploadUrl
        });
        
        var afterCropFn = Ext.bind(this._insertLocalImageCb, this, [editor], 0);
        
        function autoValidate(filename)
        {
            return /\.jpg|\.jpeg|\.gif|\.png$/i.test(filename); 
        }
        function afterUpload(id, filename, fileSize, viewHref, downloadHref)
        {
            if (autoValidate(filename))
            {
                Ext.create('Ametys.helper.CropDialog', {
                    imageId: id,
                    imageName: filename,
                    imageSize: fileSize,
                    imageViewHref: viewHref,
                    imageDownloadHref: downloadHref,
                    cropUrl: cropUrl,
                    afterCropFn: afterCropFn
                }).show();
            }
            else
            {
                afterCropFn(id, filename, fileSize, viewHref, downloadHref);
            }            
        }
    },
    
	/**
	 * Callback function called after uploading local image
     * @param {tinymce.Editor} editor The richtext editor
	 * @param {String} id Id of the uploading image
	 * @param {String} filename File name of the uploading image
	 * @param {String} size File length of the uploading image
	 * @param {String} viewHref The URL to view the uploaded image
	 * @param {String} downloadHref The URL to download the uploaded image
	 * @private
	 */
	_insertLocalImageCb: function(editor, id, filename, size, viewHref, downloadHref)
	{
		editor.focus();
		if (id != null)
        {
            var data = {
                "id": Ext.id(),
                "class": tinyMCE.activeEditor.schema.elements.img ? tinyMCE.activeEditor.schema.elements.img.attributes.class.defaultValue : "",
                "src": viewHref,
                "_mce_ribbon_select": "1",
                "data-ametys-type": "temp",
                "data-ametys-temp-src": id
            };
            editor.selection.setContent(editor.dom.createHTML('img', data));
            
            var maxWidth = editor.getBody().offsetWidth * 0.9;
			
			var node = editor.dom.get(data.id);
            var editor = tinyMCE.activeEditor;
            node.onload = function() {
                node.onload = null;
                
                var imgWidth = node.offsetWidth; 
                var imgHeight = node.offsetHeight;
                 
                if (imgWidth > maxWidth)
                {
                    node.setAttribute("width", maxWidth);
                    node.setAttribute("height", Math.round(100 * imgHeight * maxWidth / imgWidth) / 100.0);
                }
                editor.selection.select(node);
            }
			node.removeAttribute("id");
		}
	},
	
	/**
	 * Insert a resource image (from the resources explorer)
	 * @param controller {Ametys.cms.editor.EditorButtonController} controller The controller calling this function
	 */
	insertResourceImage : function (controller)
	{
        var iconCls = [controller.getInitialConfig('icon-glyph'), controller.getInitialConfig('icon-decorator')].join(' ');
        this._insertResourceImage(iconCls, true);
	},
    /**
     * Insert a resource image (from the resources explorer)
     * @param {String} iconCls icon class
     * @param {Boolean} allowDragAndDrop True to allow drag and drop
     */
    _insertResourceImage : function (iconCls, allowDragAndDrop)
    {
        Ametys.cms.uihelper.ChooseResource.open({
            iconCls: iconCls,
            title: "{{i18n PLUGINS_CMS_EDITOR_RESOURCE_IMAGE_INSERT_LABEL}}",
            helpmessage: "{{i18n PLUGINS_CMS_EDITOR_RESOURCE_IMAGE_BROWSER_DESC}}",
            callback: this._insertResourceImageCb,
            allowDragAndDrop: allowDragAndDrop,
            filter: Ametys.explorer.tree.ExplorerTree.IMAGE_FILTER
        });
    },
	
	/**
	 * Callback function called after uploading a resource image
	 * @param {String} id Id of the uploading image
	 * @param {String} filename File name of the uploaded image
	 * @param {String} size File length of the uploaded image
	 * @param {String} viewHref The URL to view the uploaded image
	 * @param {String} downloadHref The URL to download the uploaded image
	 * @private
	 */
	_insertResourceImageCb: function(id, filename, size, viewHref, downloadHref)
	{
	    // FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
		tinyMCE.activeEditor.focus();
		if (id != null)
		{
            var data = {
               "id": Ext.id(),
               "class": tinyMCE.activeEditor.schema.elements.img ? tinyMCE.activeEditor.schema.elements.img.attributes.class.defaultValue : "",
               "src": viewHref,
               "_mce_ribbon_select": "1",
               "data-ametys-type": "explorer",
               "data-ametys-src": id
            };
            tinyMCE.activeEditor.selection.setContent(tinyMCE.activeEditor.dom.createHTML('img', data));
            
            
            var node = tinyMCE.activeEditor.dom.get(data.id);
            var editor = tinyMCE.activeEditor;
            node.onload = function() {
                node.onload = null;
                editor.selection.select(node);
            }
            node.removeAttribute("id");            
        }
	},
	
	// ---------------------------------------- //
	//					WIDTH 					//
	// ---------------------------------------- //
	/**
	 * Set the width of current <img> node when the input loses the focus
	 * @param {Ext.form.field.Text} input The input text
	 */
	setImageWidthOnBlur: function(input)
	{
		var inputValue = input.getValue();
		var img = this._currentNode;
		
		if (img != null && img.style.width != inputValue + 'px')
		{
			this.setWidth(inputValue);
		}
	},
	
	/**
	 * Set the width of current <img> node when the spinner is made to spin up or down
	 * @param {Ext.form.field.Text} input The input text
	 * @param {String} direction The direction: 'up' if spinning up, or 'down' if spinning down.
	 */
	setImageWidthOnSpin: function(input, direction)
	{
		this.setWidth(input.getValue() + (direction == 'up' ? 1 : -1));
	},
	
	/**
	 * Set the width of current <img> node when pressing ENTER or ESC key.
	 * @param {Ext.form.field.Text} input The input text
	 * @param {Ext.event.Event} e The event object
	 */
	setImageWidthOnSpecialKey: function(input, e)
	{
		if (e.getKey() == e.ENTER) 
		{
			e.preventDefault();
			e.stopPropagation();
			this.setWidth(input.getValue());
			// FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
			tinyMCE.activeEditor.focus();
		}	
		else if (e.getKey() == e.ESC) 
		{
			e.preventDefault();
			e.stopPropagation();
			var img = this._currentNode;
			input.setValue(img.style.width);
			// FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
			tinyMCE.activeEditor.focus();
		}
	},
	
	/**
	 * Set 'width' style to current <img> node
	 * @param {String} value The value
	 */
	setWidth: function (value)
	{
		var img = this._currentNode;
		if (img != null)
		{
            var ratio = img.getAttribute("data-ratio");
            if (!ratio)
            {
                ratio = tinyMCE.activeEditor.dom.get(img).offsetHeight / tinyMCE.activeEditor.dom.get(img).offsetWidth;
                img.setAttribute("data-ratio", ratio);
            }
			
			img.style.width = value + "px";
			img.style.height = Math.round(value*ratio) + "px";
			img.removeAttribute("data-mce-style");
			tinyMCE.activeEditor.execCommand('mceAddUndoLevel');
		}
	},
	
	/**
	 * Enable/disable controller and set input value according the selected image
	 * @param {Ametys.ribbon.element.ui.FieldController} 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.
	 */
	widthListener: function (controller, field, node)
	{
		if (node)
		{
			controller.setValue (field.getEditor().dom.get(node).offsetWidth);
		}
		else
		{
			controller.setValue ('');
		}
		
		this._currentNode = node;
	},
	
	// ---------------------------------------- //
	//					HEIGHT 					//
	// ---------------------------------------- //
	/**
	 * Set the height of current <img> node when the input loses the focus
	 * @param {Ext.form.field.Text} input The input text
	 */
	setImageHeightOnBlur: function(input)
	{
		var inputValue = input.getValue();
		var img = this._currentNode;
		
		if (img != null && img.style.height != inputValue + 'px')
		{
			this.setHeight(inputValue);
		}
	},
	
	/**
	 * Set the height of current <img> node when the spinner is made to spin up or down
	 * @param {Ext.form.field.Text} input The input text
	 * @param {String} direction The direction: 'up' if spinning up, or 'down' if spinning down.
	 */
	setImageHeightOnSpin: function(input, direction)
	{
		this.setHeight(input.getValue() + (direction == 'up' ? 1 : -1));
	},
	
	/**
	 * Set the height of current <img> node when pressing ENTER or ESC key.
	 * @param {Ext.form.field.Text} input The input text
	 * @param {Ext.event.Event} e The event object
	 */
	setImageHeightOnSpecialKey: function(input, e)
	{
		if (e.getKey() == e.ENTER) 
		{
			e.preventDefault();
			e.stopPropagation();
			this.setHeight(input.getValue());
			// FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
			tinyMCE.activeEditor.focus();
		}
		else if (e.getKey() == e.ESC) 
		{
			e.preventDefault();
			e.stopPropagation();
			var img = this._currentNode;
			input.setValue(img.style.height);
			// FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
			tinyMCE.activeEditor.focus();
		}
	},
	
	/**
	 * Set 'height' style to current <img> node
	 * @param {String} value The value
	 */
	setHeight: function (value)
	{
		var img = this._currentNode;
		if (img != null)
		{
            var ratio = img.getAttribute("data-ratio");
            if (!ratio)
            {
                // FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
                ratio = tinyMCE.activeEditor.dom.get(img).offsetHeight / tinyMCE.activeEditor.dom.get(img).offsetWidth;
                img.setAttribute("data-ratio", ratio);
            }

            img.style.width = Math.round(value/ratio) + "px";
			img.style.height = value + "px";
			img.removeAttribute("data-mce-style");
			tinyMCE.activeEditor.execCommand('mceAddUndoLevel');
		}
	},
	
	/**
	 * Enable/disable controller and set input value according the selected image
	 * @param {Ametys.ribbon.element.ui.FieldController} 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.
	 */
	heightListener: function (controller, field, node)
	{
		if (node)
		{
			controller.setValue (field.getEditor().dom.get(node).offsetHeight);
		}
		else
		{
			controller.setValue ('');
		}
		
		this._currentNode = node;
	},
	
	
	// ---------------------------------------- //
	//					LEGEND 					//
	// ---------------------------------------- //
	/**
	 * Set 'title' attribute to current <img> node when the input loses the focus
	 * @param {Ext.form.field.Text} input The input text
	 */
	setLegendOnBlur: function (input)
	{
		var inputValue = input.getValue();
		if (this._getLegend(this._currentNode) != inputValue)
		{
			this.setLegend(inputValue);
		}
	},
	
	/**
	 * Set 'title' attribute to current <img> node when pressing ENTER or ESC key.
	 * @param {Ext.form.field.Text} input The input text
	 * @param {Ext.event.Event} e The event object
	 */
	setLegendOnSpecialKey: function (input, e)
	{
		if (e.getKey() == e.ENTER) 
		{
			e.preventDefault();
			e.stopPropagation();
			this.setLegend(input.getValue());
			// FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
			tinyMCE.activeEditor.focus();
		}	
		else if (e.getKey() == e.ESC) 
		{
			e.preventDefault();
			e.stopPropagation();
			input.setValue(this._getLegend(this._currentNode));
			// FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
			tinyMCE.activeEditor.focus();
		}
	},
	 
	/**
	 * Set 'title' attribute to current <img> node when the input loses the focus
	 * @param {String} value The value
	 */
	setLegend: function (value)
	{
		var img = this._currentNode;
		if (img != null)
		{
			if (value == '')
			{
				img.removeAttribute("title");
			}
			else
			{
				img.title = value;
			}
			tinyMCE.activeEditor.execCommand('mceAddUndoLevel');
		}
	},
	
	/**
	 * Get the 'title' attribute of a <img> node
	 * @param {HTMLElement} node The <img> node.
	 * @return {String} The title attribute
	 * @private
	 */
	_getLegend: function (node)
	{
		return node.title || "";
	},
	
	/**
	 * Enable/disable controller and set input value according the selected image
	 * @param {Ametys.ribbon.element.ui.FieldController} 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.
	 */
	legendListener: function  (controller, field, node)
	{
		if (node)
		{
			controller.setValue (this._getLegend(node));
		}
		else
		{
			controller.setValue ('');
		}
		
		this._currentNode = node;
	},
	
	// ---------------------------------------- //
	//				ALTERNATIVE 				//
	// ---------------------------------------- //
	
	/**
	 * Set 'alt' attribute to current <img> node
	 * @param {Ext.form.field.Text} input The input text
	 */
	setAlternativeOnBlur: function (input)
	{
		var inputValue = input.getValue();
		if (this._getAlternative(this._currentNode) != inputValue)
		{
			this.setAlternative(input.getValue());
		}
	},
	
	/**
	 * Set 'alt' attribute to current <img> node when pressing ENTER or ESC key.
	 * @param {Ext.form.field.Text} input The input text
	 * @param {Ext.event.Event} e The event object
	 */
	setAlternativeOnSpecialKey: function (input, e)
	{
		if (e.getKey() == e.ENTER) 
		{
			e.preventDefault();
			e.stopPropagation();
			this.setAlternative(input.getValue());
			// FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
			tinyMCE.activeEditor.focus();
		}	
		else if (e.getKey() == e.ESC) 
		{
			e.preventDefault();
			e.stopPropagation();
			input.setValue(this._getAlternative(this._currentNode));
			// FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
			tinyMCE.activeEditor.focus();
		}
	},
	
	/**
	 * Set 'alt' attribute to current <img> node
	 * @param {String} value The value
	 */
	setAlternative: function (value)
	{
		var img = this._currentNode;
		if (img != null)
		{
			if (value == '')
			{
				img.removeAttribute("alt");
			}
			else
			{
				img.alt = value;
			}
			tinyMCE.activeEditor.execCommand('mceAddUndoLevel');
		}
	},
	
	/**
	 * Get the 'alt' attribute of a <img> node
	 * @param {HTMLElement} node The <img> node.
	 * @return {String} The alt attribute
	 * @private
	 */
	_getAlternative: function (node)
	{
		return node.alt || "";
	},
	
	/**
	 * Enable/disable controller and set input value according the selected image
	 * @param {Ametys.ribbon.element.ui.FieldController} 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.
	 */
	alternativeListener: function (controller, field, node)
	{
		if (node)
		{
			controller.setValue (this._getAlternative(node));
		}
		else
		{
			controller.setValue ('');
		}
		
		this._currentNode = node;
	},
	
	// ---------------------------------------- //
	//					FLOAT 					//
	// ---------------------------------------- //
	/**
	 * Align image on the text left
	 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
	 */
	applyFloatLeft: function (controller) 
	{
		if (this._currentNode != null)
		{
		    // FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
			tinyMCE.activeEditor.focus();
			tinyMCE.activeEditor.execCommand('mceBeginUndoLevel');
			tinyMCE.activeEditor.dom.addClass(this._currentNode, 'floatleft');
			tinyMCE.activeEditor.dom.removeClass(this._currentNode, 'floatright');
			tinyMCE.activeEditor.execCommand('mceEndUndoLevel');
		}
	},
	
	/**
	 * Align image on the text right
	 * @param {Ametys.ribbon.element.ui.ButtonController}  controller The controller calling this function
	 */
	applyFloatRight: function (controller) 
	{
		if (this._currentNode != null)
		{
		    // FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
			tinyMCE.activeEditor.focus();
			tinyMCE.activeEditor.execCommand('mceBeginUndoLevel');
			tinyMCE.activeEditor.dom.addClass(this._currentNode, 'floatright');
			tinyMCE.activeEditor.dom.removeClass(this._currentNode, 'floatleft');
			tinyMCE.activeEditor.execCommand('mceEndUndoLevel');
		}
	},
	
	/**
	 * Align image with the text
	 * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
	 */
	applyNoFloat: function (controller) 
	{
		if (this._currentNode != null)
		{
			tinyMCE.activeEditor.focus();
			tinyMCE.activeEditor.execCommand('mceBeginUndoLevel');
			tinyMCE.activeEditor.dom.removeClass(this._currentNode, 'floatleft');
			tinyMCE.activeEditor.dom.removeClass(this._currentNode, 'floatright');
			tinyMCE.activeEditor.execCommand('mceEndUndoLevel');
		}
	},

    /**
     * Enable/disable and toggle/untoggle controller according the image alignment
     * @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.
     */
    floatControllerListener: function (controller, field, node)
    {
        var editor = (node != null && field != null) ? field.getEditor() : null;
        var cssClass = controller.getInitialConfig('css-class');
        var toggleFunction = Ext.bind(controller.toggle, controller);
        var enableFunction = function(enable) {if (enable) {controller.enable()} else {controller.disable()}};
        this.floatListener(node, editor, toggleFunction, enableFunction, cssClass);
    },

    /**
     * Enable/disable and toggle/untoggle controller according the image alignment
     * @param {HTMLElement} node The current selected node. Can be null.
     * @param {Object} editor The tinyMce editor
     * @param {Function} toggleFunction function called with true/false to select/unselect button
     * @param {Function} enableFunction function called with true/false to enable/disable button
     * @param {String} cssClass The class that have to be there
     */
    floatListener: function (node, editor, toggleFunction, enableFunction, cssClass)
    {
        if (node)
        {
            var isActive = editor.dom.hasClass(node, cssClass);
            toggleFunction(isActive);
            enableFunction(true);
        }
        else
        {
            toggleFunction(false);
            enableFunction(true);
        }
    },

	/**
	 * Enable/disable and toggle/untoggle controller according the image alignment
	 * @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.
	 */
	noFloatControllerListener: function (controller, field, node)
	{
        var editor = (node != null && field != null) ? field.getEditor() : null;
        var cssClass = ['floatleft', 'floatright'];
        var toggleFunction = Ext.bind(controller.toggle, controller);
        var enableFunction = function(enable) {if (enable) {controller.enable()} else {controller.disable()}};
        this.noFloatListener(node, editor, toggleFunction, enableFunction, cssClass);
	},

    /**
     * Enable/disable and toggle/untoggle controller according the image alignment
     * @param {HTMLElement} node The current selected node. Can be null.
     * @param {Object} editor The tinyMce editor
     * @param {Function} toggleFunction function called with true/false to select/unselect button
     * @param {Function} enableFunction function called with true/false to enable/disable button
     * @param {String[]} cssClass The class that have to be absent
     */
    noFloatListener: function (node, editor, toggleFunction, enableFunction, cssClass)
    {
        if (node)
        {
            var isActive = true;
            for (var i = 0; i < cssClass.length; i++) {
                isActive = isActive && !editor.dom.hasClass(node, cssClass[i]);
            }
            toggleFunction(isActive);
            enableFunction(true);
        }
        else
        {
            toggleFunction(false);
            enableFunction(true);
        }
    },
	
	/**
	 * Enable/disable controller if the current selected node is an 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.
	 */
	imageSelectionListener: function (controller, field, node)
	{
		this._currentNode = node;
	},
	
	// ---------------------------------------- //
	//				ZOOMABLE					//
	// ---------------------------------------- //
	
	/**
	 * Add or remove the "zoomable" attribute on the current image node
	 * @param {Ext.form.field.Checkbox} input The checkbox input field
	 */
	setZoomableOnChange: function (input)
	{
		var img = this._currentNode;
		if (img != null)
		{
			var newValue = input.getValue();
			if (newValue != img.hasAttribute('zoomable'))
			{
				if (newValue)
				{
					img.setAttribute("zoomable", "");
				}
				else
				{
					img.removeAttribute("zoomable");
				}
				// FIXME "tinyMCE.activeEditor" a better method is to use the field.getEditor()
				tinyMCE.activeEditor.execCommand('mceAddUndoLevel');
			}
			tinyMCE.activeEditor.focus();
		}
	},

	/**
	 * Enable/disable controller and check box if the current selected node is a zoomable 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.
	 */
	zoomableListener: function (controller, field, node)
	{
		if (node)
		{
			controller.setValue(node.hasAttribute("zoomable"));
		}
		else
		{
			controller.setValue(false);
		}
		
		this._currentNode = node;
	}
	
});