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

/**
 * Search context widget
 * This widget is registered for fields of type Ametys.form.WidgetManager#TYPE_STRING
 */
Ext.define('Ametys.web.form.widget.SearchContext', {
    extend: 'Ametys.form.AbstractFieldsWrapper',
    
    /**
     * @private
     * @property {Ext.container.Container} _allContextCt The container of all single contexts
     */
    /**
     * @private
     * @property {Ext.container.Container} _bottomContainer The bottom container
     */
    
    /**
     * @cfg {Number} initialSize The number of contexts to initially create
     */
    initialSize: 0,
    
    statics: {
        /**
         * @property {Number} LOGICAL_OPERATOR_COMPONENT_WIDTH The width of the components displaying logical operators
         * @static
         * @readonly
         */
        LOGICAL_OPERATOR_COMPONENT_WIDTH: 35,
        /**
         * @property {String} CONNECTING_LOGICAL_OPERATOR_ITEM_ID The item id of the intermediate logical operator, to connect two contexts
         * @static
         * @readonly
         */
        CONNECTING_LOGICAL_OPERATOR_ITEM_ID: 'logical-operator',
        /**
         * @property {String} CONNECTING_LOGICAL_OPERATOR_TEXT The text of the intermediate logical operator, to connect two contexts ('OR' keyword)
         * @static
         * @readonly
         */
        CONNECTING_LOGICAL_OPERATOR_TEXT: '{{i18n PLUGINS_WEB_SERVICE_SEARCH_WIDGET_CONTEXTS_LOGICAL_OPERATOR}}'
    },
    
    initComponent: function()
    {
        this.items = [{
            xtype: 'panel',
            flex: 1,
            layout: {
                type: 'vbox',
                align: 'stretch'
            },
            bodyPadding: 10,
            items: [this._createAllContextContainer(), this._createBottomContainer()]
        }];
        
        this.callParent(arguments);
    },
    
    /**
     * @private
     * Creates and returns the container of all single contexts
     * @return {Ext.container.Container} the container of all single contexts
     */
    _createAllContextContainer: function()
    {
        this._allContextCt = Ext.create('Ext.container.Container', {
            layout: {
                type: 'vbox',
                align: 'stretch'
            },
            items: []
        });
        return this._allContextCt;
    },
    
    /**
     * @private
     * Creates and returns a component representing a single context
     * @param {Number} position The position where the component is inserted
     * @param {Object} [values] The values to set with {@link Ametys.form.ConfigurableFormPanel#setValues}.
     * @return {Ext.container.Container} a component representing a single context
     */
    _createSingleContextComponent: function(position, values)
    {
        var items = [];
        if (position > 0)
        {
            items.push(this._logicalOperatorComponentCfg(position));
        }
        items.push({
            xtype: 'container',
            flex: 1,
            layout: {
                type: 'hbox',
                align: 'stretch'
            },
            items: [this._formPanel(values), this._buttonContainerCfg()]
        });
        
        return Ext.create('Ext.container.Container', {
            singleContext: true,
            layout: {
                type: 'vbox',
                align: 'stretch'
            },
            items: items,
            
            style: {
                marginBottom: '10px'
            }
        });
    },
    
    /**
     * @private
     * Gets the configuration of the component displaying the logical operator
     * @param {Number} position The position where the context component is inserted
     * @return {Object} the configuration of the component displaying the logical operator
     */
    _logicalOperatorComponentCfg: function(position)
    {
        return Ext.merge(this._logicalOperatorBaseCfg(), {
            xtype: 'component',
            itemId: this.statics().CONNECTING_LOGICAL_OPERATOR_ITEM_ID,
            html: this.statics().CONNECTING_LOGICAL_OPERATOR_TEXT
        });
    },
    
    /**
     * @private
     * Gets the base configuration of the logical operator component and the last-parenthesis component (width, style...)
     * @return {Object} the base configuration of the logical operator component and the last-parenthesis component (width, style...)
     */
    _logicalOperatorBaseCfg: function()
    {
        return {
            width: this.statics().LOGICAL_OPERATOR_COMPONENT_WIDTH,
            style: {
                textAlign: 'left',
                fontWeight: 'bold'
            }
        };
    },
    
    /**
     * @private
     * Creates and gets the form containing the items of a single context
     * @param {Object} [values] The values to set with {@link Ametys.form.ConfigurableFormPanel#setValues}.
     * @return {Object} the form containing the items of a single context
     */
    _formPanel: function(values)
    {
        var sitesFieldName = 'sites',
            sitemapContextFieldName = 'search-sitemap-context',
            langFieldName = 'context-lang',
            tagsFieldName = 'tags';
        var data = {};
        data[sitesFieldName] = {
            label: '{{i18n PLUGINS_WEB_SERVICE_SEARCH_WIDGET_CONTEXTS_SITES}}',
            description: '{{i18n PLUGINS_WEB_SERVICE_SEARCH_WIDGET_CONTEXTS_SITES_DESC}}',
            type: 'STRING',
            widget: 'edition.select-site',
            'widget-params': {
                allowSelectContext: true,
                multipleSites: true
            },
            validation:{
                mandatory: true
            }
        };
        data[sitemapContextFieldName] = {
            label: '{{i18n PLUGINS_WEB_SERVICE_SEARCH_WIDGET_CONTEXTS_SITEMAP_CONTEXT}}',
            description: '{{i18n PLUGINS_WEB_SERVICE_SEARCH_WIDGET_CONTEXTS_SITEMAP_CONTEXT_DESC}}',
            type: 'STRING',
            widget: 'edition.search-sitemap-context',
            'widget-params': {
                naturalOrder: true,
                sitesField: sitesFieldName
            },
            validation:{
                mandatory: true
            },
            'default-value': {
                context: 'CURRENT_SITE'
            }
        };
       data[langFieldName] = {
            label: '{{i18n PLUGINS_WEB_SERVICE_SEARCH_WIDGET_CONTEXTS_LANGUAGE}}',
            description: '{{i18n PLUGINS_WEB_SERVICE_SEARCH_WIDGET_CONTEXTS_LANGUAGE_DESC}}',
            type: 'STRING',
            disableCondition: {
                condition: [{
                    id: sitemapContextFieldName,
                    operator: 'neq',
                    value: Ext.encode({
                        context: 'CURRENT_SITE',
                        page: null
                    })
                }]
            },
            widget: 'edition.combobox',
            'widget-params': {
                naturalOrder: true
            },
            enumeration: [
                {
                    value: 'CURRENT', 
                    label: "{{i18n PLUGINS_WEB_SERVICE_SEARCH_WIDGET_CONTEXTS_LANGUAGE_CURRENT}}"
                }, 
                {
                    value: 'ALL', 
                    label: "{{i18n PLUGINS_WEB_SERVICE_SEARCH_WIDGET_CONTEXTS_LANGUAGE_ALL}}"
                }, 
                {
                    value: 'OTHERS',
                    label: "{{i18n PLUGINS_WEB_SERVICE_SEARCH_WIDGET_CONTEXTS_LANGUAGE_OTHERS}}"
                }
            ],
            validation:{
                mandatory: true
            },
            'default-value': 'CURRENT'
        };
        data[tagsFieldName] = {
            label: '{{i18n PLUGINS_WEB_SERVICE_SEARCH_WIDGET_CONTEXTS_TAGS}}',
            description: '{{i18n PLUGINS_WEB_SERVICE_SEARCH_WIDGET_CONTEXTS_TAGS_DESC}}',
            type: 'STRING',
            multiple: true,
            widget: 'edition.tag',
            'widget-params': {
                targetType: 'CONTENT',
                url: 'tags.json',
                sitesField: sitesFieldName,
                allowToggleAutoposting: true
            }
        };
        var cfp = Ext.create('Ametys.form.ConfigurableFormPanel', {
            itemId: 'form',
            flex: 1,
            layout: {
                type: 'vbox',
                align: 'stretch'
            },
            defaultFieldConfig: {
                labelWidth: 175,
                flex: 1
            }
        });
        cfp.configure(data);
        cfp.setValues(values || {});
        return cfp;
    },
    
    /**
     * @private
     * Gets the configuration of the container of the buttons placed on the right of a single context
     * @return {Object} the configuration of the container of the buttons placed on the right of a single context
     */
    _buttonContainerCfg: function()
    {
        return {
            xtype: 'container',
            width: '80px',
            layout: {
                type: 'hbox',
                align: 'stretch'
            },
            defaults: {
                xtype: 'button',
                ui: 'default-toolbar-small'
            },
            items: [{
                itemId: 'rem-btn',
                iconCls: 'ametysicon-sign-raw-cross',
                tooltip: '{{i18n PLUGINS_WEB_SERVICE_SEARCH_WIDGET_CONTEXTS_REMOVE_BUTTON_TOOLTIP}}',
                handler: function(remBtn) {
                    var singleContextCmp = this._getSingleContextCmp(remBtn);
                    this._removeContext(singleContextCmp);
                    
                    // force computing of valid state
                    this.isValid();
                },
                scope: this
            }]
        }
    },
    
    /**
     * @private
     * Removes a context
     * @param {Ext.container.Container} contextComponent The context component to remove
     */
    _removeContext: function(contextComponent)
    {
        var position = this._allContextCt.items.indexOf(contextComponent);
        
        // Context which is at first position will be removed. The one which is second needs to have its logical operator updated
        if (position == 0)
        {
            var secondContext = this._allContextCt.items.getAt(1);
            secondContext && secondContext.remove(this.statics().CONNECTING_LOGICAL_OPERATOR_ITEM_ID);
        }
        
        this._allContextCt.remove(contextComponent);
    },
    
    /**
     * @private
     * Gets the parent single context component of this button
     * @param {Ext.button.Button} btn The button
     * @return {Ext.container.Container} the parent single context component
     */
    _getSingleContextCmp: function(btn)
    {
        return btn.up('container[@singleContext=true]');
    },
    
    /**
     * @private
     * Creates and returns the bottom container
     * Gets the configuration of the bottom container
     * @return {Object} the bottom container
     */
    _createBottomContainer: function()
    {
        this._bottomContainer = Ext.create('Ext.container.Container', {
            layout: {
                type: 'vbox',
                align: 'begin'
            },
            items: [{
                xtype: 'button',
                text: '{{i18n PLUGINS_WEB_SERVICE_SEARCH_WIDGET_CONTEXTS_ADD_CONTEXT}}',
                iconCls: 'ametysicon-sign-raw-add',
                handler: function() {
                    this._allContextCt.add(this._createSingleContextComponent(this._allContextCt.items.getCount()));
                    // Avoid some weird horizontal scrollbar which can appear
                    this.updateLayout();
                },
                scope: this
            }]
        });
        return this._bottomContainer;
    },
    
    setValue: function(value)
    {
        this._allContextCt.removeAll();
        if (Ext.isArray(value))
        {
            Ext.Array.forEach(value, function(contextAsStr, index) {
                var contextObj = Ext.decode(contextAsStr),
                    contextCmp = this._createSingleContextComponent(index, {values: contextObj});
                this._allContextCt.add(contextCmp);
            }, this);
        }
        else if (value == null)
        {
            for (var i = 0; i < parseInt(this.initialSize); i++)
            {
                this._allContextCt.add(this._createSingleContextComponent(i));
            }
        }
    },
    
    getValue: function()
    {
        return this._allContextCt.items.getRange() // get context components
            .map(function(ct) { return ct.down('#form'); }) // get ConfigurableFormPanel of contexts
            .map(function(cfp) { return cfp.getJsonValues();} ) // get values of form
            .map(function(values) { return Ext.encode(values); }); // encode values
    },
    
    getErrors: function(value)
    {
        function getFields(configurableFormPanel)
        {
            return configurableFormPanel.getFieldNames()
                .map(function(fieldName) { return configurableFormPanel.getField(fieldName) });
        }
        
        function concatErrors(existingErrors, subField)
        {
            if (subField.isDisabled())
            {
                return existingErrors;
            }
            else
            {
                return existingErrors.concat(subField.getErrors());
            }
        }
        
        var errors = this.callParent(arguments);
        var fieldsPerContext = this._allContextCt.items.getRange() // get context components
            .map(function(ct) { return ct.down('#form'); }) // get ConfigurableFormPanel of contexts
            .map(function(cfp) { return getFields(cfp); }); // get fields of form
        var fields = Ext.Array.flatten(fieldsPerContext);
        
        var subFieldErrors = [];
        fields.forEach(function (field) {
            subFieldErrors = concatErrors(subFieldErrors, field);
        });
            
        if (subFieldErrors.length)
        {
            errors.push("{{i18n PLUGINS_WEB_SERVICE_SEARCH_WIDGET_CONTEXTS_ERROR_CONTEXT_INVALID}}");
        }
        return errors;
    }
});