/*
 *  Copyright 2014 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 pages on a sitemap tree.
 * See {@link #open} method.
 * 
 * 			Ametys.web.helper.ChoosePage.open({
 * 				multiple: false,
            	siteContext: Ametys.web.sitemap.SitemapTree.SITE_CONTEXT_CURRENT,
            	sitemapContext: Ametys.web.sitemap.SitemapTree.SITEMAP_CONTEXT_CURRENT,
            	allowCreation: true,
            	callback:  Ext.bind(this._choosePageCb, this)
 * 			});
 */
Ext.define('Ametys.web.helper.ChoosePage', {
    singleton: true,
    
    /**
     * Configure and open the dialog box
     * @param {Object} config The configuration options :
     * @param {String} [config.icon] The full path to icon (16x16 pixels) for the dialog box.
     * @param {String} [config.iconCls=ametysicon-world91] The CSS class for icon of the dialog box
     * @param {String} [config.title] The title of the dialog box.
     * @param {String} [config.helpMessage] The help message to display on top of dialog box.
     * @param {String} [config.values] the selected pages as an array of id.
     * @param {String} [config.siteContext=Ametys.web.helper.ContextToolbar#SITE_CONTEXT_CURRENT] The site context. Set to Ametys.web.helper.ContextToolbar#SITE_CONTEXT_ALL to see all sites.
     * @param {String} [config.sitemapContext=Ametys.web.helper.ContextToolbar#SITEMAP_CONTEXT_ALL] The sitemap context. Set to Ametys.web.helper.ContextToolbar#SITEMAP_CONTEXT_ALL to see all languages. Set to a language code such as 'fr', 'en', .. for using a specifying language.
     * @param {String} [config.defaultSiteName] The default site name.
     * @param {String} [config.defaultSitemapName] The default sitemap language.
     * @param {Boolean} [config.multiple]=false `true` to allow selecting multiple pages.
     * @param {Boolean} [config.allowCreation=false] `true` to allow the creation of new pages from the dialog box.
     * @param {Boolean} [config.allowDeletion=false] `true` to allow to delete current selection
     * @param {Boolean} [config.showSelectDecorators=true] `true` to display a button to allow to the user to select the decorators to display 
     * @param {Function} config.callback The callback function invoked when pages are selected. The callback function will received the following parameters:
     * @param {String[]/String} config.callback.ids The identifiers of the selected pages. In simple mode, the value is a string, in multiple mode it is an array. If the selection had been deleted, null or [] is returned. If the dialog has been closed, undefined is returned.
     * @param {String} config.callback.title In simple mode, the title of the page. In multiple mode, nothing is returned
     * @param {String} config.callback.path In simple mode, the title of the path. In multiple mode, nothing is returned
     */
    open: function (config)
    {
        config = config || {};
        
        box = this._createDialogBox({
            icon: config.icon,
            iconCls: config.iconCls,
            title: config.title,
            helpMessage: config.helpMessage,
            siteContext: config.siteContext || Ametys.web.helper.ContextToolbar.SITE_CONTEXT_CURRENT,
            sitemapContext: config.sitemapContext || Ametys.web.helper.ContextToolbar.SITEMAP_CONTEXT_ALL,
            defaultSitemapName: config.defaultSitemapName,
            defaultSiteName: config.defaultSiteName || Ametys.getAppParameter('siteName'),
            multiple: config.multiple || false,
            callback: config.callback,
            values: config.values || [],
            allowCreation: config.allowCreation || false,
            allowDeletion: config.allowDeletion || false,
            showSelectDecorators: config.showSelectDecorators,
            rightId: config.rightId || 'Web_Rights_Page_Create'
        });
        
        box.show();
    },
    
    /**
     * Creates the dialog box
     * @param {Object} config The configuration options :
     * @param {String} [config.icon] The full path to icon (16x16 pixels) for the dialog box.
     * @param {String} [config.iconCls] The CSS class for icon of the dialog box
     * @param {String} [config.title] The title of the dialog box.
     * @param {String} [config.helpMessage] The help message to display on top of dialog box.
     * @param {String} [config.values] the selected pages as an array of id.
     * @param {String} [config.siteContext=Ametys.web.helper.ContextToolbar#SITE_CONTEXT_CURRENT] The site context. Set to Ametys.web.helper.ContextToolbar#SITE_CONTEXT_ALL to see all sites.
     * @param {String} [config.sitemapContext=Ametys.web.helper.ContextToolbar#SITEMAP_CONTEXT_ALL] The sitemap context. Set to Ametys.web.helper.ContextToolbar#SITEMAP_CONTEXT_ALL to see all languages. Set to a language code such as 'fr', 'en', .. for using a specifying language.
     * @param {String} [config.defaultSiteName] The default site name.
     * @param {String} [config.defaultSitemapName] The default sitemap language.
     * @param {Boolean} [config.multiple]=false `true` to allow selecting multiple pages.
     * @param {Boolean} [config.allowCreation=false] `true` to allow the creation of new pages from the dialog box.
     * @param {Boolean} [config.allowDeletion=false] `true` to allow to delete current selection
     * @param {Boolean} [config.showSelectDecorators=true] `true` to display a button to allow to the user to select the decorators to display 
     * @param {Function} config.callback The callback function invoked when pages are selected. The callback function will received the following parameters:
     * @param {String[]/String} config.callback.ids The identifiers of the selected pages. In simple mode, the value is a string, in multiple mode it is an array
     * @param {String} config.callback.title In simple mode, the title of the page. In multiple mode, nothing is returned
     * @param {String} config.callback.path In simple mode, the title of the path. In multiple mode, nothing is returned
     * @private
     */
    _createDialogBox: function (config)
    {
        var self = this;
        var tree = Ext.create('Ametys.web.sitemap.SitemapTree', {
            border: true,
            flex: 1,
            
        	siteContext: config.siteContext,
        	sitemapContext: config.sitemapContext,
        	defaultSitemapName: config.defaultSitemapName,
		    defaultSiteName: config.defaultSiteName,
        		    
            allowCreation: config.allowCreation,
            showSelectDecorators: config.showSelectDecorators,
            
            checkMode: config.multiple
        });
        
        tree.getView().cellFocused = false;
        
        var box = Ext.create('Ametys.window.DialogBox', {
            title: config.title || "{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_TITLE}}",
            icon: config.icon,
            iconCls: config.icon ? null : config.iconCls || 'ametysicon-website38',
            width: 420,
            height: 500,
            scrollable: false,
            
            bodyStyle: {
            	padding: 0
            },
            cls: 'ametys-dialogbox choose-page-dialog',
            
            sitemapConfig : {
	            multiple: config.multiple,
	            allowCreation: config.allowCreation,
	            callback: config.callback,
	            values: config.values,
	            rightId: config.rightId
	        },
        
            layout: {
                type: 'vbox',
                align : 'stretch',
                pack  : 'start'
            },
            
            items: [{
	                xtype: 'component',
	                cls: 'a-text',
                    padding: '5',
	                html: config.helpMessage || (config.multiple ? "{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_SELECT_PAGES_HELP_MESSAGE}}" : "{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_SELECT_PAGE_HELP_MESSAGE}}")
	            },
	            tree,
	            {
	                xtype: 'component',
	                cls: 'a-text link',
	                html: "{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_NO_PAGE_SELECTED}}",
	                hidden: !config.allowCreation
	            }
	        ],
            
            _clickEventRegistered: false,
            
            referenceHolder: true,
            defaultButton: 'validate',
            
            closeAction: 'destroy',
            buttons : [{
            		reference: 'validate',
            		itemId: 'ok-btn',
            		disabled: true,
                    text :"{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_BUTTON_OK}}"
                }, {
            		itemId: 'delete-btn',
            		hidden: !config.allowDeletion || config.values.length == 0,
                    text :"{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_BUTTON_DELETE}}"
                }, {
                    itemId: 'cancel-btn',
                    text :"{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_BUTTON_CANCEL}}"
                } 
            ]
        });
        
        box.addListener("close", this._close, this, [box]);
        box.down("button[itemId='ok-btn']").setHandler (Ext.bind(this._validate, this, [box]));
        box.down("button[itemId='delete-btn']").setHandler (Ext.bind(this._delete, this, [box]));
        box.down("button[itemId='cancel-btn']").setHandler (Ext.bind(this._cancel, this, [box]));
        
        tree.on('selectionchange', Ext.bind(this._onSelectPage, this, [box], 2));
        tree.initialize(Ext.bind (this._initValues, this, [box, config.values]));
        
        return box;
    },
    
    /**
     * @private
     * Initialize selected pages
     * @param {Ametys.window.DialogBox} box the dialog box
     * @param {String[]} values the page ids 
     */
    _initValues: function (box, values)
    {
    	var tree = box.items.get(1);
    	if (values && values.length > 0)
    	{
    		Ametys.data.ServerComm.callMethod({
    			role: "org.ametys.web.repository.page.PageDAO",
    			methodName: "getPagesInfos",
    			parameters: [values],
    			callback: {
    				scope: this,
    				handler: this._getPagesInfosCb,
                    arguments: {
                        box: box
                    }
    			},
    			errorMessage: {
    				category: this.self.getName(),
    				msg: "{{i18n PLUGINS_WEB_UITOOL_SITEMAP_ERROR}}"
    			}
    		});
    	}
    	
    	if (values.length == 0 || this.multiple)
    	{
    		var rootNode = tree.getRootNode();
    		tree.getSelectionModel().select(rootNode);
        	tree.getView().focusNode(rootNode);
    	}
    },
    
    /**
     * @private
     * Callback after retrieving pages' properties. Select pages in tree.
     * @param {Object} response The server response
     * @param {Object} args The arguments transmitted
     * @param {Ametys.window.DialogBox} args.box The dialog box
     */
    _getPagesInfosCb: function (response, args)
    {
    	var pages = response.pages;
    	
    	var paths = [];
    	var box = args.box;
    	var tree = box.items.get(1);
        
    	Ext.Array.each (pages, function (page) {
    		if (page.siteName == tree.getCurrentSiteName() && page.lang == tree.getCurrentSitemapName())
        	{
    			tree.expandNodeByPath(page.path, function (successful, node) {
    				if (successful)
    				{
                    	if (box.sitemapConfig.multiple)
                		{
                    		node.set('checked', true);
                		}
                    	else
                		{
                    		tree.getSelectionModel().select(node);
                    		tree.getView().focusNode(node);
                		}
    				}
    			})
        	}
    	});
    },
    
    /**
     * This method update the dialog box depending on the current selection 
     * @private
     */
    _onSelectPage: function(sm, nodes, box)
    {
    	var isValidSelection = this._isSelectionValid(nodes);
    	box.down("button[itemId='ok-btn']").setDisabled(!isValidSelection);
        
    	if (box.sitemapConfig.allowCreation)
    	{
    		this._updateLinkCreation(box);
    	}
    },
    
    /**
     * Check if selection is valid according to the configuration parameters
     * @param {Ext.data.TreeModel} nodes The selected nodes.
     * @return {boolean} true if the selection is valid
     * @private
     */
    _isSelectionValid : function(nodes)
    {
        if (nodes.length == 0)
        {
        	return false;
        }
        
        for (var i=0; i <nodes.length; i++)
        {
        	if (nodes[i].isRoot())
        	{
        		return false;
        	}
        }
        
        return true;
    },
    
    /**
     * This method retrieves the current selection, and set the tag creation link accordingly. 
     * @private
     */
    _updateLinkCreation: function (box, selection)
    {
        var tree = box.items.get(1);
    	var selection = tree.getSelectionModel().getSelection();
        
    	var createLink = box.child("component[cls~=link]");
    	
    	var node = selection[0];
        if (node != null)
        {
            this._checkUserRight(box, node);
        }
        else
        {
        	// No page is selected
        	box._clickEventRegistered = false;
        	createLink.update("{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_NO_PAGE_SELECTED}}");
        }
    },
    
    /**
     * @private
     * Check if user has the specified right on page node
     * @param {Ametys.window.DialogBox} box The current dialog box
     * @param node The page node
     */
    _checkUserRight: function (box, node)
    {
        var me = this,
        	tree = box.items.get(1),
        	pageId = node.getId(),
        	rightId = box.sitemapConfig.rightId,
        	createLink = box.child("component[cls~=link]");
        
        function callback (hasRight)
        {
        	if (hasRight)
            {
            	createLink.update("<a class='action'>{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_CREATE_PAGE_LINK}}</a>");     
            	// add a click event listener on the <a class='action'> dom node to call the #_createPage method.
                if (!box._clickEventRegistered)
                {
                	createLink.mon(createLink.getEl(), 'click', Ext.bind(me._createPage, me, [box]), me, {delegate: 'a.action'});
                    box._clickEventRegistered = true;
                }
            }
            else
            {
            	// No right to create page
            	box._clickEventRegistered = false;
            	createLink.update("{{i18n PLUGINS_WEB_HELPER_CHOOSEPAGE_CREATE_PAGE_NORIGHT}}");
            }
        }
        
        Ametys.web.page.PageDAO.hasRight([pageId, rightId], callback, {scope: this});
    },
    
    /**
     * Retrieve the current tree values, and call the callback function from the initial configuration sent to #open
     * @param {Ametys.window.DialogBox} box The current dialog box
     * @private
     */
    _validate: function (box)
    {
        var selection, ids;
        var tree = box.items.get(1);
        
        if (box.sitemapConfig.multiple)
        {
            selection = tree.getChecked();
            ids = Ext.Array.map(selection, function(record) {
                return record.getId();
            });
        }
        else
        {
            selection = tree.getSelectionModel().getSelection();
        }
        
        if (box.sitemapConfig.callback)
        {
            if (box.sitemapConfig.multiple)
            {
                box.sitemapConfig.callback.call(this, ids);
            }
            else
            {
                var record = selection[0];
                box.sitemapConfig.callback.call(this, record.getId(), record.get('title'), record.get('path'));
            }
        }
        box.sitemapConfig.callback = null;
        box.close();
    },
    
    /**
     * Call the callback function with null argument (no selection)
     * @param {Ametys.window.DialogBox} box The current dialog box
     * @private
     */
    _delete: function (box)
    {
    	if (box.sitemapConfig.callback)
        {
    		if (box.sitemapConfig.multiple)
            {
                box.sitemapConfig.callback.call(this, []);
            }
            else
            {
                box.sitemapConfig.callback.call(this, null);
            }
        }
    	box.sitemapConfig.callback = null;
    	box.close();
    },
    
    /**
     * @private
     * Handler function invoked when the 'Cancel' button is clicked
     */
    _cancel: function (box)
    {
        box.close(); 
    },
    
    /**
     * @private
     * Handler function invoked when the 'Close' button is clicked
     */
    _close: function (box)
    {
        if (box.sitemapConfig.callback)
        {
            box.sitemapConfig.callback.call(this, undefined);
            box.sitemapConfig.callback = null;
        }
    },
    
    /**
     * Create a page from the dialog box
     * @param {Ametys.window.DialogBox} box The dialog box
     * @private
     */
    _createPage: function(box)
    {
        var tree = box.items.get(1);
        var selection = tree.getSelectionModel().getSelection();
        if (selection && selection.length > 0)
        {
        	var parentId = selection[0].get('id');
        	Ametys.plugins.web.page.AddPageWizard.open (parentId, Ext.bind(this._createPageCb, this, [tree], true), {});
        }
    },
    
    /**
     * @private
     * Callback function called after creating page
     * @param {Ametys.web.page.Page} page The created page
     * @param {Boolean} success creation is successfull
     * @param {Ext.tree.Panel} tree The sitemap tree panel
    */
    _createPageCb: function (page, success, tree)
    {
    	var node = tree.getStore().getNodeById(page.getParentId());
		if (node != null)
		{
			node.set('expanded', true);
			tree.getStore().load({node: node, callback: Ext.bind (this._createPageCb2, this, [tree, page.getId()])});
		}
    },
    
    /**
     * @private
     * Select the new created page
     * @param {Ext.tree.Panel} tree The sitemap tree panel
     * @param {String} pageId The page id to select
     */
    _createPageCb2: function (tree, pageId)
    {
    	var node = tree.getStore().getNodeById(pageId);
    	if (node != null)
    	{
    		tree.getSelectionModel().select(node);
    	}
    },
    
    /**
     * Callback function after page creation
     * @param {Ext.tree.Panel} tree The sitemap tree panel
     * @param {String} pageId The page's id
     * @param {String} parentId The page's parent id
     * @private
     */
    _onPageCreated: function (tree, pageId, parentId)
    {
        var node = tree.getStore().getNodeById(parentId);
        if (node != null)
        {
            node.set('expanded', true);
            
            tree.getStore().load({
                node : node,
                callback : function()
                {
                    Ext.defer(tree.expandNode, 200, tree, [ node, false, null ]);
                },
                scope : this
            });
        }
    }
});