/*
 *  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 sort field widget
 * This widget is registered for fields of type Ametys.form.WidgetManager#TYPE_STRING
 */
Ext.define('Ametys.web.form.widget.SearchSort', {
    extend: 'Ametys.web.form.widget.SearchField',
    
    /**
     * @cfg {String} [dependingFieldName] The relative path to a widget whose value will be the restriction of the store of the SearchSort field.
     */
    
    /**
     * @private
     * @property {String} iconSortPlugin The plugin having the sort icons
     */
    iconSortPlugin: 'cms',
    /**
     * @private
     * @property {String} iconSortASC The page in the resources dir of the #iconSortPlugin plugin for the ASC sort image
     */
    iconSortASC: 'img/widgets/ascending.png',
    /**
     * @private
     * @property {String} iconSortDESC The page in the resources dir of the #iconSortPlugin plugin for the DESC sort image
     */
    iconSortDESC: 'img/widgets/descending.png',
    
    getServerId: function()
    {
        return "edition.search-sort";
    },
    
    /**
     * @protected
     * The model name for the store
     * @return {String} model name for the store
     */
    getStoreModel: function()
    {
        return 'Ametys.web.form.widget.SearchSort.Model';
    },
    
    constructor: function(config)
    {
        this.iconSorts = {
            'ASC': Ametys.getPluginResourcesPrefix(this.iconSortPlugin) + '/' + this.iconSortASC,
            'DESC': Ametys.getPluginResourcesPrefix(this.iconSortPlugin) + '/' + this.iconSortDESC
        };
        
        config.labelTpl = this._getLabelTpl();
        config.labelHTML = true;
        
        this.callParent(arguments);
    },
    
    initComponent: function()
    {
        this.cls = 'x-form-selectmetadatasort-widget';
        
        if (this.dependingFieldName && this.form && Ext.isFunction(this.form.onRelativeFieldsChange))
        {
            this.form.onRelativeFieldsChange(this.dependingFieldName, this, this._onDependingFieldChange);
        }
        
        this.callParent(arguments);
    },
    
    /**
     * @private
     * Listener called when the value of the depending field changed.
     * @param {Ext.form.field.Base} dependingField The field that its value will restrain the store of the sort field. Must be multiple.
     * @param {String[]} newValue The new value of the depending field
     * @param {String[]} oldValue the old value of the depending field
     */
    _onDependingFieldChange: function(dependingField, newValue, oldValue)
    {
        // Clean current selection
        if (newValue.length > 0)
        {
            var filteredValue = this.value
                .map(this._decode)
                .filter(function(sort) {
                    return Ext.Array.contains(newValue, sort.name);
                });
            this.setValue(filteredValue);
        }
        
        // Add a filter on the store if proposed sorts are not empty
        var filters = this.getStore().getFilters();
        filters.removeAll();
        if (newValue.length > 0)
        {
            filters.add(function(sort) {
                // only keep entries that are selected in the depending field
                return Ext.Array.contains(newValue, sort.getId());
            });
        }
        // else no filter as we want to allow all sorts from proposed sorts
    },
    
    initEvents: function()
    {
        this.callParent(arguments);
        
        // Click listener on the combobox to deals with clickable entries.
        this.mon(this.itemList, 'click', this._onComboboxItemListClick, this);
    },
    
    /**
     * @private
     * Event when the combobox itemlist is clicked to open the content if required
     * @param {Event} evt The click event
     * @param {Ext.Element} el The item list
     * @param {Object} o Options. Emtpy.
     */
    _onComboboxItemListClick: function(evt, el, o)
    {
        // Handle clickable entry, in order to open the corresponding content.
        var itemEl = evt.getTarget('.x-tagfield-item'),
            clickValid = itemEl ? evt.getTarget('.clickable') : false,
            record, name, sort;
            
        if (clickValid)
        {
            record = this.getRecordByListItemNode(itemEl);
            if (record)
            {
                record.invertIfPossible();
            }
        }
    },
    
    /**
     * @private
     * Gets the label template
     * @return {String[]} the label template
     */
    _getLabelTpl: function()
    {
        var labelTpl = [];
        
        labelTpl.push('<tpl if="values.sort == \'ASC\'"><img class="clickable" width="16" height="16" src="' + this.iconSorts.ASC + '"/></tpl>');
        labelTpl.push('<tpl if="values.sort == \'DESC\'"><img class="clickable" width="16" height="16" src="' + this.iconSorts.DESC + '{smallIcon}"/></tpl>');
        labelTpl.push('<span>{[values.text]}</span>');
     
        return labelTpl;
    },
    
    /**
     * @private
     * Decodes the given object
     * @param {String/Object} valueObj The encoded (as string) object, or the object itself
     * @return {Object} The decoded object
     */
    _decode: function(valueObj)
    {
        if (Ext.isString(valueObj))
        {
            var decoded = Ext.decode(valueObj, true/*safe*/);
            if (decoded == null)
            {
                decoded = {
                    name: valueObj,
                    sort: null
                };
            }
            return decoded;
        }
        else
        {
            return valueObj
        }
    },
    
    _isInStore: function(valueAsStr)
    {
        var decoded = this._decode(valueAsStr);
        return this.getStore().indexOfId(decoded.name) > -1;
    },
    
    setValue: function(value)
    {
        var valueObjs = Ext.Array.from(value),
            store = this.getStore(),
            sortByNames = {};
        
        Ext.Array.map(valueObjs, this._decode, this)
            .forEach(function(valueObj) { sortByNames[valueObj.name] = valueObj.sort; });
        
        this.callParent([Object.keys(sortByNames)]);
        this.value = value;
        
        Ext.Object.each(sortByNames, function(name, sort) {
            var record = store.getById(name);
            if (record && sort)
            {
                record.set('sort', sort);
            }
        });
    },
    
    getValue: function()
    {
        var value = Ext.Array.from(this.callParent(arguments)),
            store = this.getStore();
        
        function toObj(record)
        {
            return {
                name: record.getId(),
                sort: record.get('sort')
            };
        }
        var valueObjs = value
            .map(this._decode)
            .map(function(valueObj) { return valueObj.name; })
            .map(function(id) { return store.getById(id); })
            .filter(function(record) { return record != null; })
            .map(toObj)
            .map(function(valueObj) { return Ext.encode(valueObj); });
        
        return this.multiple ? valueObjs : (valueObjs.length > 0 ? valueObjs[0] : null);
    }
});

/**
 * @private
 * The model used in the {@link Ametys.web.form.widget.SearchSort} store 
 */
Ext.define('Ametys.web.form.widget.SearchSort.Model', { 
    extend: 'Ametys.web.form.widget.SearchFacet.Model',
    
    fields: [
        'availableOrders',
        {
            name: 'sort', convert: function(v, record) {
                var availableOrders = record.get('availableOrders');
                if (availableOrders.length == 1)
                {
                    return availableOrders[0];
                }
                else
                {
                    return v || 'ASC';
                }
            },
            depends: ['availableOrders']
        }
    ],
    
    /**
     * Inverts the sort direction, if possible (i.e. if both direction are present in availableOrders)
     */
    invertIfPossible: function()
    {
        var availableOrders = this.get('availableOrders');
        if (availableOrders.length == 1)
        {
            // Cannot invert, do nothing
        }
        else
        {
            var sort = this.get('sort');
            this.set('sort', sort == 'DESC' ? 'ASC' : 'DESC');
        }
    }
});