/*
 *  Copyright 2016 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 one or more sites.<br>
 * This embeds a drop down list with querying on title of sites and type-ahead support.<br>
 * A dialog box also allow to select sites in the site tree.<br>
 * See {@link Ametys.web.helper.ChooseSite}<br>
 * 
 * This widget is registered for fields of type Ametys.form.WidgetManager#TYPE_STRING
 */
Ext.define('Ametys.web.form.widget.SelectSite', {
    extend: 'Ametys.form.AbstractQueryableComboBox',
    
    statics: {
        /**
         * @property {String} SITE_CONTEXT_CURRENT values for the current site
         * @private
         * @readonly 
         */
        SITE_CONTEXT_CURRENT: 'CURRENT_SITE',
        /**
         * @property {String} SITE_CONTEXT_ALL values for all sites
         * @private
         * @readonly 
         */
        SITE_CONTEXT_ALL: 'SITES',
        /**
         * @property {String} SITE_CONTEXT_OTHERS values for others sites
         * @private
         * @readonly 
         */
        SITE_CONTEXT_OTHERS: 'OTHER_SITES',
        /**
         * @property {String} SITE_CONTEXT_SITES_LIST values for list of sites
         * @private
         * @readonly 
         */
        SITE_CONTEXT_SITES_LIST: 'SITES_LIST'
    },
    
    /**
     * @cfg {Boolean/String} [allowSelectContext=false] True display the select context combobox. If false, allowSelectSite must be true.
     */
    /**
     * @cfg {Boolean/String} [allowContextAll=true] Set to false to not display the all option in the select context combobox
     */
    /**
     * @cfg {Boolean/String} [allowContextOthers=true] Set to false to not display the other option in the select context combobox
     */
    
    /**
     * @cfg {Boolean/String} [allowSelectSite=true] Set to false to not display the site combobox. If false, allowSelectContext must be true.
     */
    
    /**
     * @cfg {Boolean/String} [readAccessOnly=false] true to list sites on read access only. Defaults to false.
     */
    /**
     * @cfg {Boolean/String} [sharedSitesOnly=false] true to list sites with shared contents only. Defaults to false.
     */
    
    /**
     * @cfg {Boolean} [multiple=false] True Use #multipleSites
     * @private
     */
    multiple: false,
    
    /**
     * @cfg {Boolean/String} multipleSites To allow to select multiple sites. Do not use #cfg-multiple.
     */
    
    /**
     * @cfg {Object} buttonConfig The main button configuration. Some fields like handler cannot be set, and take care of overwwrite by #cfg-buttonText, #cfg-buttonIcon or #cfg-buttonIconCls.
     */
    
    /**
     * @cfg {String} [boxTitle] The title of the dialog box.
     */
    /**
     * @cfg {String} [boxIcon] The title of the dialog box.
     */
    /**
     * @cfg {String} [helpMsg] The message displayed at the top of dialog box
     */
    
    /**
     * @cfg {String} [buttonText=""] The text of the select site button.
     * Note that if you supply a value for {@link #buttonConfig}, the buttonConfig.text
     * value will be used instead if available.
     */
    buttonText: '',
    /**
     * @cfg {String} buttonIcon The button icon path for the select button.
     * Note that if you supply a value for {@link #buttonConfig}, the buttonConfig.icon
     * value will be used instead if available.
     */
    buttonIcon: null,
    
    /**
     * @cfg {String} buttonIconCls The CSS class to apply to the select button 
     * Note that if you supply a value for {@link #buttonConfig}, the buttonConfig.icon
     * value will be used instead if available.
     */
    buttonIconCls: 'ametysicon-world91',
    
    /**
     * @cfg {String} buttonTooltip The button icon tooltip for the select button.
     */
    buttonTooltip: "{{i18n PLUGINS_WEB_WIDGET_SITESTREEWIDGET_SELECTSITE}}",
    
    /**
     * @cfg {Number} [minChars=3] The minimum number of characters the user must type before autocomplete activates.
     */
    minChars: 3,
    
    /**
     * @cfg {Number} [growMax=300] If not set to `false`, the max height in pixels of the box select
     */
    growMax: 300,
    
    /**
     * @property {Ext.button.Button} selectButton The select sites button, if any.
     * @protected
     */
    
    valueField: 'name',
    displayField: 'title',
    
    maxResult: 50,
    
    simpleValue: false,
    
    /**
     * @property {Ext.form.field.ComboBox} _contextCombobox The site context combobox
     * @private
     */
    /**
     * @property {String} _contextValue The currently selected context, even if the context combobox is not rendered yet.
     * @private
     */
     
    /**
     * @property {String[]} _siteValues An array containing the currently selected site (even if the site are not yet rendered in the combobox)
     * @private
     */
    
    initComponent: function()
    {
        this.cls = 'x-form-selectsite-widget';
        
        // allowSelectContext is false by default
        this.allowSelectContext = this.allowSelectContext == true || this.allowSelectContext == 'true';
        // allowContextAll is true by default
        this.allowContextAll = this.allowContextAll != false && this.allowContextAll != 'false';
        // allowContextOthers is true by default
        this.allowContextOthers = this.allowContextOthers != false && this.allowContextOthers != 'false';
        
        // allowSelectSite is true by default
        this.allowSelectSite = this.allowSelectSite != false && this.allowSelectSite != 'false';
        
        // readAccessOnly is false by default
        this.readAccessOnly = this.readAccessOnly == true || this.readAccessOnly == 'true';
        // sharedSitesOnly is false by default
        this.sharedSitesOnly = this.sharedSitesOnly == true || this.sharedSitesOnly == 'true';
        
        // workaround to handle multiple site on the widget side (but not on the storage side because the stored value is a JSON string)
        // false by default
        this.multiple = this.multipleSites == true || this.multipleSites == 'true';
        
        this.callParent(arguments);
        
        // initialize fields status
        if (this.allowSelectContext && this.allowSelectSite)
        {
            var siteContext = this.getSiteContext();
            if (siteContext != Ametys.web.form.widget.SelectSite.SITE_CONTEXT_SITES_LIST)
            {
                this._disableSelectSite();
            }
        }
    },
    
    getItems: function()
    {
        var otherItems = [];
        
        this._contextValue = this._contextValue || Ametys.web.form.widget.SelectSite.SITE_CONTEXT_CURRENT; 
        
        var containerItems = [];
        
        if (this.allowSelectContext)
        {
            var contextComboBox = {
                xtype: 'combobox',
                itemId: 'select-site-context',
                editable: false,
                forceSelection: true,
                allowBlank: this.allowBlank,
                width: 120,
                valueField: this.valueField,
                displayField: this.displayField,
                queryMode: 'local',
                value: this._contextValue,
                store: {
                    type: 'array', // Ext.data.ArrayStore
                    fields: [this.valueField, this.displayField],
                    data: [
                           [Ametys.web.form.widget.SelectSite.SITE_CONTEXT_CURRENT, "{{i18n PLUGINS_WEB_WIDGET_SELECTSITE_CURRENTSITE_OPTION}}"],
                           [Ametys.web.form.widget.SelectSite.SITE_CONTEXT_ALL, "{{i18n PLUGINS_WEB_WIDGET_SELECTSITE_ALL_OPTION}}"],
                           [Ametys.web.form.widget.SelectSite.SITE_CONTEXT_OTHERS, "{{i18n PLUGINS_WEB_WIDGET_SELECTSITE_OTHERS_OPTION}}"]
                    ]
                },
                listeners: {
                    render: {fn: this._onContextRender, scope: this},
                    change: {fn: this._onContextChange, scope: this}
                }
            }
            
            // adding site lists if allow select site
            if (this.allowSelectSite)
            {
                contextComboBox.store.data.push([Ametys.web.form.widget.SelectSite.SITE_CONTEXT_SITES_LIST, this.multiple ? "{{i18n PLUGINS_WEB_WIDGET_SELECTSITE_SITES_OPTION}}" : "{{i18n PLUGINS_WEB_WIDGET_SELECTSITE_SITE_OPTION}}"]);
            }
            
            containerItems.push(contextComboBox);
        }
        
        if (this.allowSelectSite)
        {
            containerItems = containerItems.concat(this.callParent(arguments));
            
            // Button that opens the search dialog box.
            if (!this.readOnly)
            {
                var buttonConfig = this.buttonConfig || {};
                Ext.applyIf(buttonConfig, {
                    text: this.buttonText,
                    icon: this.buttonIcon,
                    iconCls: this.buttonIcon ? null : this.buttonIconCls,
                    tooltip: this.buttonTooltip,
                    handler: this.chooseSite,
                    scope: this
                });
                
                this.selectButton = Ext.create('Ext.button.Button', buttonConfig);
                otherItems.push(this.selectButton);
            }
        }
        
        return [
            {
                xtype: 'container',
                itemId: 'container',
                layout: { 
                    type: 'hbox',
                    align: 'stretch'
                },
                flex: 1,
                items: containerItems
            }
        ].concat(otherItems);
    },
    
    onResize: function(w, h)
    {
        this.callParent(arguments);
        
        if (this.allowSelectContext && this.allowSelectSite && this.bodyEl.getWidth() < 300)
        {
            var labelHeight = 0;
            if (this.labelAlign == "top" && this.labelEl)
            {
                labelHeight = this.labelEl.getHeight();
            }
            
            var minHeight = labelHeight + 2 * 24;
            if (h < minHeight)
            {
                this.getComponent('container').getLayout().setVertical(false);
            }
            else
            {
                this.getComponent('container').getLayout().setVertical(true);
            }
        }

    },
    
    getComboBoxConfig: function()
    {
        var cfg = this.callParent(arguments);
        
        cfg.listeners = cfg.listeners || {};
        cfg.listeners.change = {
            scope: this,
            fn: function(combo, newValue, oldValue)
            {
                this._siteValues = Ext.Array.from(newValue);
            }
        }
        
        return cfg;
    },
    
    getErrors: function ()
    {
        var errors = [];
        
        if (this.allowSelectContext)
        {
            errors = errors.concat(this._getContextCombobox().getErrors());
        }
        
        if (this.allowSelectSite)
        {
            errors = errors.concat(this.combobox.getErrors());
        }
        
        return errors;
    },
    
    getLabelTpl: function()
    {
        return '{[values.title]}';
    },
    
    getTipTpl: function()
    {
        var tipTpl = ['<div class="site-tooltip">'];
        tipTpl.push('<b>{[values.title]} ({[values.name]})</b><br/>');
        tipTpl.push("<u>{{i18n PLUGINS_WEB_WIDGET_SITEMAPWIDGET_TOOLTIP_URL}}</u> : {[values.url]}<br/>");
        tipTpl.push("<u>{{i18n PLUGINS_WEB_WIDGET_SITESTREEWIDGET_TOOLTIP_TYPE}}</u> : {[values.type]}<br/>");
        tipTpl.push('</div>');
        
        return tipTpl;
    },
    
    /**
     * Open the dialog box to select sites in sitemap
     */
    chooseSite: function()
    {
        var config = {
            title: this.chooseSiteDialogTitle,
            icon: this.chooseSiteDialogIcon,
            helpMessage: this.chooseSiteDialogHint,
            values: this.getSiteValues(),
            readAccessOnly: this.readAccessOnly,
            sharedSitesOnly: this.sharedSitesOnly,
            multiple: this.multiple,
            callback:  Ext.bind(this._chooseSiteCb, this)
        };
        
        Ametys.web.helper.ChooseSite.open(config);
    },
    
    /**
     * @private
     * This function is called after selecting sites
     * Sets the value of the field.
     * @param {String/String[]} siteNames The names of selected sites 
     */
    _chooseSiteCb: function(siteNames)
    {
        this._setSiteValues(siteNames);
    },
    
    getStore: function()
    {
        return Ext.create('Ext.data.Store', {
            proxy: {
                type: 'ametys',
                plugin: 'web',
                url: 'search-sites',
                reader: {
                    type: 'json',
                    rootProperty: 'sites'
                }
            },
            fields: [
                     {name: 'id'},
                     {name: 'name'},
                     {name: 'title'},
                     {name: 'description'},
                     {name: 'path'},
                     {name: 'url'},
                     {name: 'type'}
            ],
            pageSize: this.maxResult,
            
            remoteSort: true,
            sortOnLoad: true,
            sorters: [{property: 'title', direction:'ASC'}],
            
            listeners: {
                beforeload: {fn: this._onStoreBeforeLoad, scope: this}
            }
        });
    },
    
    /**
     * @private
     * Set the request parameters before loading the store.
     * @param {Ext.data.Store} store The store.
     * @param {Ext.data.operation.Operation} operation The Ext.data.operation.Operation object that will be passed to the Proxy to load the Store.
     */
    _onStoreBeforeLoad: function(store, operation)
    {
        var params = operation.getParams() || {};
        
        operation.setParams(Ext.apply(params, {
            names: params.name ? params.name.split(',') : null,
            query: operation.getParams().query,
            readAccessOnly: this.readAccessOnly,
            sharedSitesOnly: this.sharedSitesOnly
        }));
    },
    
    setValue: function (value)
    {
        // retrieves the value object
        value = value || null;
        if (value)
        {
            if (Ext.isString(value))
            {
                if (!this.simpleValue)
                {
                    if (value.startsWith("{"))
                    {
                        value = Ext.JSON.decode(value);
                    }
                    else if (value == Ametys.web.form.widget.SelectSite.SITE_CONTEXT_CURRENT || value == Ametys.web.form.widget.SelectSite.SITE_CONTEXT_ALL || value == Ametys.web.form.widget.SelectSite.SITE_CONTEXT_OTHERS)
                    {
                        value = {context: value};
                    }
                    else
                    {
                        value = {context: Ametys.web.form.widget.SelectSite.SITE_CONTEXT_SITES_LIST, sites: value.split(',')}
                    }
                }
                else
                {
                    if (value == Ametys.web.form.widget.SelectSite.SITE_CONTEXT_CURRENT)
                    {
                        value = {context: null, sites: [Ametys.getAppParameter("siteName")]};
                    }
                    else
                    {
                        value = {context: null, sites: value.split(',')}
                    }
                }
            }
            
            // value should be an object at this point
            if (!Ext.isObject(value))
            {
                value = null;
            }
        }
        
        if (value)
        {
            this._setSiteContext(value.context || null);
            this._setSiteValues(value.sites || null);
        }
        else
        {
            this._setSiteContext(null);
            this._setSiteValues(null);
        }
    },
    
    getValue: function()
    {
        if (this.simpleValue)
        {
            if (this.multiple)
            {
                return this.getSiteValues();
            }
            else
            {
                return this.getSiteValues() && this.getSiteValues().length > 0 ? this.getSiteValues()[0] : null; 
            }
        }
        else
        {
        	var value = {
                context: this.getSiteContext(),
                sites: this.getSiteValues()
             };
        }
         
        return Ext.encode(value);
    },
    
    /**
     * Retrieves the site context
     * @return {String} The site context.
     */
    getSiteContext: function()
    {
        let contextCombobox = this._getContextCombobox();
        return contextCombobox ? contextCombobox.getValue() : null;
    },
    
    /**
     * @private
     * Set the value of the site context field
     * @param {String} context The site context to set. If null the default site context Ametys.web.form.widget.SelectSite.SITE_CONTEXT_CURRENT will be used.
     */
    _setSiteContext: function(context)
    {
        // Remember the value, because when the combobox is loading (after a set value), a call to combobox.getValue do not return the desired value.
        this._contextValue = context || Ametys.web.form.widget.SelectSite.SITE_CONTEXT_CURRENT;
        
        let contextCombobox = this._getContextCombobox();
        if (contextCombobox)
        {
            contextCombobox.setValue(this._contextValue);
        }
    },
    
    /**
     * @private
     * Retrieves the site context field
     */
    _getContextCombobox: function()
    {
        if (this.allowSelectContext && !this._contextCombobox)
        {
            this._contextCombobox = this.getComponent('container').getComponent('select-site-context'); 
        }
        
        return this._contextCombobox;
    },
    
    /**
     * Retrieves the site values
     * @return {String[]} Array of the site names or null if the field is disabled
     */
    getSiteValues: function()
    {
        return this._siteValues || null;
    },
    
    /**
     * @private
     * Set the site values
     * @param {String/String[]} values The names of the site to set. Can be null to empty the field. 
     */
    _setSiteValues: function(values)
    {
        values = Ext.Array.from(values);
        // Remember the values, because when the combobox is loading (after a set value), a call to combobox.getValue do not return the desired value.
        this._siteValues = values;
        
        if (this.combobox)
        {
            values = this.multiple ? values : values[0];
            this.combobox.setValue(values);
        }
    },
    
    /**
     * @private
     * Called when the context combobox is rendered.
     * @param {Ext.form.field.Field} combobox The context combobox
     */
    _onContextRender: function(combobox)
    {
        // If the context value was initialized before the context combobox is rendered, set the value now.
        if (this._contextValue != null)
        {
            combobox.setValue(this._contextValue);
        }
    },
    
    /**
     * @private
     * Listener on the change event of the context combobox
     * @param {Ext.form.field.Field} field the field
     * @param {String} newValue The new context
     * @param {String} oldValue The old context
     */
    _onContextChange: function(field, newValue, oldValue)
    {
        this._contextValue = newValue;
        
        if (newValue != Ametys.web.form.widget.SelectSite.SITE_CONTEXT_SITES_LIST)
        {
            this._disableSelectSite();
        }
        else
        {
            this._enableSelectSite();
        }
    },
    
    /**
     * @private
     * Enable the select site field
     */
    _enableSelectSite: function()
    {
        if (this.combobox)
        {
            this.combobox.enable();
            this.combobox.allowBlank = false;
            if (this.selectButton)
            {
                this.selectButton.enable();
            }
        }
    },
    
    /**
     * @private
     * Disable the select site field
     */
    _disableSelectSite: function()
    {
        if (this.combobox)
        {
            this.combobox.disable();
            this.combobox.allowBlank = true;
            this.combobox.lastQuery = null;
            this._setSiteValues(null);
            this.combobox.clearInvalid();
            
            if (this.selectButton)
            {
                this.selectButton.disable();
            }
        }
    },
    
    getReadableValue: function(separator)
    {
        separator = separator || ',';
        
        var readableValue = '';
        
        let contextCombobox = this._getContextCombobox();
        if (contextCombobox)
        {
            var contextStore = contextCombobox.getStore(),
                index = contextStore.find(this.valueField, this.getSiteContext()),
                record = contextStore.getAt(index);
            if (record)
            {
                readableValue += record.get(this.displayField);
            }
        }
        
        if (this.combobox)
        {
            readableValue += this.callParent(arguments);
        }
        
        return readableValue;
    }
});