/*
 *  Copyright 2022 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 class provides a widget to select an image
 */
Ext.define('Ametys.form.widget.Image', {
    extend: 'Ametys.form.AbstractField',

    /** @cfg {String} [deleteButtonIcon] The full path to the delete button icon (in 16x16 pixels). Can be null to use #cfg-deleteButtonIconCls instead */
    /** @cfg {String} [deleteButtonIconCls=ametysicon-delete30] The CSS class to apply to delete button icon */
    deleteButtonIconCls: 'ametysicon-delete30',
    
    /** @cfg {String} [shape=rectangle] Can be 'rectangle', 'square' or 'circle' */
    /** @cfg {String|String[]} [allowSources="external"] The allowed file sources in a Array or separated by comma. Default to 'external'. */

    /** @cfg {Number} border=2 The border width */
    border: 3,
    
    /** @cfg {String} noImage Url when there is no image */
    noImage: Ametys.getPluginResourcesPrefix('core-ui') +  '/img/Ametys/common/form/widget/image/noimage.svg',
    
    constructor: function (config)
    {
        config.shape = config.shape || 'rectangle';
        config.cls = ['ametys-widget', 'ametys-widget-image', 'ametys-widget-image-shape-' + config.shape];
        
        if (config.shape == 'circle' && config.shadow === undefined)
        {
            config.shadow = false;
        }
        
        config.imageWidth = config.width || 100;
        config.imageHeight = config.height || 100;
        if (config.shape == 'square' || config.shape == 'circle')
        {
            config.imageHeight = config.imageWidth;
        }
        
        config.width = config.imageWidth + this.border * 2;
        config.height = config.imageHeight + this.border * 2;
        
        // File location
        if (Ext.isEmpty(config['allowSources']))
        {
            /**
             * @protected
             * @cfg {String[]} [defaultSources=[Ametys.form.widget.File.External.SOURCE]] The sources to use when {#cfg-allowSources} is null or empty
             */
            this.fileSources = config.defaultSources || [Ametys.form.widget.File.External.SOURCE];
        }
        else
        {
            this.fileSources = Ext.isArray(config['allowSources']) ? config['allowSources'] : config['allowSources'].split(',');
        }
        delete config.allowSources;
        
        this.callParent(arguments);
        
        this.on('boxready', function() {
            if (this.autoClick && this._bbar.items.length == 2 && !this._bbar.items.get(1).isVisible())
            {
                // auto click activated 
                // + only one button (ignoring the hidden delete button)
                this._usedAutoClick = true;
                this._bbar.items.get(0).click();
            } 
        }, this);
    },
    
    disable: function()
    {
        this.callParent(arguments);
        
        this._updateDisable();
    },

    enable: function()
    {
        this.callParent(arguments);
        
        this._updateDisable();
    },
    
    /**
     * Sets the read only state of this field.
     * @param {Boolean} readOnly New status
     */
    setReadOnly: function(readOnly)
    {
        this._bbar.setVisible(!readOnly);
    },

    
    /**
     * @private
     * Update the enable status
     */
    _updateDisable: function() {
        if (this._bbar.rendered)
        {
            var me = this;
            this._bbar.items.each(function(elt) {
                elt.setDisabled(me.disabled);
            });        
        }
    },
    
    setMinWidth: function(width)
    {
        this.callParent(arguments);
        
        if (this.rendered && width > this.getWidth()
            || !this.rendered && width > this.width)
        {
            this.setWidth(width);
        }
    },
    
    setWidth: function(width)
    {
        this.imageWidth = width - this.border * 2;
        if (this.shape == 'square' || this.shape == 'circle')
        {
            this.imageHeight = this.imageWidth;
        }
        
        this.callParent(arguments);
        
        this._updateSize();
    },
    
    setMinHeight: function(height)
    {
        this.callParent(arguments);
        
        if (this.rendered && height > this.getHeight()
            || !this.rendered && height > this.height)
        {
            this.setHeight(height);
        }
    },
    
    setHeight: function(height)
    {
        this.imageHeight = height - this.border * 2;
        if (this.shape == 'square' || this.shape == 'circle')
        {
            this.imageWidth = this.imageHeight;
        }
        
        this.callParent(arguments);
        
        this._updateSize();
    },
    
    /**
     * @private
     * Update the size
     */
    _updateSize: function()
    {
        this._container.setWidth(this.imageWidth + this.border * 2); // image width + padding
        this._container.setHeight(this.imageHeight + this.border * 2); // image height + padding
        
        if (this._img.rendered)
        {
            this._img.el.dom.style.lineHeight = (this.imageHeight - 2) + "px";
            this._img.imgEl.dom.style.width = this.imageWidth + "px";
            this._img.imgEl.dom.style.height = this.imageHeight + "px";
            this._img.imgEl.dom.style.maxWidth = this.imageWidth + "px";
            this._img.imgEl.dom.style.maxHeight = this.imageHeight + "px";
            this._img.imgEl.dom.style.objectFit = 'contain';
        }
        
        if (this._bbar.rendered)
        {
            var maxPercentSize = 0.33;
            var maxPixelSize = 50;
            var externalSeparatorWeight = 2;
            var maxInternalSeparatorPixelSize = 10;
            var glyphSize = 0.33;
        
            var minSize = Math.min(this.imageWidth, this.imageHeight);
            
            var nbButtons = this.fileSources.length + 1 /* delete */;
            var buttonSize = Math.min(Math.min(1 / (nbButtons + 1 /* space */), maxPercentSize) * minSize, maxPixelSize);
            var separatorSize = Math.min((this.imageWidth - nbButtons * buttonSize) / (2 * nbButtons + 2 * externalSeparatorWeight), maxInternalSeparatorPixelSize);

            this._bbar.items.each(function(elt) {
                elt.setWidth(buttonSize);
                elt.setHeight(buttonSize);
                elt.setStyle({ fontSize: (buttonSize * glyphSize) + 'px' })
                elt.setMargin('0 ' + separatorSize + ' 0 ' + separatorSize);
            });
            
            this._bbar.setLocalXY(0, ((this.imageHeight + this.border * 2) - buttonSize) * 3 / 4);
            this._bbar.setWidth(this.imageWidth + this.border * 2);
            this._bbar.setHeight(buttonSize);
        }
        
        // Change image preview url
        this._updateUI();
    },
    
    initComponent: function()
    {
        this.items = this.items || [];
        
        this._container = Ext.create('Ext.container.Container', {
            layout: 'absolute'
        });
        this.items.push(this._container);
        
        this._createPreviewImage();   
        this._createButtons(); 
        
        this.callParent(arguments);
    },
    
    /**
     * @private
     * Creates the image preview image
     */
    _createPreviewImage: function()
    {
        /**
         * @property {Ext.Img} _img Displaying the selected image OR the empty image
         * @private
         */
        this._img = Ext.create('Ext.Img', {
            autoEl: 'div', 
            src: this.noImage,
            width: '100%',
            height: '100%',
            listeners: {
                'afterrender': {
                    fn: this._updateSize,
                    scope: this, 
                    options: { single: true } 
                }
            }
        });
    
        this._container.add(this._img);
    },
    
    /**
     * @private
     * Creates the button to insert local image
     */
    _createButtons: function()
    {
        var buttons = [];
        
        var buttonCfg = {
            text: "",
            disabled: this.disabled,
            scope: this
        };
        
        for (var i=0; i < this.fileSources.length; i++)
        {
            var source = Ametys.form.widget.File.getFileSource(this.fileSources[i]);
            var itemCfg = source.getMenuItemConfig (this.getInitialConfig(), Ametys.form.widget.File.IMAGE_FILTER);
            itemCfg.tooltip = itemCfg.text + (itemCfg.tooltip ? "<br/>" + itemCfg.tooltip : "");
            itemCfg.text = "";
            itemCfg.handler = this._selectFile;
            Ext.applyIf(itemCfg, buttonCfg);
            
            buttons.push(Ext.create("Ext.button.Button", itemCfg));
        }
        
        this._deleteButton = Ext.create("Ext.button.Button", Ext.applyIf({
            icon: this.deleteButtonIcon,
            iconCls: this.deleteButtonIcon ? null : this.deleteButtonIconCls,        
            /** @cfg {String} deleteTextConfirm The text to display when deleting a file. */
            tooltip: this.deleteText ||  Ametys.form.widget.File.filters[Ametys.form.widget.File.IMAGE_FILTER].deleteText,
            handler: this._deleteFile,
            hidden: true
        }, buttonCfg)); 
        buttons.push(this._deleteButton);
        
        /**
         * @private
         * @property {Ext.Container} _bbar The parent of action buttons
         */
        this._bbar = Ext.create("Ext.Container", {
            layout: {
                type: 'hbox',
                pack: 'center'
            },
            /**
             * @cfg {Boolean} readOnly true to prevent the user from changing the field
             */
            hidden: this.readOnly,
            items: buttons,
            listeners: {
                'afterrender': {
                    fn: this._updateSize,
                    scope: this, 
                    options: { single: true } 
                }
            }
        });
        this._container.add(this._bbar);
    },
    
    /**
     * Select a file.
     * @param {Ext.Button} btn The button calling this function
     * @protected
     */
    _selectFile: function (btn)
    {
        this.triggerDialogBoxOpened = true;
        
        var source = Ametys.form.widget.File.getFileSource(btn.source);
        source.handler (this.getInitialConfig(), Ametys.form.widget.File.IMAGE_FILTER, null, Ext.bind(this._insertResourceCb, this, [source.getFileType()], true));
    },    
    
    /**
     * Callback function, called when a resource is uploaded in the dialog.
     * @param {String} id The uploaded file id.
     * @param {String} filename The uploaded file name.
     * @param {Number} fileSize The uploaded file size in bytes.
     * @param {String} viewHref A URL to view the file.
     * @param {String} downloadHref A URL to download the file.
     * @param {Object} actionResult The result of the upload action. Can be null.
     * @param {String} type The type of the resource. Can be null.
     * @protected
     */
    _insertResourceCb: function(id, filename, fileSize, viewHref, downloadHref, actionResult, type)
    {
        if (id == null)
        {
            return;
        }
        
        this.setValue({
            id: id,
            filename: filename,
            size: fileSize,
            viewUrl: viewHref,
            downloadUrl: downloadHref,
            type: type
        });
        
        this.triggerDialogBoxOpened = false; 
        this.focus();
    },
    
    /**
     * Delete the file.
     * @private
     */
    _deleteFile: function()
    {
        this.triggerDialogBoxOpened = true; 
        
        // Show the confirmation dialog.
        Ametys.Msg.confirm(
            this.getInitialConfig('deleteText') ||  Ametys.form.widget.File.filters[Ametys.form.widget.File.IMAGE_FILTER].deleteText, 
            this.getInitialConfig('deleteTextConfirm') ||  Ametys.form.widget.File.filters[Ametys.form.widget.File.IMAGE_FILTER].deleteTextConfirm,
            this._effectiveDeletFile,
            this
        );
    },
    
    /**
     * Delete the file.
     * @private
     */
    _effectiveDeletFile: function(btn)
    {
        if (btn == 'yes')
        {
            this.setValue();
        }
        
        this.triggerDialogBoxOpened = false;
        this.focus();
    },
    
    /**, 
     * Sets a data value into the field.
     * @param {Object} value The value to set.
     * @param {String} [value.id] The file identifier.
     * @param {String} value.filename The file name (if applicable).
     * @param {String} value.type The file object type ('metadata').
     * @param {Number} value.size The file size in bytes.
     * @param {Number} value.lastModified The file's last modification date.
     * @param {Number} value.viewUrl A URL to view the file.
     * @param {Number} value.downloadUrl A URL to download the file.
     */
    setValue: function (value)
    {
        if (value && !Ext.Object.isEmpty(value))
        {
            value = Ext.applyIf (value, {
                id: 'untouched',
                type: 'external'
            });
        }
        else
        {
            value = {};
        }
        
        this.callParent([value]);

        Ext.suspendLayouts();
        try
        {
           this._updateUI();
        }
        finally
        {
            Ext.resumeLayouts(true);
        }
    },
    
    isEqual: function(value1, value2) 
    {
        return value1 != null && value2 != null && value1.id === value2.id;
    }, 
    
    getSubmitValue: function ()
    {
        return this.value ? Ext.encode(this.value) : '';
    },
    
    getReadableValue: function ()
    {
        return this.value ? this.value.filename : '';
    },   
    
         /**
     * Update UI
     * @private
     */
    _updateUI: function()
    {
        var value = this.value;
        
        if (!value || !value.id)
        {
            this._deleteButton.hide();
            
            this._img.setSrc(this.noImage);
        }
        else
        {
            if (!this.readOnly && this.allowBlank)
            {
                this._deleteButton.show();
            }
            
            var separator = '&';
            if (value.viewUrl.indexOf('?') < 0)
            {
                separator = '?';
            }
            
            var maxWidth = Math.round(this.imageWidth);
            var maxHeight = Math.round(this.imageHeight);
            
            var imgSrc = value.viewUrl.replace('_max100x100', '_max' + maxHeight + 'x' + maxWidth) + separator + 'maxWidth=' + maxWidth + '&maxHeight=' + maxHeight + '&foo=' + Math.random();
            this._img.setSrc(imgSrc);
        }
    }
});