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

/**
 * @private
 * Tool that the display all the permissions of a user.
 */

Ext.define('Ametys.plugins.coreui.profiles.UserPermissionsTool', {
    extend: "Ametys.tool.Tool",
    
    statics : {
        /**
         * @private
         * Renderer for the assignment cells, which draws an icon representing the assignment
         * @param {Object} value The data value for the current cell
         * @param {Object} metaData A collection of metadata about the current cell
         * @param {Ext.data.Model} record The record for the current row
         * @return {String} The HTML string to be rendered
         */
        _renderAssignment: function(value, metaData, record)
        {
            var value = value || {};
            var glyph = Ametys.plugins.coreui.profiles.UserPermissionsTool._computeGlyph(value.accessResult);
            
            var tooltip = Ametys.plugins.coreui.profiles.ProfilesGridHelper.computeExplanation(value.accessExplanations);
            metaData.tdAttr = `data-qtip="${ Ext.String.htmlEncode(tooltip)}" data-qclass="uitool-user-permissions-tooltip"`; // surround with quote to avoid breaking rendering
            
            return '<span class="a-grid-access-result-glyph ' + glyph + '"/>';
        },
        
        /**
         * @private
         * Gets a list of css classes based on an access result.
         * @param accessResult a string representing an access result
         * @return {String} The HTML string to be rendered
         */
        _computeGlyph: function (accessResult)
        {
            switch (accessResult) 
            {
                case Ametys.plugins.coreui.rights.AccessResult.ANONYMOUS_ALLOWED:
                case Ametys.plugins.coreui.rights.AccessResult.ANY_CONNECTED_ALLOWED:
                    return "ametysicon-check allowed";
                case Ametys.plugins.coreui.rights.AccessResult.ANONYMOUS_DENIED:
                case Ametys.plugins.coreui.rights.AccessResult.ANY_CONNECTED_DENIED:
                    return "ametysicon-cross denied"
                case Ametys.plugins.coreui.rights.AccessResult.USER_ALLOWED:
                case Ametys.plugins.coreui.rights.AccessResult.GROUP_ALLOWED:
                    return "ametysicon-check-1 allowed";
                case Ametys.plugins.coreui.rights.AccessResult.USER_DENIED:
                case Ametys.plugins.coreui.rights.AccessResult.GROUP_DENIED:
                    return "ametysicon-cross-1 denied";
                case Ametys.plugins.coreui.rights.AccessResult.UNKNOWN:
                default:
                    // Undetermined
                    return "ametysicon-minus-symbol unknown disabled";
            }
        }
    },

    createPanel: function() {
        return this._createAssignmentsGrid();
    },
    
    setParams: function(params)
    {
        this.callParent(arguments);
        
        this._login = params.login;
        this._populationId = params.populationId;
        // Call the server to retrieve the user full name and update the tool title
        // (or close the tool if the user doesn't exists)
        this.serverCall('getUser', [this._login, this._populationId], this._getUserCb, {scope: this, ignoreCallbackOnError: false});
    },
    
    /**
     * @private
     * The callback for getuser
     * @param {Object} user the user's properties
     */
    _getUserCb: function(user)
    {
        if (user)
        {
            this._fullName = user.fullname || user.login + " / " + user.populationLabel;
            
            this.setTitle(new Ext.Template("{{i18n PLUGINS_CORE_UI_TOOL_USER_PROFILES_LABEL}}").apply([user.fullname]));
            this.setDescription(new Ext.Template("{{i18n PLUGINS_CORE_UI_TOOL_USER_PROFILES_DESCRIPTION}}").apply([user.fullname, user.login, user.populationLabel]))
            
            // mark the tool out of date to start the process or retrieving the permissions
            this.showOutOfDate();

        }
        else
        {
            this.close();
            Ametys.notify({
                title: "{{i18n PLUGINS_CORE_UI_TOOL_USER_PROFILES_AUTOCLOSE_TITLE}}",
                description: "{{i18n PLUGINS_CORE_UI_TOOL_USER_PROFILES_AUTOCLOSE_DESCRIPTION}}",
                type: "warn"
            });
            this.getLogger().error("Unknown user " + this._login + "#" + this._populationId);
        }
    },
    
    
    refresh: function (manual)
    {
        this.showRefreshing();
        var me = this
        this._store.load(function() {me.showRefreshed()});
    },
    
    /**
     * @private
     * Create a grid displaying all the assignments of a user
     * @param {Object[]} a list of object representing the profiles
     * @returns {Ext.grid.Panel} the assignment grid panel
     */
    _createAssignmentsGrid: function() {
        this._store = Ext.create('Ext.data.Store', this._getStoreCfg());
        
        this._assignmentsGrid = Ext.create("Ext.grid.Panel", {
            dockedItems: this._getGridDockedItemsCfg(),
            
            itemId: 'user-permissions-grid',
            store: this._store,
            cls: 'uitool-user-permissions',
            // First the context then all the profiles
            columns: this._getFirstColumn(),
            
            enableColumnHide: false, // disable column hiding by the user. Column visibility is handled by the right filter
            scrollable: true,
            
            stateful: true,
            stateId: this.self.getName() + "$assignment-grid",
            features: [{
                    ftype:'grouping',
                    enableGroupingMenu: false,
                    groupHeaderTpl: '{name} ({children.length})',
            }]
        });
        
        // reset column visibility
        // column are stateful, but the visibility is linked to the right filter and not the user interaction
        Ext.Array.forEach(this._assignmentsGrid.getColumns(), function(column, index) {
            column.setVisible(true);
        }, this);
        
        return this._assignmentsGrid;
    },

    /**
     * @private
     * Get the store configuration for the assignments grid
     * @param {Object[]} a list of object representing all the existing profiles
     * @returns {Object} the store configuration
     */
    _getStoreCfg: function()
    {
        var me = this;
        return {
            model: 'Ametys.plugins.coreui.profiles.UserPermissionsTool.Entry',
            proxy: {
                type: 'ametys',
                role: this.getServerRole(),
                serverId : this.getServerId(),
                methodName: 'getUserPermissions',
                methodArguments: ['login', 'population'],
                reader: {
                    type: 'json',
                    rootProperty: 'data'
                }
            },
            groupField: 'category',
            
            sorters: [{property: 'order', direction:'DESC'}, {property: 'label', direction:'ASC'}],
            listeners: {
                scope: this,
                'beforeload': function(store, operation)
                {
                    // Get the parameters, or create empty object if needed
                    var parameters = operation.getParams() || {};
                    
                    // Add the method arguments
                    parameters = Ext.apply(parameters, {
                        login: this._login,
                        population: this._populationId,
                    });
                    // Set the parameters
                    operation.setParams(parameters);
                },
                'metachange': function(store, meta) {
                    let columns = Ametys.plugins.coreui.profiles.ProfilesGridHelper.getColumnsFromPermissions(meta.permissions, Ametys.plugins.coreui.profiles.UserPermissionsTool._renderAssignment);
                    
                    this._assignmentsGrid.reconfigure(store, this._getFirstColumn().concat(columns));
                }
            }
        }
    },
    
    /**
     * @private
     * Get the definition of the components to dock on the assignments grid
     * @returns {Object/Object[]} the component definition
     */
    _getGridDockedItemsCfg: function()
    {
        return {
            dock: 'top',
            xtype: 'toolbar',
            layout: {
                type: 'hbox',
                align: 'stretch'
            },
            border: false,
            defaultType: 'textfield',
            items: [{
                xtype: 'component',
                html: '{{i18n PLUGINS_CORE_UI_TOOL_USER_PROFILES_FILTERS}}'
            }, {
                xtype: 'edition.right',
                itemId: 'profile-filter',
                name: 'profile-filter',
                cls: 'ametys',
                allowBlank: true,
                multiple: false,
                stacked: "false",
                width: 400,
                emptyText: "{{i18n PLUGINS_CORE_UI_TOOL_USER_PROFILES_RIGHT_FILTER}}",
                listeners: {change: Ext.bind(this._filterByRight, this)}
            }, {
                itemId: 'context-filter',
                name: 'context-filter',
                cls: 'ametys',
                width: 400,
                emptyText: "{{i18n PLUGINS_CORE_UI_TOOL_USER_PROFILES_CONTEXT_FILTER}}",
                listeners: {change: Ext.Function.createBuffered(this._filterByContext, 500, this)}
            }, {
                xtype: 'button',
                // Clear filter
                tooltip: "{{i18n PLUGINS_CORE_UI_TOOL_USER_PROFILES_CLEAR_CONTEXT_FILTER}}",
                handler: Ext.bind (this._clearFilter, this, ['context-filter'], false),
                iconCls: 'a-btn-glyph ametysicon-eraser11 size-16',
                cls: 'a-btn-light'
            }
            ]
        };
    },
    
    _getFirstColumn: function()
    {
        return [
            {
                stateId: 'grid-context-label',
                text: "",
                locked: true,
                lockable: false,
                dataIndex: "label",
                width: 300,
                minWidth: 300,
                sortable: false,
                hideable: false,
            }
        ];
    },
    
    /**
     * @private
     * Filters the columns by right
     * @param {Ametys.form.widget.Right} field The right field
     */
    _filterByRight: function(field)
    {
        Ext.suspendLayouts();
        var rightId = field.getValue();
        if (Ext.isEmpty(rightId))
        {
            Ext.Array.forEach(this._assignmentsGrid.getColumns(), function(column, index) {
                column.setVisible(true);
            }, this);
            this._store.removeFilter('emptyProfile');
        }
        else
        {
            // Computes the columns to let visible
            var matchingColumns = [];
            
            // Hide the others (except the first column)
            Ext.Array.forEach(this._assignmentsGrid.getColumns(), function(column, index) {
                let rights = column.getInitialConfig("rights");
                if (index == 0)
                {
                    // Do nothing on label column
                }
                else if (rights == undefined || Ext.Array.contains(rights, rightId))
                {
                    column.setVisible(true);
                    matchingColumns.push(column.dataIndex);
                }
                else
                {
                    column.setVisible(false);
                }
            }, this);
            
            this._store.addFilter([{
                id: 'emptyProfile',
                filterFn: function(record)
                {
                    for (var dataIndex of matchingColumns)
                    {
                        var assignment = record.get(dataIndex)
                        if (assignment != undefined && assignment.accessResult != Ametys.plugins.coreui.rights.AccessResult.UNKNOWN)
                        {
                            return true;
                        }
                    }
                    return false;
                }
            }]);
        }
        Ext.resumeLayouts(true);
    },
    
    /**
     * @private
     * Filters the row by the context label
     * @param {Ext.form.field.Text} field The context field
     */
    _filterByContext: function(field)
    {
        Ext.suspendLayouts();
        var query = field.getValue();
        if (Ext.isEmpty(query))
        {
            this._store.removeFilter('context');
        }
        else
        {
            this._store.addFilter([{
                id: 'context',
                value: query,
                filterFn: function(item) {return item.data.label != null && item.data.label.toLowerCase().includes(this.getValue().toLowerCase())}
            }])
        }
        Ext.resumeLayouts(true);
    },
    
    /**
     * @private
     * Clear the current text filter
     * @param {String} id The input id
     */
    _clearFilter: function(id)
    {
        this.getContentPanel().down("#"+id).reset();
    },
    
    getMBSelectionInteraction: function() {
        return Ametys.tool.Tool.MB_TYPE_NOSELECTION;
    }
});

 /**
 * This class is the data model for user permissions grid entries.
 * @private
 */
Ext.define('Ametys.plugins.coreui.profiles.UserPermissionsTool.Entry', {
    extend: 'Ext.data.Model',
    
    fields: [
        {name: 'label', mapping: 'label'},
        {name: 'group', type: 'string', mapping: 'group'},
        {name: 'order', type: 'int', mapping: 'order'}
    ]
});