/*
 *  Copyright 2019 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 global validation report for programs
 * @private
 */
Ext.define('Ametys.plugins.odf.validation.GlobalValidationReportTool', {
	extend: "Ametys.tool.Tool",
	
	/**
	 * @private
	 * @property {Ext.LoadMask} _mask A mask bound to the grid to display information to the user
	 */
	
	/**
	 * @private
	 * @property {Ext.grid.Panel} _grid the grid panel of the tool
	 */
    
    /**
     * @private
     * @property {Ext.XTemplate} _hintTpl The template for the list of referenced contents
     */
    _hintTpl : Ext.create('Ext.XTemplate', "{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_HINT}}"), 

	statics: {
		/**
		 * @property {Number}
		 * @readonly
		 * @static
		 * The number of records to display by 'page'
		 */
		PAGE_SIZE: 50,
        
        /**
         * @static
         * Show the list of invalidated contents computed by the last report
         * @param {String} contentId The content id
         * @param {String} contentTitle The content title
         */
        showInvalidatedContents: function(contentId, contentTitle)
	    {
	        Ametys.data.ServerComm.callMethod({
	            role: "org.ametys.odf.schedulable.GlobalValidationReport",
	            methodName: "getInvalidatedContents",
	            parameters: [contentId],
	            callback: {
	                handler: function (invalidatedContents) {
                        Ametys.odf.helper.Content.showInvalidatedContents({
				            id: contentId,
				            title: contentTitle,
				            invalidatedContents: invalidatedContents
				        }, null, null, null, "{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_SHOW_INVALIDATED_CONTENTS_DIALOG_HINT1}}");
                    },
	                scope: this
	            },
	            errorMessage: "{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_SHOW_INVALIDATED_ERROR}}"
	        });
	    }
	    
	},
	
	getMBSelectionInteraction: function() 
	{
		return Ametys.tool.Tool.MB_TYPE_ACTIVE;
	},
	
	constructor: function(config)
	{
		this.callParent(arguments);
	
		Ametys.message.MessageBus.on(Ametys.message.Message.GLOBAL_VALIDATION_CHECK_STARTED, this._onCheckStart, this);
		Ametys.message.MessageBus.on(Ametys.message.Message.GLOBAL_VALIDATION_CHECK_ENDED, this._onCheckEnd, this);
	},
	
	createPanel: function()
	{
		this._createGrid();
		return this._grid;
	},
	
	sendCurrentSelection: function()
	{
		var selection = this._grid.getSelectionModel().getSelection();
		var contentIds = Ext.Array.map(selection, function(record) {
			return record.get('contentId');
		});
		
		Ext.create("Ametys.message.Message", {
			type: Ametys.message.Message.SELECTION_CHANGED,
			
			targets: {
				id: Ametys.message.MessageTarget.CONTENT,
				parameters: { ids: contentIds }
			}
		});
	},
	
	setParams: function(params)
	{
		this.callParent(arguments);
		this._refreshData();
	},
	
	onClose: function()
	{
		this.callParent(arguments);
		
		if (this._mask)
		{
			this._mask.hide();
			delete this._mask;
		}
	},
	
	/**
	 * @private
	 * Create the main grid that display the results.
	 */
	_createGrid: function()
	{
        this._store = this._createStore();
        
		this._grid = Ext.create('Ext.grid.Panel', {
			border: false,
			scrollable: true,
			
			stateful: true,
			stateId: this.self.getName() + "$grid",
			
			store: this._store,
			selModel : {
				mode: 'MULTI'
			},
			
			forceFit: true,
			columns: [
				{stateId: 'grid-title', header: "{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_CONTENT_TITLE}}", width: 220, sortable: true, dataIndex: 'title', renderer: Ametys.plugins.cms.search.SearchGridHelper.renderTitle},
				{stateId: 'grid-code', header: "{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_CONTENT_CODE}}", width: 120, sortable: true, dataIndex: 'code'},
                {stateId: 'grid-catalog', header: "{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_CONTENT_CATALOG}}", width: 150, sortable: true, dataIndex: 'catalog'},
				{stateId: 'grid-invalidatedContentsCount', header: "{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_INVALIDATED_CONTENTS_COUNT}}", width: 100, sortable: true, dataIndex: 'invalidatedContentsCount', xtype: 'numbercolumn', format: '0', renderer: this._renderResultCount},
                {stateId: 'grid-contributor', header: "{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_CONTENT_CONTRIBUTOR}}", width: 140, sortable: true, dataIndex: 'contributor'},
				{stateId: 'grid-lastModified', header: "{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_CONTENT_LASTMODIFIED}}", width: 120, sortable: true, dataIndex: 'lastModified', renderer: Ext.util.Format.dateRenderer(Ext.Date.patterns.FriendlyDateTime)}
			],
			
            dockedItems: this._getDockedItems(),
            
			listeners: {
				selectionchange: {fn: this._onSelectionChange, scope: this},
				itemdblclick: {fn: this._onItemDblClick, scope: this}
			}
		});
	},
	
	/**
	 * @private
	 * Create the store that the grid should use as its data source.
	 * @return {Ext.data.Store} The created store
	 */
	_createStore: function ()
	{
		return Ext.create('Ext.data.Store', {
            autoLoad: false,
            
            model: 'Ametys.plugins.odf.validation.GlobalValidationReportTool.ProgramEntry',
            proxy: {
                type: 'ametys',
                plugin: 'odf',
                url: 'global-validation/programs.xml',
                reader: {
                    type: 'xml',
                    keepRawData: true,
                    rootProperty: 'programs',
                    record: 'program',
                    totalProperty: 'total'
                }
            },
            
            pageSize: Ametys.plugins.odf.validation.GlobalValidationReportTool.PAGE_SIZE,
            sortOnLoad: true,
            sorters: [{property: 'title', direction:'ASC'}],
            
            listeners: {
                load: {fn: this._onLoad, scope: this}
            }
        });
	},
	
	/**
	 * @private
	 * Returns the docked items configuration
	 * @return {Object/Object[]} the items to dock
	 */
	_getDockedItems: function()
	{
        return [{
            dock: 'top',
            xtype: 'component',
            itemId: 'hint',
            hidden: true,
            ui: 'tool-hintmessage',
            html: "{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_NO_REPORT}}"
        },
		{
			// paging bottom bar
            xtype: 'pagingtoolbar',
			dock: 'bottom',
			store: this._store,
			displayInfo: true,
			displayMsg: "{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_RESULT_1}}{0}{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_RESULT_2}}{1}{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_RESULT_3}}{2}",
			emptyMsg: "{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_NO_RESULT}}"
		}]
	},
	
	/**
	 * Fires a event of selection on message bus, from the selected contents in the grid.
	 * @param {Ext.selection.Model} model The selection model
	 * @param {Ext.data.Model[]} selected The selected records
	 * @private
	 */
	_onSelectionChange: function(model, selected)
	{
		this.sendCurrentSelection();
	},
	
	/**
	 * Open the corresponding content
	 * @param {Ext.view.View} view The view
	 * @param {Ext.data.Model} record The record that belongs to the item
	 * @param {HTMLElement} item the item's element
	 * @private
	 */
	_onItemDblClick: function(view, record, item)
	{
		var params = {
			id: record.get('contentId'),
			mode: 'view'
		}
		
		Ametys.tool.ToolsManager.openTool('uitool-content', params);
	},

	/**
	 * Fill the grid with the contents
	 * @private
	 */
	_refreshData: function()
	{
        this._grid.getView().unmask();
		this._grid.getStore().loadPage(1);
	},
	
	/**
	 * Called whenever the store load data
	 * @param {Ext.data.Store} store the store
	 * @param {Ext.data.Model[]} records An array of records
	 * @param {Boolean} success True if the operation was successful.
	 * @private
	 */
	_onLoad: function(store, records, success)
	{
		if (!success)
		{
			Ametys.Msg.alert(
				"{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_ERROR_TITLE}}",
				"{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_ERROR_MSG}}"
			);
			return;
		}
        
        var hintPanel = this._grid.getDockedItems('[itemId="hint"]')[0];
		
		var xmlRawData = store.getProxy().reader.rawData;
		if (xmlRawData.nodeName == 'file-not-found')
		{
			this._grid.getView().mask("{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_NO_REPORT}}", 'ametys-mask-unloading');
            hintPanel.setVisible(false);
		}
		else 
		{
            if (records.length == 0)
            {
                this._grid.getView().mask("{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_NO_RESULT}}", 'ametys-mask-unloading');
            }
            
            var date = Ext.util.Format.date(xmlRawData.getAttribute("date"), Ext.Date.patterns.FriendlyDateTime);
            var hintMsg = this._hintTpl.apply({date: date, count: this._store.getTotalCount()});
        
	        hintPanel.update(hintMsg);
	        hintPanel.setVisible(true);
		}
	},
	
	/**
	 * Bus message listener for message of type : Ametys.message.Message.GLOBAL_VALIDATION_CHECK_STARTED
	 * Displays an informative message and recursively verify if the global validation
	 * check process is still running on the server.
	 * @param {Ametys.message.Message} message the bus message
	 * @private
	 */
	_onCheckStart: function(message)
	{
        this._grid.getView().unmask();
        this._grid.getView().mask("{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_WAIT_MESSAGE}}");
        
        Ext.Function.defer(this._checkEngine, 1000, this);
	},
	
	/**
	 * Bus message listener for message of type : Ametys.message.Message.GLOBAL_VALIDATION_CHECK_ENDED
	 * Refresh the grid data.
	 * @param {Ametys.message.Message} message the bus message
	 * @private
	 */
	_onCheckEnd: function(message)
	{
		this._grid.getView().unmask();
		this._refreshData();
	},
	
	/**
	 * Verify if the global validation check process is still running on the server.
	 * @private
	 */
	_checkEngine: function()
	{
        Ametys.data.ServerComm.callMethod({
            role: "org.ametys.odf.schedulable.GlobalValidationReport",
            methodName: "isRunning",
            parameters: [],
            callback: {
                handler: this._checkEngineCb,
                scope: this,
                arguments: []
            },
            errorMessage: "{{i18n PLUGINS_ODF_UITOOL_GLOBAL_VALIDATION_CHECK_ENGINE_ERROR}}"
        });
	},
	
	/**
	 * #_checkEngine callback function.
	 * Fire the Ametys.message.Message.GLOBAL_VALIDATION_CHECK_ENDED when the server process has ended.
	 * @param {Boolean} running the server's response. True if check engine is running
	 * @private
	 */
	_checkEngineCb: function(running)
	{
		if (running == null)
		{
			// Stop to recursively checks if the process is still running.
			this._onCheckEnd();
			return;
		}
		
		if (!running)
		{
			// Send a message on the bus to tell everyone that the server process has ended.
			Ext.create("Ametys.message.Message", { type: Ametys.message.Message.GLOBAL_VALIDATION_CHECK_ENDED });
			return;
		}
		
		Ext.defer(this._checkEngine, 1000, this);
	},
    
    /**
     * Function to render the number of invalidated contents in the grid.
     * @param {Object} value The data value
     * @param {Object} metaData A collection of metadata about the current cell
     * @param {Ext.data.Model} record The record
     */
    _renderResultCount: function(value, metaData, record)
    {
        var escapedTitle = record.get('title').replace(/"/g, '"').replace(/'/g, "'");
        return `<a href='javascript:(function(){Ametys.plugins.odf.validation.GlobalValidationReportTool.showInvalidatedContents("${record.get('contentId')}", "${escapedTitle}")})()'>${value}</a>`;
    }
});

Ext.define('Ametys.plugins.odf.validation.GlobalValidationReportTool.ProgramEntry', { 
    extend: 'Ext.data.Model',
    schema: 'default',
    fields: [
        {name: 'contentId', mapping: '@id'},
        {name: 'title', mapping: '@title', type: 'string'},
        {name: 'iconSmall', mapping: '@iconSmall'},
        {name: 'iconGlyph', mapping: '@iconGlyph'},
        {name: 'invalidatedContentsCount', mapping: '@invalidated-contents-count'},
        {name: 'catalog', mapping: 'catalog'},
        {name: 'code', mapping: 'code'},
        {name: 'lastModified', mapping: 'lastModified'},
        {name: 'contributor', mapping: 'contributor/sortablename'}
    ]
});