/*
 *  Copyright 2012 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 is a helper to select one or more groups from the group manager. See {@link #act} method
 * 
 *      Ametys.helper.SelectGroup.act({
 *          callback: Ext.bind(function (groups) { console.info(groups[0].id + ', ' + groups[0].groupDirectory); }, this), 
 *          allowMultiselection: false,
 *      }); 
 */
Ext.define('Ametys.helper.SelectGroup', {
    singleton: true,
    
    /**
     * @property {Number} RESULT_LIMIT
     * @readonly
     * The maximum number of records to search for
     */
    RESULT_LIMIT: 100,
    
    /**
     * @private
     * @property {Boolean} _initialized Determine if the plugin have been already initialized or not
     */
    _initialized: false,
    
    /**
     * @property {Boolean} allowMultiselection Property to enable or disable multiselection
     */
    
    /**
     * @property {String} [pluginName="core"] The name of the currently selected plugin to use for requests. Selected by the {@link #act} call.
     */
    /**
     * @property {String} [url="groups/search.json"] The url of the currently selected plugin to use for requests. Selected by the {@link #act} call.
     */
    /**
     * @property {String[]} [contexts] The contexts for the group directories to display in the combobox. Default to the current contexts.
     */
    /**
     * @private
     * @property {Ext.form.field.ComboBox} _groupDirectoriesField The combobox of the dialog box that displays the group directories where to search
     */
    /**
     * @property {Boolean} _enableAllDirectoriesOption True to add an option in the group directories combobx for searching over all the directories.
     * @private
     */
    /**
     * @property {Boolean} _allDirectoriesOptionId The id of the 'all directories' options.
     * @private
     * @readonly
     */
    _allDirectoriesOptionId: '#all',
    
    /**
     * @private
     * @property {Function} callBack The current callback function registered by the {@link #act} call
     */
    /**
     * @private
     * @property {Function} cancelCallback The current cancel callback function registered by the {@link #act} call
     */
    
    /**
     * @private
     * @property {Ext.form.field.Text} _searchField The field of the dialog box that displays the filter field
     */
    /**
     * @private
     * @property {Ext.grid.Panel} _groupList The grid of result groups
     */
    /**
     * @private
     * @property {Ametys.window.DialogBox} _box The re-usable dialog box
     */
    
    /**
     * Open the dialog box to select a group
     * @param {Object} config The configuration options:
     * @param {Function} config.callback The callback function that is called when the group has been selected
     * @param {Object[]} config.callback.groups An array of groups.
     * @param {String} config.callback.groups.id The id of the group
     * @param {String} config.callback.groups.groupDirectory The group directory id of the group
     * @param {String} config.callback.groups.label The name of the group
     * @param {String} config.callback.groups.groupDirectoryName The name of the group directory
     * @param {Function} config.cancelCallback The callback function if the group cancel the dialog box. Can be null.
     * @param {Boolean} [config.allowMultiselection=true] Set to false to disable multiple selection of users.
     * @param {String} [config.plugin=core] The plugin to use for search request.
     * @param {String} [config.url=groups/search.json] The url to use for search request.
     * @param {String/String[]} [config.contexts] The contexts for the group directories to display in the combobox. Default to the current contexts.
     * @param {Boolean} [config.enableAllDirectoriesOption=true] True to add an option in the directory combobx for searching over all the directories.
     */
    act: function (config)
    {
        if (config.url)
        {
            throw new Error("The config parameter 'url' is not supported anymore");
        }
        
        config = config || {};
        
        this.callback = config.callback || function () {};
        this.cancelCallback = config.cancelCallback || function () {};
        this.allowMultiselection = config.allowMultiselection || true;
        this.pluginName = config.plugin || 'core';
        this.contexts = Ext.Array.from(config.contexts || Ametys.getAppParameter('populationContexts'));
        this._enableAllDirectoriesOption = config.enableAllDirectoriesOption !== false;
        
        this.delayedInitialize();
        
        this._groupDirectoriesField.clearValue();
        this._searchField.setValue("");
        this._groupList.getSelectionModel().setSelectionMode(this.allowMultiselection ? 'SIMPLE' : 'SINGLE');
        this._groupList.getSelectionModel().deselectAll();
        this._groupList.getStore().setProxy({
            type: 'ametys',
            reader: {
                type: 'json',
                rootProperty: 'groups'
            },
            role:"org.ametys.plugins.core.group.GroupDAO",
            // No method name because we don't always call the same one
            
            cancelOutdatedRequest: true,
            
            extraParams: {
                limit: this.RESULT_LIMIT
            }
        });
        
        this._box.show();
        this._loadDirectories();
    },
    
    /**
     * @private
     * This method is called to initialize the dialog box. Only the first call will be taken in account.
     */
    delayedInitialize: function ()
    {
        if (this._initialized)
        {
            return true;
        }
        this._initialized = true;
        
        this._groupDirectoriesField = Ext.create('Ext.form.field.ComboBox', {
            xtype: 'combobox',
            fieldLabel: "{{i18n PLUGINS_CORE_UI_GROUPS_SELECTGROUP_DIALOG_DIRECTORY}}",
            name: "groupDirectories",
            cls: 'ametys',
            labelWidth: 150,
            
            store: {
                fields: ['id', {name: 'label', type: 'string'}],
                proxy: {
                    type: 'ametys',
                    plugin: 'core-ui',
                    url: 'group-directories.json',
                    reader: {
                        type: 'json',
                        rootProperty: 'groupDirectories'
                    }
                },
                sorters: [{property: 'label', direction: 'ASC'}],
                listeners: {
                    'beforeload': {fn: this._onBeforeLoadDirectories, scope: this},
                    'load': {fn: this._onLoadDirectories, scope: this}
                }
            },
            valueField: 'id',
            displayField: 'label',
            queryMode: 'local',
            forceSelection: true,
            triggerAction: 'all',
            
            listeners: {change: Ext.bind(this._onGroupDirectoryChange, this)}
        });

        this._searchField = Ext.create('Ext.form.TextField', {
            cls: 'ametys',
            labelWidth: 70,
            width: 210,
            
            fieldLabel: "{{i18n PLUGINS_CORE_UI_GROUPS_SELECTGROUP_DIALOG_FIND}}",
            name: "criteria",
            value: "",
             
            enableKeyEvents: true,
            listeners: {'keyup': Ext.bind(this._reload, this)}
        });
        
        var model = Ext.define('Ametys.helper.SelectGroup.Group', {
            extend: 'Ext.data.Model',
            fields: [
                {
                    name: 'id', 
                    calculate: function(data) {
                        return data.groupId + '#' + data.groupDirectory;
                    }
                },
                {name: 'groupId', mapping: 'id'},
                {name: 'label', type: 'string'},
                {name: 'groupDirectory'},
                {name: 'groupDirectoryLabel', type: 'string'},
                {
                    name: 'displayName',
                    type: 'string',
                    calculate: function(data)
                    {
                        return data.label + ' (' + data.groupId + ', ' + data.groupDirectoryLabel + ')';
                    }
                }
            ]
        });

        var store = Ext.create('Ext.data.Store', {
            model: 'Ametys.helper.SelectGroup.Group',
            sortOnLoad: true,
            sorters: [{property: 'label', direction:'ASC'}],
            
            listeners: {
                'beforeload': Ext.bind(this._onBeforeLoad, this),
                'load': Ext.bind(this._onLoad, this)
            }
        });
        
        this._groupList = Ext.create('Ext.grid.Panel', {
            flex: 1,
            store: store,
            hideHeaders: true,
            columns: [{header: "Label", flex: 1, menuDisabled : true, sortable: true, dataIndex: 'displayName'}]
        }); 
        
        this._box = Ext.create('Ametys.window.DialogBox', {
            title: this.allowMultiselection ? "{{i18n PLUGINS_CORE_UI_GROUPS_SELECTGROUPS_DIALOG_CAPTION}}" : "{{i18n PLUGINS_CORE_UI_GROUPS_SELECTGROUP_DIALOG_CAPTION}}",
            layout: {
                type: 'vbox',
                align : 'stretch',
                pack  : 'start'
            },
            width: 450,
            height: 600,
            //icon: Ametys.getPluginResourcesPrefix('core-ui') + '/img/groups/group_16.png',
            iconCls: 'ametysicon-multiple25',
            
            items: [
                     this._groupDirectoriesField,
                     this._searchField, 
                     this._groupList, 
                     {
                         xtype: 'container',
                         style: {
                             textAlign: 'center'
                         },
                         cls: 'a-text-warning',
                         html: "{{i18n PLUGINS_CORE_UI_GROUPS_SELECTGROUP_DIALOG_WARN100}}"
                     }
            ],
            
            defaultFocus: this._searchField,
            closeAction: 'hide',
            
            referenceHolder: true,
            defaultButton: 'validate',
            
            buttons: [{
                reference: 'validate',
                text: "{{i18n PLUGINS_CORE_UI_GROUPS_SELECTGROUP_DIALOG_OK}}",
                handler: Ext.bind(this.ok, this)
            }, {
                text: "{{i18n PLUGINS_CORE_UI_GROUPS_SELECTGROUP_DIALOG_CANCEL}}",
                handler: Ext.bind(this.cancel, this)
            } ]
        });
    },
    
    /**
     * @private
     * Load the groups when the current group directory has changed 
     */
    _onGroupDirectoryChange: function()
    {
        this.load();
    },
    
    /**
     * Function called before loading the group directory store
     * @param {Ext.data.Store} store The store
     * @param {Ext.data.operation.Operation} operation The object that will be passed to the Proxy to load the store
     * @private
     */
    _onBeforeLoadDirectories: function(store, operation)
    {
        operation.setParams( Ext.apply(operation.getParams() || {}, {
            contexts: this.contexts
        }));
    },
    
    /**
     * @private
     * Listener invoked after loading group directories
     * @param {Ext.data.Store} store The store
     * @param {Ext.data.Model[]} records The records of the store
     */
    _onLoadDirectories: function(store, records)
    {
        if (this._enableAllDirectoriesOption)
        {
            // Add an option in the directories combobox for searching over all the directories
            store.add({
                id: this._allDirectoriesOptionId,
                label: "{{i18n PLUGINS_CORE_UI_GROUPS_SELECTGROUP_DIALOG_DIRECTORY_OPTION_ALL}}"
            });
        }
    },
    
    /**
     * @private
     * Load the group directories
     */
    _loadDirectories: function()
    {
        this._groupDirectoriesField.getStore().load({
            scope: this,
            callback: function(records) {
                // When store loaded, select the 'all' option if it is available
                if (this._enableAllDirectoriesOption)
                {
                    this._groupDirectoriesField.select(this._allDirectoriesOptionId);
                }
                // Otherwise select the fist data
                else if (records.length > 0)
                {
                    this._groupDirectoriesField.select(records[0].get('id'));
                }
                // If there is one and only one directory, hide the combobox
                this._groupDirectoriesField.setHidden(records.length == 1);
            }
        });
    },
    
    /**
     * This method is called to apply the current filter immediately
     * @private
     */
    load: function ()
    {
        this._reloadTimer = null;
        this._groupList.getStore().load();
    },
    
    /**
     * Function called before loading the store
     * @param {Ext.data.Store} store The store
     * @param {Ext.data.operation.Operation} operation The object that will be passed to the Proxy to load the store
     * @param {Object} eOpts Event options
     * @private
     */
    _onBeforeLoad: function(store, operation, eOpts)
    {
        // If the directory combobox value is invalid, cancel the loading
        if (this._groupDirectoriesField.getValue() == null)
        {
            return false;
        }
        
        var proxy = operation.getProxy();
        // 'all' option is selected
        if (this._groupDirectoriesField.getValue() == this._allDirectoriesOptionId)
        {
            proxy.methodName = "searchGroupsByContexts";
            proxy.methodArguments = ['contexts', 'limit', 'offset', 'searchCriteria'];
            
            proxy.setExtraParam('contexts', this.contexts);
            proxy.setExtraParam('searchCriteria', this._searchField.getValue());
                        
            return true;
        }
        else
        {
            proxy.methodName = "searchGroupsByDirectory";
            proxy.methodArguments = ['groupDirectoryId', 'searchCriteria', 'limit', 'offset'];
            
            proxy.setExtraParam('groupDirectoryId', this._groupDirectoriesField.getValue());
            proxy.setExtraParam('searchCriteria', this._searchField.getValue());
        }
    },
    
    /**
     * Function called after loading the store
     * @param {Ext.data.Store} store The store
     * @param {Ext.data.Model[]} records The loaded records
     */
    _onLoad: function (store, records)
    {
        if (records.length == 0)
        {
            Ametys.Msg.show({
               title: this.allowMultiselection ? "{{i18n PLUGINS_CORE_UI_GROUPS_SELECTGROUPS_DIALOG_CAPTION}}" : "{{i18n PLUGINS_CORE_UI_GROUPS_SELECTGROUP_DIALOG_CAPTION}}",
               msg: "{{i18n PLUGINS_CORE_UI_GROUPS_SELECTGROUP_DIALOG_NORESULT}}",
               buttons: Ext.Msg.OK,
               icon: Ext.MessageBox.INFO
            });
        }
    },
    
    /**
     * This method is called to apply the current filter but in a delayed time.
     * This is a listener method on filter modificaiton.
     * Every modification will not be directly applyed. Consecutive modifications (separated by less than 500 ms) will be applyed at once.
     * @private
     */
    _reload: function (field, newValue, oldValue)
    {
        if (this._reloadTimer != null)
        {
            window.clearTimeout(this._reloadTimer);
        }
        this._reloadTimer = window.setTimeout(Ext.bind(this.load, this), 500);
    },
    
    /**
     * @private
     * The method called when the group push the ok button of the dialog box
     */
    ok: function ()
    {
        var addedgroups = [];
        
        var selection = this._groupList.getSelectionModel().getSelection();
        if (selection.length == 0)
        {
            Ametys.Msg.show({
                   title: this.allowMultiselection ? "{{i18n PLUGINS_CORE_UI_GROUPS_SELECTGROUPS_DIALOG_CAPTION}}" : "{{i18n PLUGINS_CORE_UI_GROUPS_SELECTGROUP_DIALOG_CAPTION}}",
                   msg: "{{i18n PLUGINS_CORE_UI_GROUPS_SELECTGROUP_DIALOG_ERROR_EMPTY}}",
                   buttons: Ext.Msg.OK,
                   icon: Ext.MessageBox.INFO
                });
            return;
        }

        this._box.hide();
        
        for (var i=0; i < selection.length; i++)
        {
            var opt = selection[i];
            addedgroups.push({
               id: opt.get('groupId'),
               groupDirectory: opt.get('groupDirectory'),
               label: opt.get('label'),
               groupDirectoryName: opt.get('groupDirectoryLabel')
            });
        }
    
        this.callback(addedgroups);
    },

    /**
     * @private
     * The method called when the group cancel the dialog box
     */
    cancel: function ()
    {
        this._box.hide();

        this.cancelCallback();
    }
});