/*
 *  Copyright 2015 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 UI helper provides a dialog to select one or more orgunits on a orgunits tree.
 * See {@link #open} method.
 */
Ext.define('Ametys.odf.helper.ChooseOrgUnit', {
    singleton: true,
    
    /**
     * @private
     * @property {Boolean} _multiple True to allow multiple selection. Default to false.
     */
    /**
     * @private
     * @property {String[]} _valuesToCheck If multiple, the values to initially check.
     */
    
    /**
     * @private
     * @property {String} [_contentType="org.ametys.plugins.odf.Content.orgunit"] The content type for orgunit to create when creation is enabled
     */
    
    /**
     * Configure and open the dialog box
     * @param {Object} config The configuration options :
     * @param {String} [config.values] The selected orgunits (ids).
     * @param {Boolean} [config.allowCreation=false] `true` to allow the creation of new orgunits from the dialog box.
     * @param {Function} config.callback The callback function invoked when orgunit is selected. The callback function will received the following parameters:
     * @param {Function} config.callback.ids ids of selected orgunits.
     * @param {Boolean} [config.multiple]=false `true` to allow selecting multiple orgunits.
     * @param {String[]} config.values If mutliple is true, the values to check
     */
    open: function (config)
    {
        config = config || {};
        
        this._cbFn = config.callback;
        
        this._multiple = config.multiple || false;
        this._valuesToCheck = config.values;
        
        this._rootOrgUnitId = config.rootOrgUnitId;
        
        // configuration options for creation        
        this._allowCreation = config.allowCreation || false;
        this._contentType = config.contentType || 'org.ametys.plugins.odf.Content.orgunit';
        this._workflowName = config.workflowName || 'orgunit';
        this._initWorkflowActionId = config.initWorkflowActionId || 1;
        this._editWorkflowActionId = config.editWorkflowActionId || '2';
        
        this._createDialogBox();
        this._tree.getStore().getProxy().setExtraParam('tree', "odf-orgunit-tree-config");
        
        this._box.show();
        if (this._rootOrgUnitId)
        {
        	this._setRootNode(this._rootOrgUnitId);
        }
        else
        {
        	Ametys.data.ServerComm.callMethod({
        		role: "org.ametys.odf.orgunit.RootOrgUnitProvider",
        		methodName: "getRootId",
        		callback: {
        			handler: this._setRootNode,
        			scope: this
        		},
        		waitMessage: false
        	});
        }
    },
    
    /**
     * Creates the dialog box
     * @private
     */
    _createDialogBox: function ()
    {
        var me = this;
        
        this._tree = Ext.create('Ametys.plugins.contentstree.ContentsTreePanel', {
        	checkMode: this._multiple,
            treeId: "odf-orgunit-tree-config",
            
            border: true,
            flex: 1,
            selModel: {
            	mode: 'SINGLE'
            },
            
            listeners : {
            	'selectionchange': Ext.bind(this._onSelectOrgUnit, this)
            }
        });
        
        this._box = Ext.create('Ametys.window.DialogBox', {
            title: "{{i18n PLUGINS_ODF_WIDGET_ORGUNITWIDGET_SELECTTITLE}}",
            iconCls: 'odficon-orgunit',
            icon: null,
            width: 395,
            height: 400,
            scrollable: false,
            
            layout: {
                type: 'vbox',
                align : 'stretch',
                pack  : 'start'
            },
            
            items: [{
	                xtype: 'component',
	                cls: 'a-text',
	                html: "{{i18n PLUGINS_ODF_WIDGET_ORGUNITWIDGET_SELECTDESCRIPTION}}"
	            },
	            this._tree,
                {
                    xtype: 'component',
                    cls: 'a-text link',
                    html: "{{i18n PLUGINS_ODF_HELPER_CHOOSEORGUNIT_NO_ORGUNIT_SELECTED}}",
                    hidden: !this._allowCreation
                }
	        ],
            
            
            closeAction: 'destroy',
            buttons : [{
            		itemId: 'ok-btn',
            		disabled: !this._multiple,
                    text : "{{i18n PLUGINS_ODF_HELPER_CHOOSEORGUNIT_OKBUTTON}}",
                    handler : Ext.bind(this._validate, this)
                }, {
                    text : "{{i18n PLUGINS_ODF_HELPER_CHOOSEORGUNIT_CANCELBUTTON}}",
                    handler: Ext.bind(function() {this._box.close();}, this)
                } 
            ]
        });
    },
    
    /**
     * @private
     * Sets the root node
     * @param {String} rootOrgUnitId The id of the root orgunit to set
     */
    _setRootNode: function(rootOrgUnitId)
    {
    	this._tree.setContentRootNode(rootOrgUnitId, {metadataPath: 'childOrgUnits'}, Ext.bind(this._setRootNodeCb, this));
    },
    
    /**
     * @private
     * The callback function of #_setRootNode
     */
    _setRootNodeCb: function()
    {
    	this._tree.getSelectionModel().deselectAll();
        this._tree.getRootNode().expand();
        
    	if (this._multiple && this._valuesToCheck.length)
    	{
            this._tree.store.getRoot().on('expand', function() { 
        		Ametys.data.ServerComm.callMethod({
        			role: "org.ametys.odf.orgunit.OrgUnitDAO",
        			methodName: "getOrgUnitsInfos",
        			parameters: [this._valuesToCheck, this._rootOrgUnitId],
        			callback: {
        				handler: this._expandAndCheckNodes,
        				scope: this
        			},
        			waitMessage: false
        		});
            }, this, { single: true });
    	}
    },
    
    /**
     * @private
     * Expands and select the nodes retrieved from the server.
     * @param {Object} response The response
     */
    _expandAndCheckNodes: function(response)
    {
    	Ext.Array.forEach(response.orgUnits, function(orgUnit) {
    		this._tree.selectPath(orgUnit.path, 'name', '/', this._checkNode, this);
    	}, this);
    	this._tree.getSelectionModel().deselectAll();
    },
    
    /**
     * @private
     * Check the given node
     * @param {Boolean} success true if the node expansion was successful.
     * @param {Ext.data.Model} record If successful, the target record.
     */
    _checkNode: function(success, record)
    {
    	if (success)
    	{
    		record.set('checked', true);
    	}
    },
    
    /**
     * This method updates the dialog box depending on the current selection for non-mutliple selection
     * @param {Ext.selection.Model} sm The selection model
     * @param {Ext.data.Model[]} nodes The selected records
     * @private
     */
    _onSelectOrgUnit: function(sm, nodes)
    {
    	if (!this._multiple)
    	{
	    	var isValidSelection = this._isSelectionValid(nodes);
	    	this._box.down("button[itemId='ok-btn']").setDisabled(!isValidSelection);
    	}
        
        if (this._allowCreation)
        {
            this._updateLinkCreation();
        }
    },
    
    /**
     * This method retrieves the current selection, and set the tag creation link accordingly. 
     * @private
     */
    _updateLinkCreation: function ()
    {
        var selection = this._tree.getSelectionModel().getSelection();
        
        var createLink = this._box.child("component[cls~=link]");
        
        var node = selection[0];
        if (node != null)
        {
            this._checkUserRight(node);
        }
        else
        {
            // No node is selected
            this._box._clickEventRegistered = false;
            createLink.update("{{i18n PLUGINS_ODF_HELPER_CHOOSEORGUNIT_NO_ORGUNIT_SELECTED}}");
        }
    },
    
    /**
     * @private
     * Check if user has right to create a new orgunit
     * @param node The selected node
     */
    _checkUserRight: function (node)
    {
        var me = this,
            createLink = me._box.child("component[cls~=link]");
        
        // FIXME this should also check if user can edit the parent orgunit
        var contentType = Ametys.cms.content.ContentTypeDAO.getContentType(this._contentType);
        if (contentType == null || contentType.isMixin() || contentType.isAbstract() || !contentType.hasRight())
        {
                // No right to create orgunit
                me._box._clickEventRegistered = false;
                createLink.update("{{i18n PLUGINS_ODF_HELPER_CHOOSEORGUNIT_CREATE_ORGUNIT_NORIGHT}}");
        }
        else
        {
            createLink.update("<a class='action'>{{i18n PLUGINS_ODF_HELPER_CHOOSEORGUNIT_CREATE_ORGUNIT_LINK}}</a>");     
            // add a click event listener on the <a class='action'> dom node to call the #_createOrgUnit method.
            if (!me._box._clickEventRegistered)
            {
                createLink.mon(createLink.getEl(), 'click', Ext.bind(me._createOrgUnit, me, [[contentType]]), me, {delegate: 'a.action'});
                me._box._clickEventRegistered = true;
            }
        }
    },
    
    /**
     * @private
     * Create a new orgunit
     * @param {Object[]} contentTypes The content type
     */
    _createOrgUnit: function (contentTypes)
    {
        var selection = this._tree.getSelectionModel().getSelection();
        if (selection && selection.length > 0)
        {
            var parentNode = selection[0];
            
            Ametys.cms.uihelper.CreateContent.open({
	            contentTypes: contentTypes,
	            contentLanguage: parentNode.get('lang') || 'fr',
	            initWorkflowActionId: this._initWorkflowActionId,
	            workflowName: this._workflowName
	        }, Ext.bind(this._createOrgUnitCb, this, [parentNode], true), this);
        }
    },
    
    /**
     * Callback invoked after creating the orgunit. Set the parent metadata
     * @param {String} ouId The id of created orgunit
     * @param {Ext.data.NodeInterface} parentNode The parent node
     */
    _createOrgUnitCb: function(ouId, parentNode)
    {
        var me = this,
            contentIdsToEdit = {};
        contentIdsToEdit[parentNode.get('contentId')] = null;
        
        Ametys.data.ServerComm.callMethod({
            role: "org.ametys.core.ui.RelationsManager",
            id: "org.ametys.cms.relations.setcontentattribute",
            methodName: "setContentAttribute",
            parameters: [
                [ouId],
                contentIdsToEdit,
                [],
                parentNode.get('metadataPath'),
                [this._editWorkflowActionId]
            ],
            callback: {
                handler: function(response, args) {
                    if (response == null)
                    {
                        Ametys.log.ErrorDialog.display({
                            title: "{{i18n plugin.contents-tree:PLUGINS_CONTENTSTREE_ADDELEMENT_CREATEDNOTASSOCIATED_TITLE}}",
                            text: "{{i18n plugin.contents-tree:PLUGINS_CONTENTSTREE_ADDELEMENT_CREATEDNOTASSOCIATED_TEXT}}"
                        });
                    }
                    else if (response['success'] == true)
                    {
                        // Set leaf to false, to allow children to be added during the load.
			            parentNode.set('leaf', false);
			            me._tree.getStore().load({node: parentNode});
                    }
                },
                arguments: null
            },
            waitMessage: true
        });
    },
    
    /**
     * Check if selection is valid according to the configuration parameters
     * @param {Ext.data.Model[]} nodes The selected nodes.
     * @return {boolean} true if the selection is valid
     * @private
     */
    _isSelectionValid : function(nodes)
    {
        if (nodes.length == 0)
        {
        	return false;
        }
        
        return true;
    },
    
    /**
     * Retrieve the current tree value, and call the callback function from the initial configuration sent to #open
     * @private
     */
    _validate: function ()
    {
    	if (this._multiple)
    	{
    		var selected = this._tree.getChecked();
    	}
    	else
    	{
	    	var selected = this._tree.getSelectionModel().getSelection(); // always an array of length 1
    	}
        
        this._box.close();
        if (this._cbFn)
        {
        	var contentIds = Ext.Array.map(selected, function(item) {
        		return item.get('contentId');
        	}, this);
            this._cbFn(contentIds);
        }
    }
});