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

/**
 * JCR tree panel.
 * @private
 */
Ext.define('Ametys.repository.tree.JcrTreePanel',
{
    extend: 'Ext.tree.Panel',
    animate: true,
    
    constructor: function (config) {
    	Ext.applyIf (config, {
    		
    		defaultWorkspaceName: 'default',
    		
            store: this._getStore(),
            viewConfig: {
                toggleOnDblClick: false
            }
        });
        
        config.dockedItems = this._getDockedItems();
        
        this.callParent(arguments);
    },
    
    /**
     * @protected
     * Create the tree store
     * @return {Ext.data.TreeStore} the store
     */
    _getStore: function ()
    {
        return Ametys.repository.RepositoryDao.getNodeStore();
    },
    
    /**
     * @protected
     * Get the docked items
     * @return {Object} the docked items configuration object
     */
    _getDockedItems: function ()
    {
    	return [{
            dock: 'top',
			xtype: 'toolbar',
            layout: { 
                type: 'hbox',
                align: 'stretch'
            },
            defaultType: 'button',
			items: [
                    // Workspace selection
                    Ext.apply(this._getWorkspacesComboBoxConfig(), {flex: 1}),
                    {
                        xtype: 'tbspacer',
                        flex: 0.0001
                    },
                    {
                        // Refresh node
                        tooltip: "{{i18n PLUGINS_REPOSITORYAPP_REFRESH_NODE_BTN}}",
                        handler: Ext.bind (this.refreshNode, this, [], false),
                        iconCls: 'a-btn-glyph ametysicon-arrow123 size-16',
                        cls: 'a-btn-light'
                    }
            ]
		}]
    },
    
    /**
	 * @private
	 * Get configuration for workspaces combo box
	 * @return {Object} the configuration object
	 */
    _getWorkspacesComboBoxConfig: function ()
    {
    	return {
    		xtype: 'combobox',
			itemId: 'workspaces-combo',
			
            cls: 'ametys',
            maxWidth: 400,
                        
	    	forceSelection: true,
	    	editable: false,
	    	triggerAction: 'all',
	    	queryMode: 'local',
	    	 
	    	fieldLabel: "{{i18n PLUGINS_REPOSITORYAPP_WORKSPACE_NAME}}",
	    	
	    	store: {
                fields: ['name'],
                data: []
            },
	    	
	    	name: 'workspaceName',
	        valueField: 'name',
	        displayField: 'name',
	        
            style: {
                marginRight: '0px'
            },
                        
	        listeners: {
                render: {fn: this._onWorkspaceComboRender, scope: this},
                select: {fn: this._onWorkspaceChange, scope: this}
            }
    	}
    },
    
    /**
     * Load workspaces when the corresponding combobox is rendered.
     * @param {Ext.Component} combo the workspace combobox.
     * @param {Object} eOpts Options added to the addListener
     * @private
     */
    _onWorkspaceComboRender: function(combo, eOpts)
    {
    	// Get the workspaces from the server, in order to fill the workspace combobox.
    	Ametys.data.ServerComm.callMethod({
            role: 'org.ametys.workspaces.repository.jcr.RepositoryDao', 
            methodName: 'getWorkspaces', 
            callback: {
                handler: this._loadWorkspacesCb,
                scope: this
            },
            priority: Ametys.data.ServerComm.PRIORITY_MAJOR,
            errorMessage: "{{i18n PLUGINS_REPOSITORYAPP_WORKSPACE_NAME}}"
        });
    },
    
    /**
     * Get the name of current workspace
     * @return {String} the current workspace's name
     */
    getCurrentWorkspaceName: function ()
    {
    	var workspaceCombo = this.down("combobox[itemId='workspaces-combo']");
    	return workspaceCombo.getValue();
    },
    
    /**
	 * This function reload the given node
	 * @param {String} [id] The id of the node to reload. If null the tree current selection will be used.
	 */
	refreshNode: function (id)
	{
		var node;
		if (id == null)
		{
			var selection = this.getSelectionModel().getSelection();
			node = selection.length > 0 ? selection[0] : null;
			
			// Workaround - Refresh selection in case node is not existing anymore
			this.getSelectionModel().deselect(node, true);
			this.getSelectionModel().select(node);
		}
		else
		{
			node = this.getStore().getNodeById(id);
		}
		
		if (node != null)
		{
			// Set leaf to false, to allow children to be added during the load. Leaf will be set to true again if needed after the load.
			node.set('leaf', false);
			
			var me = this;
			this.getStore().load({
				node: node,
				callback: function(records, operation, success) {
                    if (success)
                    {
                        this.getSelectionModel().select([node]);
                        // FIXME In case the node is not already expanded, we have to force it or the expand sprite will disappear.
                        node.expand();
                    }
                },
				scope: this
			});
		}
	},
	
    /**
     * Callback effectively filling the workspace combobox.
     * @param {String[]} workspaces The workspaces names
     * @param {Array} args The callback arguments.
     * @private
     */
    _loadWorkspacesCb: function(workspaces, args)
    {
    	var workspaceCombo = this.down("combobox[itemId='workspaces-combo']");
        var store = workspaceCombo.getStore();
        
        var wspData = [];
        for (var i = 0; i < workspaces.length; i++)
        {
            wspData.push({
                name: workspaces[i]
            });
        }
        
        store.loadData(wspData, false);
        
        // Initialize with default value
        workspaceCombo.setValue(this.defaultWorkspaceName);
        Ametys.repository.RepositoryApp.setCurrentWorkspace(this.defaultWorkspaceName);
    },
    
    /**
     * Listens for workspace change.
     * @param {Ext.form.field.ComboBox} combo The workspace combobox.
     * @param {Ext.data.Model} record The selected record.
     * @param {Object} eOpts Options added to the addListener
     * @private
     */
    _onWorkspaceChange: function(combo, record, eOpts)
    {
        // Single selection.
        var selectedWorkspace = record.get('name');
        
        // The workspace wasn't changed: do nothing.
        if (Ametys.repository.RepositoryApp.getCurrentWorkspace() == selectedWorkspace)
        {
            return;
        }
        
        Ametys.repository.RepositoryApp.setCurrentWorkspace(selectedWorkspace);
        
        // Reload and keep the selection.
        this.reload(true);
    },
    
    /**
     * Get a node in the store by its path.
     * @param {String} path The node path (without groups).
     * @return {Ametys.repository.data.RepositoryNode} The node if found, `null` otherwise.
     */
    getNodeByPath: function(path)
    {
        var nodes = this.getStore().query('path', path, false, false, true);
        if (nodes.length > 0)
        {
            return nodes.getAt(0);
        }
        return null;
    },
    
    /**
     * Select a node in the tree from its JCR path (without node groups).
     * @param {String} path The full repository node path, with or without leading slash.
     */
    selectNodeByJcrPath: function(path)
    {
        var nodes = this.getSelectionModel().getSelection();
        if (nodes.length > 0 && nodes[0].get('path') == path)
        {
            // The node is already selected: return.
            return;
        }
        
        var nodeColl = this.getStore().query('path', path, false, false, true);
        if (nodeColl.length > 0)
        {
            // The node model is cached in the store: select it.
            this.getSelectionModel().select(nodeColl.getAt(0));
            return;
        }
        
        // If not, query the tree path from the server.
        var relPath = path.charAt(0) == '/' ? path.substring(1) : path;
        
        Ametys.data.ServerComm.callMethod({
            role: 'org.ametys.workspaces.repository.jcr.RepositoryDao', 
            methodName: 'getNodeByPath',
            parameters: [relPath, Ametys.repository.RepositoryApp.getCurrentWorkspace()],
            callback: {
                handler: this._selectNodeByJcrPathCb,
                scope: this
            }
        });
    },
    
    /**
     * Callback for selectNodeByJcrPath.
     * @param {XMLElement} result The XML document.
     * @param {Array} args The callback arguments.
     * @private
     */
    _selectNodeByJcrPathCb: function(result, args)
    {
        if (result != null)
        {
            this.selectNodeByPath(result.pathWithGroups);
        }
    },
    
    /**
     * Select a node in the tree. The full tree path (with node groups) is required.
     * @param {String} path the full tree node path, with groups if applicable. 
     */
    selectNodeByPath: function(path)
    {
        var rootText = this.getRootNode().get('text');
        var fullPath = '/' + rootText + path;
        this.selectPath(fullPath, 'text');
    },
    
    /**
     * Reload the whole tree and optionally re-select the same node after loading.
     * @param {Boolean} [keepSelection=false] true to re-select the same node after loading.
     */
    reload: function(keepSelection)
    {
        var selections = this.getSelectionModel().getSelection();
        
        this.getStore().load({
            callback: function(records, operation, success) {
                if (success && keepSelection && selections != null && selections.length > 0)
                {
                    this.selectNodeByPath(selections[0].get('path'));
                }
            },
            scope: this
        });
    },
    
    /**
     * Reload a given node and optionally select it after loading.
     * @param {Ext.data.TreeModel} node The node to reload.
     * @param {Boolean} [select=true] true to select the node after loading it, false otherwise.
     */
    reloadAndSelect: function(node, select)
    {
        this.getStore().load({
            node: node,
            callback: function(records, operation, success) {
                if (success)
                {
                    // FIXME In case the node is not already expanded, we have to force it or the expand sprite will disappear.
                    node.expand();
                    if (select)
                    {
                        this.getSelectionModel().select([node]);
                    }
                }
            },
            scope: this
        });
    }
});