/*
 *  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 tool does allow the user to perform searches on resources.
 * @private
 */
Ext.define('Ametys.plugins.explorer.applications.resources.ExplorerSearchTool', {
	extend: 'Ametys.plugins.explorer.applications.resources.ResourceCollectionTool',
	
	inheritableStatics: {
		/**
		 * @property {String} INTERNAL_PREFIX an internal prefix
		 * @private
		 * @readonly
		 */
		INTERNAL_PREFIX: 'search-app-'
	},
	
    statics: {
        /**
         * @property {Number}
         * @readonly
         * @static
         * The max height of search form
         */
        FORM_MAX_HEIGHT: 300,
        
        /**
         * @property {Number}
         * @readonly
         * @static
         * The min height of search form
         */
        FORM_MIN_HEIGHT: 100,
        
        /**
         * @property {Number}
         * @readonly
         * @static
         * The default ratio of form's height
         */
        FORM_HEIGHT_RATIO: 1/3,
        
        /**
         * @property {Number}
         * @readonly
         * @static
         * The max ratio of form's height
         */
        FORM_MAX_HEIGHT_RATIO: 2/5
    },
    
	/**
	 * @private
	 * @property {Ext.Template} _hintTpl The template for hint
	 */
	
    constructor: function(config)
    {
        this.callParent(arguments);
        
        this._searchPlugin = this.getInitialConfig('search-plugin') || this.getPluginName(); 
        this._searchUrl = this.getInitialConfig('search-url') || 'resource/search';
        
        this._hintTpl = this.getHintTemplate();
    },
    
	createPanel: function() 
	{
		this._store = this._createStore();
		this._thumbnailView = this._createThumbnailView();
		this._detailsView = this._createDetailsView();
		
		this.searchPanel = this._createFormPanel();
		
        this.cardView = Ext.create ('Ext.Panel', {
            split: true,
            xtype: 'panel',
            flex: 1,
            border: false,
            layout: 'card',
            activeItem : 0,
            
            items: [this._detailsView, this._thumbnailView]
        });
        
        var me = this;
        this.mainPanel = Ext.create('Ext.panel.Panel', {
            layout: {
                type: 'vbox',
                align: 'stretch'
            },
            border: false,
            
            dockedItems: [{
                dock: 'top',
	            xtype: 'component',
	            ui: 'tool-hintmessage',
	            itemId: 'folder-hint-message',
                html: ''
            }],
            
            items: [ 
                    this.searchPanel, 
                    this.cardView
            ],
            
            listeners: {
                'resize': function (p, width, height)
                {
                    // Set the max height of form panel
                    var maxHeight = Math.min(Ametys.plugins.explorer.applications.resources.ExplorerSearchTool.FORM_MAX_HEIGHT, parseInt(height * Ametys.plugins.explorer.applications.resources.ExplorerSearchTool.FORM_MAX_HEIGHT_RATIO));
                    me.searchPanel.maxHeight = maxHeight;
                    
                    // Try to keep the current ratio if exists
                    if (me.searchPanel._heightRatio)
                    {
                        me.searchPanel.setHeight(me.searchPanel._heightRatio * height);
                    }
                }
            }
        });
        
        return this.mainPanel;
	},
	
	/**
	 * Create the form panel that holds the search criteria
	 * @return {Ext.form.Panel} The form panel
	 */
	_createFormPanel: function()
	{
        var formCfg =  {
            scrollable:  true,
            
            fieldsetLayout: {
                type: 'column'
            },
            
            defaultFieldConfig: {
                labelWidth: 120,
                width: 350/*,
                margin: '0 10 4 0'*/
            },
            
            additionalWidgetsConfFromParams: {
                contentType: 'contentType' // some widgets requires the contentType configuration  
            },
            
            withTitleOnLabels: true,
        };
        
        this.form = Ext.create ('Ametys.form.ConfigurableFormPanel', formCfg);
        this.form.configure(this._getSearchCriteria());
        this.form.setValues({});
        
        var me = this;
        return Ext.create ('Ext.Panel', {
            split: true,
            
            border: false,
            
            ui: 'light',
            header: {
                titlePosition: 1
            },
            
            stateful: true,
            stateId: this.getId() + '$explorer-search-form',
            
            title: "{{i18n PLUGINS_EXPLORER_UITOOL_SEARCH_CRITERIA}}",
            collapsible: true,
            titleCollapse: true,
            animCollapse: false,
            
            minHeight: Ametys.plugins.explorer.applications.resources.ExplorerSearchTool.FORM_MIN_HEIGHT,
            maxHeight: Ametys.plugins.explorer.applications.resources.ExplorerSearchTool.FORM_MAX_HEIGHT,
            
            items: [ this.form ],
            
            bbar: this._getSearchFormPanelBBar(),
            
            listeners: {
                'resize': function (cmp, width, height)
                {
                    if (this._oldHeight && height != this._oldHeight)
                    {
                        // The panel was manually resized: save new ratio
                        this._oldHeight = height;
                        this._heightRatio = height / me.mainPanel.getHeight();
                    }
                }
            },
            
            applyState: function (state)
            {
                this._heightRatio = state.ratio;
            },
            
            getState: function ()
            {
                // Save the height ratio
                return {
                    ratio: this._heightRatio
                }
            }
        });
	},
    
    /**
     * Get the JSON configuration for search criteria
     * @return The search criteria
     */
    _getSearchCriteria: function ()
    {
        return {
            "fieldset" : {
                "role": "fieldset",
                "elements": {
                    "name" : {
                        label: "{{i18n PLUGINS_EXPLORER_SEARCH_NAME}}",
                        description: "{{i18n PLUGINS_EXPLORER_SEARCH_NAME_DESC}}",
                        type: 'string'
                    },
                    "fulltext" : {
                        label: "{{i18n PLUGINS_EXPLORER_SEARCH_TEXTFIELD}}",
                        description: "{{i18n PLUGINS_EXPLORER_SEARCH_TEXTFIELD_DESC}}",
                        type: 'string'
                    },
                    "author" : {
                        label: "{{i18n PLUGINS_EXPLORER_SEARCH_AUTHOR}}",
                        description: "{{i18n PLUGINS_EXPLORER_SEARCH_AUTHOR_DESC}}",
                        type: 'user'
                    },
                    "keywords" : {
		                label: "{{i18n PLUGINS_EXPLORER_SEARCH_KEYWORDS}}",
		                description: "{{i18n PLUGINS_EXPLORER_SEARCH_KEYWORDS_DESC}}",
                        type: 'string'
		            }
                }
            }
        }
    },
	
	/**
	 * @protected
	 * Get the items of the optional bottom bar of the criteria panel
	 * @return {Array} The bottom bar configuration array
	 */
	_getSearchFormPanelBBar: function()
	{
		var items = [{
			// Search
			itemId: 'search',
			text: "{{i18n PLUGINS_EXPLORER_SEARCH_BUTTON_SEARCH}}",
			icon: Ametys.getPluginResourcesPrefix('explorer') + '/img/search/icon_small.png',
			handler: this._launchSearch,
			scope: this,
			tooltip: {
				title: "{{i18n PLUGINS_EXPLORER_SEARCH_BUTTON_SEARCH}}",
				text: "{{i18n PLUGINS_EXPLORER_SEARCH_BUTTON_SEARCH_DESC}}",
				image: Ametys.getPluginResourcesPrefix('explorer') + '/img/search/icon_large.png',
				inribbon: false
			}
		}, 
		'-',
		{
			// Cancel
			itemId: 'cancel',
			text: "{{i18n PLUGINS_EXPLORER_SEARCH_BUTTON_CANCEL}}",
			icon: Ametys.getPluginResourcesPrefix('explorer') + '/img/search/cancel_16.png',
			handler: this._stopSearch,
			scope: this,
			disabled: true,
			tooltip: {
				title: "{{i18n PLUGINS_EXPLORER_SEARCH_BUTTON_CANCEL}}",
				text: "{{i18n PLUGINS_EXPLORER_SEARCH_BUTTON_CANCEL_DESC}}",
				image: Ametys.getPluginResourcesPrefix('explorer') + '/img/search/cancel_48.png',
				inribbon: false
			}
		}];
		
		return items;
	},
	
	/**
	 * Launch the search via the launch button
	 * @private
	 */
	_launchSearch: function()
	{
		this._load();
	},
	
	/**
	 * @inheritdoc
	 */
	_createStore: function()
	{
		return Ext.create('Ext.data.Store', {
			autoDestroy: true,
			model: 'Ametys.plugins.explorer.applications.resources.ResourcesApplication.ResourceEntry',
			proxy: {
				type: 'ametys',
				plugin: this._searchPlugin,
				url: this._searchUrl,
				reader: {
					type: 'xml',
					record: '> Node'
				}
			},
			listeners: {
				load: {fn: this._onLoad, scope: this}
			},
			sorters: [{property: 'name'}]
		});
	},
	
	/**
	 * @inheritdoc
	 */
	_createDetailsView: function ()
	{
		return Ext.create('Ametys.plugins.explorer.view.SearchDetailsViewer', {
			itemId : this.self.INTERNAL_PREFIX + 'resources-details-view',
			
			stateful: true,
			stateId: this.getId() + "$grid",
			
			store : this.getStore(),
			border: false,
			columns: this._getColumns(),
			
			listeners: {
				activate: {fn: this._onViewActivate, scope: this},
				selectionchange: {fn: this._onSelectFiles, scope: this},
				itemdblclick: {fn: this._onDblClick, scope: this}
			}
		});
	},
	
	/**
	 * @protected
	 * Create the columns configuration array.
	 * @return {Object[]} An array of column definition object. See {@link Ext.grid.column.Column}
	 */
	_getColumns: function()
	{
		return [
			{text: "{{i18n UITOOL_EXPLORER_APP_RESOURCES_COL_NAME}}", stateId: 'name', width: 250, dataIndex: 'name', hideable: false, renderer: this._renderFilename, scope: this,
				editor: {
					xtype: 'textfield',
					allowBlank: false,
					selectOnFocus: true
				}
			},
			{text: "{{i18n UITOOL_EXPLORER_APP_RESOURCES_COL_KEYWORDS}}", stateId: 'keywords', width: 100, dataIndex: 'keywords', hidden: true},
			{text: "{{i18n UITOOL_EXPLORER_APP_RESOURCES_COL_LOCATION}}", stateId: 'path', width: 150, dataIndex: 'path',  renderer: this._renderFilePath, scope: this, hidden: false},
			{text: "{{i18n UITOOL_EXPLORER_APP_RESOURCES_COL_DATE}}", stateId: 'lastModified', width: 130, dataIndex: 'lastModified', renderer: Ext.util.Format.dateRenderer('d/m/Y H:i')},
			{text: "{{i18n UITOOL_EXPLORER_APP_RESOURCES_COL_AUTHOR}}", stateId: 'author', width: 200, dataIndex: 'author'},
			{text: "{{i18n UITOOL_EXPLORER_APP_RESOURCES_COL_SIZE}}", stateId: 'size', width: 90, dataIndex: 'size', renderer: Ext.util.Format.fileSize, align: 'right'}
		];
	},
	
	/**
	 * @inheritdoc
	 * @param {Object} params The new parameters. Depends on the tool implementation.
	 */
	setParams: function (params)
	{
		if (!this._parentPath || this._parentPath != params.path)
		{
			this._parentPath = params.path;
			this._rootId = params.rootId;
		}
		
		this.callParent(arguments);
	},
	
	/**
	 * The first auto load
	 */
	_initLoad: function()
	{
		// no auto load
	},
	
	sendCurrentSelection: function()
	{
		var targets = [];
		
		var selection = this._getSelection();
		if (selection.length > 0)
		{
			ids = Ext.Array.map(selection, function(resource) {
				return resource.getId();
			});
			
			targets = {
				id: Ametys.message.MessageTarget.RESOURCE,
				parameters: { ids: ids }
			};
		}
		
		Ext.create("Ametys.message.Message", {
			type: Ametys.message.Message.SELECTION_CHANGED,
			targets: targets
		});
	},
	
	/**
	 * Load and displays the file in the grid
	 * @param {Object} [loadParams] the load params
	 */
	_load: function(loadParams)
	{
		var form = this.form.getForm();
		if (form.isValid())
		{
		    var values = Ext.apply(form.getValues() || {}, {
                rootId: this._rootId
            })
		    
			loadParams = Ext.apply(loadParams || {}, {
				params: values
			});
			
			this.showUpToDate();
			this._setGridDisabled(true);
			
			this.callParent([loadParams]);
			
			this._message = Ametys.data.ServerComm._messages[Ametys.data.ServerComm._messages.length - 1];
		}
	},
	
	/**
	 * Stop the search in progress
	 * @private
	 */
	_stopSearch: function()
	{
		if (this._message)
		{
			this._message.cancelCode = this.getId() + '$cancelCode';
			this._message.uniqueId = this.getId() + '$' + Ext.id();
			this._message.cancelled = true;
			
			Ametys.data.ServerComm._lastUniqueIdForCancelCode[this._message.cancelCode] = '';
		}
		
		this._setGridDisabled(false);
	},
	
	/**
	 * Enable or disable the grid columns and paging toolbar
	 * @param {Boolean} disabled true to disable.
	 * @private
	 */
	_setGridDisabled: function (disabled)
	{
		this.searchPanel.getDockedItems("toolbar")[0].items.get(0).setDisabled(disabled);
		this.searchPanel.getDockedItems("toolbar")[0].items.get(2).setDisabled(!disabled);
		
		var columns = this._detailsView.getColumns();
		Ext.Array.each (columns, function (column) {
			column.setDisabled(disabled);
		})
	},
	
	/**
	 * @private
	 * Listener called after store is loaded
	 * @param {Ext.data.Store} store The store
	 * @param {Ext.data.Model[]} records An array of records
	 * @param {Boolean} successful True if the operation was successful.
	 */
	_onLoad: function(store, records, successful)
	{
		this._message = null;
		this._setGridDisabled(false);
	},
	
	/**
	 * Update tool information
	 * @protected
	 */
	_updateInfos: function()
	{
		var me = this;
		Ametys.explorer.ExplorerNodeDAO.getExplorerNode(this._parentId, function(explorerNode) {
			var data = explorerNode.getProperties();
			var name = me._getFolderName(data);
            
            me.setTitle(me.getInitialConfig('title') + " (" + name + ")");
                
            var html = me.getHintText(data);
            
            var hintPanel = me.getContentPanel().getDockedItems('#folder-hint-message')[0];
            if (hintPanel)
            {
                hintPanel.update(html);
            }
		});
	},
	
	/**
     * @protected
     * Get the template for hint
     * @return {Ext.Template} The template used for hint
     */
    getHintTemplate: function ()
	{
		return Ext.create('Ext.Template', Ext.String.format("{{i18n PLUGINS_EXPLORER_SEARCH_TOOL_HINT}}", '<strong>{name}</strong>'));
	},
	
	/**
	 * @protected
	 * Get the hint text
	 * @param {Object} data The folder data
	 */
	getHintText: function (data)
	{
		return this._hintTpl.applyTemplate({
            name: data.name
        });
	},
	
    /**
     * Get the folder's name
     * @param {Object} data The node's properties
     * @return The folder's name
     */
    _getFolderName: function(data)
    {
        if (this._rootId == data.id)
        {
            return "{{i18n plugin.explorer:PLUGINS_EXPLORER_ROOT_NODE}}";
        }
        
        return data.name;
    },
    
	/**
	 * Listener on {@link Ametys.message.Message#CREATED} message. 
	 * If the tool is concerned by the created object, the view will be refreshed
	 * @param {Ametys.message.Message} message The created message.
	 * @private
	 */
	_onResourceCreated: function (message)
	{
		if (this.isOutOfDate() != Ametys.tool.Tool.OOD_UPTODATE)
		{
			return;
		}
		
		var collectionTarget = message.getTarget(Ametys.message.MessageTarget.EXPLORER_COLLECTION);
		if (collectionTarget)
		{
			if (this._sameRootId(collectionTarget.getParameters().rootId) && Ext.String.startsWith(collectionTarget.getParameters().path, this._parentPath))
			{
				this.showOutOfDate();
				return;
			}
		}
			
		var resourceTargets = message.getTargets(Ametys.message.MessageTarget.RESOURCE);
		if (resourceTargets.length > 0)
		{
			for (var i = 0; i < resourceTargets.length; i++)
			{
				var resourceTarget = resourceTargets[i];
				if (this._sameRootId(resourceTarget.getParameters().rootId) && Ext.String.startsWith(resourceTarget.getParameters().path, this._parentPath))
				{
					this.showOutOfDate();
					return;
				}
			}
		}
	},
	
	/**
	 * Listener on {@link Ametys.message.Message#DELETED} message. 
	 * If the tool is concerned by the deleted object, the corresponding row will be removed
	 * @param {Ametys.message.Message} message The deleted message.
	 * @private
	 */
	_onResourceDeleted: function(message)
	{
		var resourceTargets = message.getTargets(Ametys.message.MessageTarget.RESOURCE);
		if (resourceTargets.length > 0)
		{
			var ids = [];
			
			for (var i = 0; i < resourceTargets.length; i++)
			{
				var resourceTarget = resourceTargets[i];
				if (this._sameRootId(resourceTarget.getParameters().rootId) && Ext.String.startsWith(resourceTarget.getParameters().path, this._parentPath))
				{
					ids.push(resourceTarget.getParameters().id);
				}
			}
			
			this._deleteEntries(ids);
		}
		
		if (this.isOutOfDate() != Ametys.tool.Tool.OOD_UPTODATE)
		{
			return;
		}
		
		var collectionTarget = message.getTarget(Ametys.message.MessageTarget.EXPLORER_COLLECTION);
		if (collectionTarget)
		{
			if (collectionTarget.getParameters().id == this._parentId)
			{
				// Async tool removal
				var me = this;
				setTimeout(function() {
					Ametys.tool.ToolsManager.removeTool(me);
				}, 0);
			}
			else
			{
				if (this._sameRootId(collectionTarget.getParameters().rootId) && Ext.String.startsWith(collectionTarget.getParameters().path, this._parentPath))
				{
					this.showOutOfDate();
					return;
				}
			}
		}
	},
	
	/**
	 * Listener on {@link Ametys.message.Message#MOVED} message. 
	 * If the tool is concerned by the moved object, the new parent node will be refreshed.
	 * @param {Ametys.message.Message} message The moved message.
	 * @private
	 */
	_onResourceMoved: function(message)
	{
		if (this.isOutOfDate() != Ametys.tool.Tool.OOD_UPTODATE)
		{
			return;
		}
		
		var collectionTarget = message.getTarget(Ametys.message.MessageTarget.EXPLORER_COLLECTION);
		if (collectionTarget)
		{
			if (this._sameRootId(collectionTarget.getParameters().rootId) && Ext.String.startsWith(collectionTarget.getParameters().path, this._parentPath))
			{
				this.showOutOfDate();
				return;
			}
		}
		
		var resourceTargets = message.getTargets(Ametys.message.MessageTarget.RESOURCE);
		if (resourceTargets.length > 0)
		{
			for (var i = 0; i < resourceTargets.length; i++)
			{
				var resourceTarget = resourceTargets[i];
				if (this._sameRootId(resourceTarget.getParameters().rootId) && Ext.String.startsWith(resourceTarget.getParameters().path, this._parentPath))
				{
					this.showOutOfDate();
					return;
				}
			}
		}
	},
	
	/**
	 * Listener on {@link Ametys.message.Message#MODIFIED} message. 
	 * If the tool is concerned by the modified object, the node of modified object will be updated.
	 * @param {Ametys.message.Message} message The modified message.
	 * @private
	 */
	_onResourceModified: function(message)
	{
		if (this.isOutOfDate() != Ametys.tool.Tool.OOD_UPTODATE)
		{
			return;
		}
		
		var collectionTarget = message.getTarget(Ametys.message.MessageTarget.EXPLORER_COLLECTION);
		if (collectionTarget)
		{
			if (this._sameRootId(collectionTarget.getParameters().rootId) && Ext.String.startsWith(collectionTarget.getParameters().path, this._parentPath))
			{
				this.showOutOfDate();
				return;
			}
		}
			
		var resourceTargets = message.getTargets(Ametys.message.MessageTarget.RESOURCE);
		if (resourceTargets.length > 0)
		{
			for (var i = 0; i < resourceTargets.length; i++)
			{
				var resourceTarget = resourceTargets[i];
				if (this._sameRootId(resourceTarget.getParameters().rootId) && Ext.String.startsWith(resourceTarget.getParameters().path, this._parentPath))
				{
					this.showOutOfDate();
					return;
				}
			}
		}
	},
	
	/**
	 * Determines if the root id argument is the same that the root id of the search tool.
	 * @param {String} rootId The root id to check
	 * @return True if same
	 * @private
	 */
	_sameRootId: function(rootId)
	{
		return this._rootId = rootId;
	}
});