/*
 *  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 displays the dashboard.
 * Composed with a grid and a tabpanel.
 * @private
 */
 Ext.define('Ametys.plugins.dashboard.DashboardTool', {
 	extend: 'Ametys.tool.Tool',
 	
 	/**
 	 * @private
 	 * @property {Object} _grids All the grids of this tool indexed by their id.
 	 */
 	
 	/**
 	 * @private
 	 * @property {Object} _tasklabels All the labels of the grids of this tool indexed by their id.
 	 */
 	
 	/**
 	 * @private
 	 * @property {Object} _taskLengths All the max lengths of the grids of this tool indexed by their id.
 	 */
 	
 	/**
 	 * @private
 	 * @property {Ext.panel.Panel} _mainPanel The main panel of the tool.
 	 */
 	
 	/**
 	 * @private
 	 * @property {Ext.data.Model[]} _selection The current selection.
 	 */
 	
 	constructor: function(config)
 	{
 		this.callParent(arguments);
 		
 		Ametys.message.MessageBus.on(Ametys.message.Message.WORKFLOW_CHANGED, this._onMessageModified, this);
		Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onMessageModified, this);
		Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._onMessageDeleted, this);
 	},
 	
 	createPanel: function()
 	{ 		
 		this._mainPanel = Ext.create('Ext.panel.Panel', {
            layout: {
            	type: 'vbox',
                align: 'stretch'
            },
            margin: '5 10 5 10',
            border: false,
 			
 			items: [
	 			{
	 				xtype: 'panel',
	 				id: 'recent-drafts-panel',
                	border: false,
                    ui: 'light',
                	title: '',
	                minHeight: 180,
	                maxHeight: 250,
	                layout: 'fit',
	                items: [{
	                	xtype: 'grid',
	                	id: 'recent-drafts-grid',
				        scrollable: true,

				        deferRowRender: false,
				        selModel: {
				        	mode: 'MULTI'
				        }
	                }]
	 			},
	 			{
	 				xtype: 'component',
	 				html: '',
	 				height: 30
	 			},
	 			{
	 				flex: 1,
	 				cls: 'ametys-form-tab-item',
	 				xtype: 'tabpanel',
	 				id: 'tabs-panel',
	 				activeTab: 0,
	                deferredRender: false,
	                border: false,
	                items: [] // will be set on refresh
	 			}
 			]
 		});
 		
 		return this._mainPanel;
 	},
 	
	getMBSelectionInteraction: function()
	{
		return Ametys.tool.Tool.MB_TYPE_ACTIVE;
	},
	
	sendCurrentSelection: function()
	{
		var records = this._selection;
		if (records && records.length > 0)
		{
			var contentIdsByPageId = {};
			var contentIds = [];
			
			// Categorize the selected contents
			for (var i = 0; i < records.length; i++)
			{
				var id = records[i].get('id');
				var pageId = records[i].get('page-id');
				
				if (pageId != null && pageId != '')
				{
					if (contentIdsByPageId[pageId] == null)
					{
						contentIdsByPageId[pageId] = [];
					}
					
					contentIdsByPageId[pageId].push(id);
				}
				else
				{
					contentIds.push(id);
				}
			}
			
			var targets = [];
			
			// Message targets for contents belonging to a page 
			Ext.Object.each(contentIdsByPageId, function(pageId){
				Ext.Array.each(contentIdsByPageId[pageId], function(contentId){
					targets.push({
						id: Ametys.message.MessageTarget.PAGE,
						parameters: {
							ids: [pageId],
							'content-id': contentId
						}
					});
				})
			});
			
			// Message targets for orphan contents
			targets.push({
				id: Ametys.message.MessageTarget.CONTENT,
				parameters: {
					ids: contentIds
				}
			});
			
			// Send the message on the bus
			Ext.create('Ametys.message.Message', {
				type: Ametys.message.Message.SELECTION_CHANGED,
				targets: targets
			});
		}
		else
		{
			Ext.create('Ametys.message.Message', {
				type: Ametys.message.Message.SELECTION_CHANGED,
				targets: []
			});
		}
	},
	
	/**
	 * Listener for change of selection in any grid.
	 * @param {String} selectedTaskId The id of the grid where the selection change happened.
	 * @param {Ext.data.Model} model The model.
	 * @param {Ext.data.Model[]} selected The selected records.
	 * @private
	 */
	_onSelectionChange: function(selectedTaskId, model, selected)
	{
		for (var taskId in this._grids)
		{
			if (taskId != selectedTaskId)
			{
				this._grids[taskId].getSelectionModel().deselectAll(true); // deselect all the other grids
			}
		}
		
		this._selection = Ext.isArray(selected) ? selected : [selected];
		this.sendCurrentSelection();
	},
	
	setParams: function(params)
	{
		this.callParent(arguments);
		this.refresh();
	},
	
	refresh: function()
	{
		this.showRefreshing();
		
		Ametys.cms.dashboard.DashboardDAO.getTasks(Ext.bind(this._refreshCb, this));
	},
	
	/**
	 * Does the real refresh after retrieving the tasks from the server.
	 * Draws and loads the grids.
	 * @param {HTMLElement} tasks The tasks sent by the server.
	 * @private
	 */
	_refreshCb: function(tasks)
	{
		this._taskLabels = {};
		this._taskLengths = {};
	    this._grids = {};
	    
	    // Retrieve the tasks
		var taskNodes = Ext.dom.Query.select("task", tasks);
		for (var i = 0; i < taskNodes.length; i++)
	    {
	        var taskId = Ext.dom.Query.selectValue('@id', taskNodes[i], '');
	        this._taskLabels[taskId] = Ext.dom.Query.getNodeValue(taskNodes[i]);
	        this._taskLengths[taskId] = Ext.dom.Query.selectValue('@length', taskNodes[i]);
	    }
	    
		// Recent drafts panel
		var recentDraftPanel = this._mainPanel.items.get('recent-drafts-panel');
		var recentDraftsGrid = recentDraftPanel.items.get('recent-drafts-grid');
		
		this._reconfigureRecentDraftsGrid(recentDraftsGrid, 'recent-drafts', this._taskLabels['recent-drafts'], this._taskLengths['recent-drafts']);
		recentDraftPanel.items.add(recentDraftsGrid);
		
	    // Tabs panel
 		var taskPanels = [];
 		for (var taskId in this._taskLabels)
 		{
 			if (taskId != 'recent-drafts')
 			{
 				var taskLabel = this._taskLabels[taskId];
 				var max = this._taskLengths['taskId'];
 				taskPanels.push({
 					xtype: 'panel',
 					itemId: taskId + '-panel',
 					title: taskLabel,
                    border: false,
                    layout: 'fit',
                    items: [this._drawGridPanel(taskId, taskLabel, max)]
 				});
 			}
 		}
 		this._mainPanel.items.get('tabs-panel').removeAll();
 		for (var i = 0; i < taskPanels.length; i++)
 		{
 			this._mainPanel.items.get('tabs-panel').add(taskPanels[i]).show();
 		}
 		
 		// Load the grids
 		for (var taskId in this._grids)
		{
			this._grids[taskId].getStore().load({
				params: {
					taskId: taskId,
					login: Ametys.getAppParameter('user').login,
					siteName: Ametys.getAppParameter('siteName'),
					lang: Ametys.getAppParameter('user').locale
				}
			});
		}
		this.showRefreshed();
	},
	
	/**
	 * Changes the store and the columns of the recent drafts grid.
	 * @param {String} grid The grid to reconfigure.
	 * @param {String} taskId The id of the task.
	 * @param {String} taskLabel The label of the task.
	 * @param {Number} max The max items to display.
	 * @private
	 */
	_reconfigureRecentDraftsGrid: function(grid, taskId, taskLabel, max)
	{
		var store = Ext.create('Ext.data.Store', {
			model: 'Ametys.plugins.dashboard.DashboardTool.ContentEntry',
 			proxy: {
 				type: 'ametys',
				plugin: 'web',
				url: 'contents/todo-list.xml',
				reader: {
					type: 'xml',
					record: 'content'
				}
 			},
 			sorters: [{property: 'lastModified', direction: 'DESC'}],
 			taskId: taskId,
 			taskLabel: taskLabel
		});
		
		var columns = [
			{stateId: 'grid-column-title', header: "{{i18n PLUGINS_DASHBOARD_COLUMN_CONTENT_TITLE}}", flex: 1, sortable: true, dataIndex: 'title', renderer: Ametys.grid.GridColumnHelper.renderTextWithIcon},
			{stateId: 'grid-column-lastModified', header: "{{i18n PLUGINS_DASHBOARD_COLUMN_LASTMODIFIED}}", width: 180, sortable: true, dataIndex: 'lastModified', renderer: Ext.util.Format.dateRenderer(Ext.Date.patterns.FriendlyDateTime)},
			{stateId: 'grid-column-workflow-step', header: "{{i18n PLUGINS_DASHBOARD_COLUMN_WORKFLOW_STEP}}", width: 60, sortable: true, dataIndex: 'workflow-step', renderer: this._renderWorkflowStep}
        ];
		
		grid.reconfigure(store, columns);
		
		// Events
		grid.on('selectionchange', this._onSelectionChange, this, {args: [taskId]});
		grid.on('celldblclick', this._openContent, this);
		grid.getStore().on('load', this._onLoad, this, [taskId, taskLabel]);
		
	    this._grids[taskId] = grid;
	},
 	
 	/**
 	 * Draws a grid for a tab.
 	 * @param {String} taskId The id of the task.
 	 * @param {String} taskLabel The label of the task.
 	 * @param {Number} max The max items to display.
 	 * @return {Ext.grid.Panel} The grid.
 	 * @private
 	 */
 	_drawGridPanel: function(taskId, taskLabel, max)
 	{
 		var store = Ext.create('Ext.data.Store', {
 			model: 'Ametys.plugins.dashboard.DashboardTool.ContentEntry',
 			proxy: {
 				type: 'ametys',
				plugin: 'web',
				url: 'contents/todo-list.xml',
				reader: {
					type: 'xml',
					record: 'content'
				}
 			},
 			sorters: [{property: 'lastModified', direction: 'DESC'}],
 			taskId: taskId,
        	taskLabel: taskLabel
 		});
 		
 		var itemId = taskId + '-grid';
 		var grid = Ext.create('Ext.grid.Panel', {
 			itemId: itemId,
 			store: store,
 			scrollable: true,
 			
 			deferRowRender: true,
 			
 			selModel: {
	        	mode: 'MULTI'
	        },
	        
	        stateful: true,
	        stateId: this.self.getName() + "$" + itemId,
 			
 			columns: [
 				{stateId: 'grid-column-title', header: "{{i18n PLUGINS_DASHBOARD_COLUMN_CONTENT_TITLE}}", flex: 1, sortable: true, dataIndex: 'title', renderer: Ametys.grid.GridColumnHelper.renderTextWithIcon},
 				{stateId: 'grid-column-contributor', header: "{{i18n PLUGINS_DASHBOARD_COLUMN_CONTRIBUTOR}}", width: 270, sortable: true, dataIndex: 'contributor'},
 				{stateId: 'grid-column-lastModified', header: "{{i18n PLUGINS_DASHBOARD_COLUMN_LASTMODIFIED}}", width: 180, sortable: true, dataIndex: 'lastModified', renderer: Ext.util.Format.dateRenderer(Ext.Date.patterns.FriendlyDateTime)}
 			]
 		});
 		
 		// Events
 		grid.on('selectionchange', this._onSelectionChange, this, {args: [taskId]});
 		grid.on('celldblclick', this._openContent, this);
 		grid.getStore().on('load', this._onLoad, this, [taskId, taskLabel]);
	    
	    this._grids[taskId] = grid;
	    
	    return grid;
 	},
 	
 	/**
 	 * Renders a workflow step.
 	 * @param {Object} value The data value for the current cell.
 	 * @param {Object} metaData A collection of metadata about the current cell.
 	 * @param {Ext.data.Model} record The record for the current row.
 	 * @return {String} The HTML for rendering the workflow step.
 	 * @private
 	 */
 	_renderWorkflowStep: function(value, metaData, record)
 	{
 		var step = record.get('workflow-step');
 		var iconSmall = record.get('workflow-icon-small');
	    if (iconSmall)
	    {
	    	return '<img src="' + Ametys.CONTEXT_PATH + iconSmall + '" alt="' + step + '" title="' + step + '" class="a-grid-icon a-grid-icon-workflow"/>';
	    }
	    else
	    {
	        return step;
	    }
 	},
 	
 	/**
 	 * Opens the selected content.
 	 * @param {Ext.view.Table} table The table.
 	 * @param {HTMLElement} td The TD element for the cell.
 	 * @param {Number} cellIndex The cell index.
 	 * @param {Ext.data.Model} record The record to open.
 	 * @private
 	 */
 	_openContent: function(table, td, cellIndex, record)
 	{
 		var pageId = record.get('page-id');
 		var id = record.get('id');
 		
 		if (pageId == null || pageId == '')
	    {
	        Ametys.tool.ToolsManager.openTool('uitool-content', {'id': id});
	    }
	    else
	    {
	        Ametys.tool.ToolsManager.openTool('uitool-page', {'id': pageId});
	    }
 	},
 	
 	/**
 	 * Listener when a store reads data.
 	 * @param {Ext.data.Store} store The store.
 	 * @param {Ext.data.Model[]} records An array of records.
 	 * @private
 	 */
 	_onLoad: function(store, records)
 	{
 		this._modifyPanelTitle(store.taskId, store.taskLabel, records.length);
 	},
 	
 	/**
 	 * Updates the title of a panel.
 	 * @param {String} taskId The id of the panel to update.
 	 * @param {String} taskLabel The new title.
 	 * @param {String} count The number to set in brackets at the end of the new title.
 	 * @private
 	 */
 	_modifyPanelTitle: function(taskId, taskLabel, count)
 	{
 		if (taskId && taskLabel)
 		{
 			var panel = this._mainPanel.getComponent(taskId + '-panel') || this._mainPanel.getComponent('tabs-panel').getComponent(taskId + '-panel');
 			if (panel != null)
 			{
 				panel.setTitle(taskLabel + ' (' + count + ')');
 			}
 		}
 	},
 	
 	/**
 	 * Listener on edition message.
 	 * @param {Ametys.message.Message} message The edition message.
 	 * @private
 	 */
 	_onMessageModified: function(message)
 	{
 		var targets = message.getTargets("content");
		if (targets)
		{
			var contentIds = this._convertToId(targets);
			if (this._areInStore(contentIds))
			{
				// We are concerned by this message
				this.showOutOfDate();
			}
		}
 	},
 	
 	/**
 	 * Listener on deletion message.
 	 * @param {Ametys.message.Message} message The deletion message.
 	 * @private
 	 */
 	_onMessageDeleted: function(message)
 	{
 		var targets = message.getTargets("content");
		if (targets)
		{
			var contentIds = this._convertToId(targets);
			if (this._areInStore(contentIds))
			{
				// We are concerned by this message
				this.showOutOfDate();
			}
		}
 	},
 	
 	/**
 	 * Gives the ids of the given targets.
 	 * @param {Object[]} targets The targets.
 	 * @return {String[]} The ids.
 	 * @private
 	 */
 	_convertToId: function(targets)
 	{
 		var targetsIds = [];
 		for (var i = 0; i < targets.length; i++)
		{
			targetsIds.push(targets[i].getParameters()['id']);
		}
		return targetsIds;
 	},
 	
 	/**
 	 * Tells if one of the contentIds are currently somewhere in the tool's grids.
 	 * @param {String[]} contentIds The ids to test.
 	 * @return {Boolean} True if at least one id is in one of the stores.
 	 * @private
 	 */
 	_areInStore: function(contentIds)
 	{
 		for (var i = 0; i < contentIds.length; i++)
 		{
 			for (var j in this._grids)
 			{
 				var record = this._grids[j].getStore().getById(contentIds[i]);
 				if (record != null)
 				{
 					return true;
 				}
 			}
 		}
 		
 		return false;
 	}
 });