/*
 *  Copyright 2013 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 choose a resource from resources explorer. 
 * See #open method.
 */
Ext.define('Ametys.cms.uihelper.ChooseResource', {
	singleton: true,
	
	
	/**
	 * @property {String} _currentId The current selected resource. Can be null.
	 * @private
	 */
    /**
     * @property {Function} _filter The node filter. Can be null.
     * @private
     */
    /**
     * @property {Boolean} _ignoreFiles Property corresponding to the ignoreFiles config
     * @private
     */
    /**
     * @property {Boolean} _folderSelectable Property corresponding to the folderSelectable config
     * @private
     */
	/**
	 * @property {Ametys.window.DialogBox} _box The dialog box (containing the resource explorer tree).
	 * @private
	 */
	/**
	 * @property {Ametys.explorer.tree.ExplorerTree} _tree The resource explorer tree.
	 * @private
	 */
	/**
	 * @property {Function} _cbFn The call back function to call after a file has been chosen.
	 * @private
	 */
	
	/**
	 * Allow the user to choose a resource file 
	 * @param {Object} config The configuration options :
     * @param {String} [config.iconCls=ametysicon-file98] One or more CSS classes to apply to dialog's icon. Can be null to use the default one or use the icon instead.
	 * @param {String} [config.icon] The full path to icon (16x16) for the dialog box. If null the iconCls will be used
	 * @param {String} config.title The title of the dialog box.
	 * @param {String} config.helpmessage The message displayed at the top of the dialog box.
	 * @param {Function} config.callback The method that will be called when the dialog box is closed. The method can return false to cancel the closing action (you might display an error message in this case). Callback parameters are : 
	 * @param {String} config.callback.id The id of the file
	 * @param {String} config.callback.filename: The name of the file
	 * @param {String} config.callback.size The size in byte of the file
	 * @param {String} config.callback.viewHref The url to VIEW the file
	 * @param {String} config.callback.downloadHref The url to DOWNLOAD the file
	 * @param {Function} [config.filter] A node filter. Choose one in {@link Ametys.explorer.tree.ExplorerTree}. (One argument, the tree node and reply true or false)
	 * @param {String} [config.currentId] The selected id. Can be null.
	 * @param {Boolean} [config.ignoreFiles=false] True to restrict the tree to folders (ignore files).
	 * @param {Boolean} [config.folderSelectable=false] True to allow a folder to be selected
	 */
	open: function(config)
	{
		this._cbFn = config.callback || Ext.emptyFn;
		this._currentId = config.currentId || null;
		this._ignoreFiles = config.ignoreFiles === true; // false by default
		this._folderSelectable = config.folderSelectable === true; // false by default

		this._filter = config.filter;
        this._allowedExtensions = config.allowedExtensions;
        this._allowDragAndDrop = Ext.isDefined(config.allowDragAndDrop) ? config.allowDragAndDrop : true;
		this._createDialogBox(config.icon, config.iconCls, config.title, config.helpmessage);

		this._box.show();

        this._refreshMask = Ext.create("Ext.LoadMask", {msg: "{{i18n plugin.core-ui:PLUGINS_CORE_UI_MSG_TOOLS_REFRESHING}}", target: this._tree});
        this._refreshMask.show();
	},
	
	/**
	 * Creates the dialog box. The box is destroyed on close action
	 * @param {String} icon The full path to icon (16x16 pixels) for the dialog box. Can be null to use CSS for icon
     * @param {String} iconCls One or more CSS classes to apply to dialog's icon. Can be null to use the default one or use the icon instead.
	 * @param {String} title The title of the dialog box.
	 * @param {String} helpmessage The message displayed at the top of the dialog box.
	 * @private
	 */
	_createDialogBox: function(icon, iconCls, title, helpmessage)
	{
		this._tree = Ext.create('Ametys.explorer.tree.ExplorerTree', {
            flex: 1,
			itemId: 'resources-tree',
			inlineEditionEnable: false,
            allowDragAndDrop: this._allowDragAndDrop,
			border: true,
			filter: this._filter,
			ignoreFiles: this._ignoreFiles,
            allowedExtensions: this._allowedExtensions,
			height: 290,
			listeners: {
				selectionchange: {fn: this._onSelectionChange, scope: this},
				filterupdated: {fn: this._onFilterUpdated, scope: this},
                beforerender: {fn: this._setRootNodes, scope: this},
                rootnodeschanged: {fn: this._onRootNodesChanged, scope: this}
			}
		});
		
		this._box = Ext.create('Ametys.window.DialogBox', {
			title: title,
            icon: icon,
			iconCls: icon ? null : (iconCls || 'ametysicon-file98'),
			
			width: 410,
			scrollable: false,
			
            layout: {
                type: 'vbox',
                align: 'stretch'
            },
                
			items: [{
					xtype: 'component',
					cls: 'a-text',
					html: helpmessage
				}, 
				this._tree,
				{
					xtype: 'component',
					cls: 'a-text',
					html: !this._ignoreFiles ? "{{i18n PLUGINS_CMS_HELPER_CHOOSERESOURCESFILES_SELECT_FOLDER}}" : ''
				}
			],
			
			closeAction: 'destroy',
			
			referenceHolder: true,
			defaultButton: 'validate',
			
			buttons : [{
				reference: 'validate',
				text: "{{i18n PLUGINS_CMS_HELPER_CHOOSERESOURCESFILES_OKBUTTON}}",
				disabled: true,
				handler: Ext.bind(this._ok, this)
			}, {
				text: "{{i18n PLUGINS_CMS_HELPER_CHOOSERESOURCESFILES_CANCELBUTTON}}",
				handler: Ext.bind(function() {this._box.close();}, this) // hide
			}],
			
			listeners: {
                show: {fn: this._init, scope: this},
                close: {fn: this._close, scope: this}//for callback on close
			}
		});
		
		this._initialized = true;
	},

    /**
     * @private
     * When dialog is closed
     */
    _close: function()
    {
        this._cbFn(null);
    },
	
	/**
	 * Set the root nodes for the resources explorer tree
	 * @returns
	 */
	_setRootNodes: function()
	{
		Ametys.explorer.ExplorerNodeDAO.getRootNodesInfo(this._setRootNodesCb, this);
	},
	
	/**
	 * @private
	 * Callback of #_setRootNodes
	 * @param {Object[]} response the server response
	 */
	_setRootNodesCb: function(response)
	{
		// The response should be the root node config object or an array of root node config objects.
        if (response)
        {
            this._tree.setRootNodes(Ext.Array.from(response));

        }

        this._refreshMask.hide();
        Ext.destroy(this._refreshMask);
        this._refreshMask = null;
	},
	
	/**
	 * @private
	 * Listener when root nodes are loaded in the tree
	 * @param {Ametys.explorer.tree.ExplorerTree} tree The explorer tree
	 */
	_onRootNodesChanged: function(tree)
	{
		this._tree.updateFilter(this._filter, this._allowedExtensions);
	},
	
	/**
	 * This function is called after the dialog box is shown.
	 * Reload the tree.
	 * @param {Ametys.window.DialogBox} box The box
	 * @private
	 */
	_init: function()
	{
		this._tree.updateFilter(this._filter, this._allowedExtensions);
	},
	
	/**
	 * Initialize the dialog box.
	 * @param {Ametys.explorer.tree.ExplorerTree} tree the tree.
	 * @private
	 */
	_onFilterUpdated: function (tree)
	{
		this._selectCurrentNode();
	},
	
	/**
	 * @private
	 * Select the first node of the tree or the node/resource if not null. 
	 */
	_selectCurrentNode: function()
	{
		if (!this._currentId)
		{
			var firstChild = this._tree.getRootNode().firstChild;
			if (firstChild)
			{
				this._tree.getSelectionModel().select(firstChild);
				this._tree.getView().focusNode(firstChild);
			}
		}
		else
		{
			var me = this;
			Ametys.explorer.ExplorerNodeDAO.getExplorerNode(this._currentId, function(node) {
				if (node)
				{
					me._tree.selectByPath(node.getPathPrefix() + node.getPath(), true);
				}
			});
		}
	},
	
	/**
	 * @private
	 * 'ok' button click handler.
	 * Calls the callback function passed in {@link #method-open} and hide the dialog box.
	 */
	_ok: function()
	{
		var node = this._tree.getSelectionModel().getSelection()[0];
		
		var id = node.getId();
		var text = node.get('text');
		
		var size, viewHref, downloadHref;
		if (node.get('type') == Ametys.explorer.tree.ExplorerTree.RESOURCE_TYPE)
		{
		    size = node.get('size');
		    viewHref = Ametys.explorer.tree.ExplorerTree.getViewHref(id); 
		    downloadHref = Ametys.explorer.tree.ExplorerTree.getDownloadHref(id);
		}
		
        if (this._cbFn(id, text, size, viewHref, downloadHref, null) !== false)
        {
            this._box.hide();
        }
        
	},
	
	/**
	 * @private
	 * Listener on selection change in the tree.
	 */
	_onSelectionChange: function(sm, nodes)
	{
		var node = nodes[0],
		    insertMsg = this._box.items.getAt(2),
		    okBtn = this._box.getDockedItems('toolbar[dock="bottom"] button')[0];
		
		if (node == null || node.isRoot())
	    {
		    okBtn.setDisabled(true);
	        insertMsg.update(!this._ignoreFiles ? "{{i18n PLUGINS_CMS_HELPER_CHOOSERESOURCESFILES_SELECT_FOLDER}}" : '');
	    }
		else
	    {
		    var type = node.get('type'),
		        isResource = type == Ametys.explorer.tree.ExplorerTree.RESOURCE_TYPE,
		        isCollection = type == Ametys.explorer.tree.ExplorerTree.COLLECTION_TYPE,
		        enabled = isResource || (this._folderSelectable && isCollection);
		    
            okBtn.setDisabled(!enabled);
                
		    if (!this._ignoreFiles)
	        {
		    	this._checkUserRight(isResource ? node.parentNode : node);
	        }
		    else
	        {
	            insertMsg.update('');
	        }
	    }
	},
	
	/**
     * @private
     * Check if user has the specified right on explorer node
     * @param node The page node
     */
    _checkUserRight: function (node)
    {
        var me = this,
        	tree = me._tree,
        	nodeId = node.getId(),
        	rightId = 'Plugin_Explorer_File_Add',
        	createLink = me._box.items.getAt(2);
        
        function callback (hasRight)
        {
        	if (hasRight)
            {
            	createLink.update("<a class='action'>{{i18n PLUGINS_CMS_HELPER_CHOOSERESOURCESFILES_MESSAGE}}</a>");     
            	// add a click event listener on the <a class='action'> dom node to call the #_uploadFile method.
                if (!me._box._clickEventRegistered)
                {
                	createLink.mon(createLink.getEl(), 'click', me._uploadFile, me, {delegate: 'a.action'});
                	me._box._clickEventRegistered = true;
                }
            }
            else
            {
            	// No right to create resource
            	me._box._clickEventRegistered = false;
            	createLink.update("{{i18n PLUGINS_CMS_HELPER_CHOOSERESOURCESFILES_ADD_NO_RIGHT}}");
            }
        }
        
        Ametys.explorer.ExplorerNodeDAO.hasRight([nodeId, rightId], callback, {scope: this});
    },

	/**
	 * @private
	 * When the user upload a file now
	 */
	_uploadFile: function()
	{
		var parentId = this._tree.getRootIds()[0]; // Root resources by default
		
		var node = this._tree.getSelectionModel().getSelection()[0];
		if (node)
		{
			if (node.get('type') == Ametys.explorer.tree.ExplorerTree.RESOURCE_TYPE)
			{
				parentId = node.parentNode ? node.parentNode.getId() : parentId;
			}
			else
			{
				parentId = node.getId();
			}
		}
		
		Ametys.explorer.resources.actions.File.add(parentId, this._uploadFileCb, this);
	},
	
	/**
	 * @private
	 * {@link #_uploadFile} callback.
     * @param {Object[]} files The files
     * @param {String} files.id The id of the new resource
     * @param {String} files.name The name of the new resource
     * @param {Boolean} files.reload true if a reload is needed.
	 * @param {String} parentId The id of parent folder
	 * @param {Boolean} reload true if a reload is needed.
     * @param {Boolean} unzip true if the uploaded file was unzipped.
	 * Store and filter management.
	 */
	_uploadFileCb: function(files, parentId, reload, unzip)
	{
        var multipleFileUploaded = unzip || files.length > 1;
        var id = files.length == 1 ? files[0].id : null;
		// Reload the parent node
		this._tree.refreshNode(parentId, Ext.bind(this._uploadFileCb2, this, [id, multipleFileUploaded]));
	},

	/**
	 * @private
	 * {@link #_uploadFileCb} callback.
     * @param {String} id The id of the new resource
     * @param {Boolean} multipleFileUploaded true if there are multiple files uploaded (unzip or multiple import).
	 */
	_uploadFileCb2: function(id, multipleFileUploaded)
	{
        if (multipleFileUploaded)
        {
            // Do nothing: let user choose his file among unzipped files.
            return;
        }
        
		var node = this._tree.getStore().getNodeById(id);
		if (node)
		{
			this._tree.selectNode(node);
			this._ok();
		}
		else
		{
			Ametys.Msg.alert(
				"{{i18n PLUGINS_CMS_HELPER_CHOOSERESOURCESFILES_ERRORFILETYPE_TITLE}}",
				"{{i18n PLUGINS_CMS_HELPER_CHOOSERESOURCESFILES_ERRORFILETYPE_DESCRIPTION}}"
			);
		}
	}
});