/*
 *  Copyright 2023 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 some statistics about entries of a given form.
 * @private
 */
Ext.define('Ametys.plugins.forms.tool.FormsStatisticsTool', {
	extend: 'Ametys.tool.SelectionTool',
	
	/**
	 * @private
	 * @property {Ext.panel.Panel} _mainPanel The main panel of this tool.
	 */
	
	constructor: function(config)
	{
		this.callParent(arguments);
		
		Ametys.message.MessageBus.on(Ametys.message.Message.CREATED, this._onMessageCreated, this);
		Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onMessageModified, this);
		Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._onMessageDeleted, this);
	},
	
	setParams: function(params)
	{
		this.callParent(arguments);
		
    	// Register the tool on the history tool
		var toolId = this.getFactory().getId();
	    var toolParams = this.getParams();

        Ametys.navhistory.HistoryDAO.addEntry({
			id: this.getId(),
			label: this.getTitle(),
			description: this.getDescription(),
			iconSmall: this.getSmallIcon(),
			iconMedium: this.getMediumIcon(),
			iconLarge: this.getLargeIcon(),
			type: Ametys.navhistory.HistoryDAO.TOOL_TYPE,
			action: Ext.bind(Ametys.tool.ToolsManager.openTool, Ametys.tool.ToolsManager, [toolId, toolParams], false)
        });
	},
	
	createPanel: function()
	{
		this._mainPanel = Ext.create('Ext.panel.Panel', {
			scrollable: true,
			border: false,
			
			cls: 'form-statistics-panel'
		});
		
		return this._mainPanel;
	},
	
	refresh: function()
	{
		var formId = this._currentSelectionTargets[0].getParameters().id;
		
		if (formId)
		{
			this._mainPanel.unmask();    
			Ametys.plugins.forms.dao.FormDAO.getStatistics([formId], this._drawPanels, {scope: this, arguments: [Ext.bind(this.showUpToDate, this)]})
		}
	},
	
	setNoSelectionMatchState: function (message)
	{
		this.callParent(arguments);
		if (this._mainPanel)
		{
			if (this._mainPanel.rendered)
			{
				this._mainPanel.mask("{{i18n PLUGINS_FORMS_STATISTICS_TOOL_NOSELECTION}}", 'form-no-selection-match-mask');
			}
			else
			{
				this._mainPanel.on("afterrender", function (panel) {
					panel.mask("{{i18n PLUGINS_FORMS_STATISTICS_TOOL_NOSELECTION}}", 'form-no-selection-match-mask');
				})
			}
		}
	},
	
	/**
	 * Draws the panels after retrieving the stats from the server.
	 * @param {Object} stats The stats retrieved.
	 * @param {Array} args The arguments :
	 * @param {Function} args.callback The callback function after the panels were drawn.
	 * @private
	 */
	_drawPanels: function(stats, args)
	{
		this._mainPanel.removeAll();
		
		var questions = stats.questions;
		
		var globalPanel = this._getGlobalPanel(stats);
		this._mainPanel.add(globalPanel);
		
		if (stats.nbEntries > 0)
		{
    		for (var i = 0; i < questions.length; i++) {
    			this._mainPanel.add(this._getQuestionPanel(questions[i], i+1));
    		}
        }
		
		var cb = args[0];
		if (Ext.isFunction(cb))
		{
			cb();
		}
	},
	
	/**
	 * Gets the first panel which displays the title of the form and the nbEntries count.
	 * @param {Object} stats The stats.
	 * @private
	 */
	_getGlobalPanel: function(stats)
	{
		var html = 
			'<h1 class="title" >' + 
				"{{i18n PLUGINS_FORMS_STATISTICS_GLOBAL_TITLE_1}}" + 
				stats.title + 
				"{{i18n PLUGINS_FORMS_STATISTICS_GLOBAL_TITLE_2}}" + 
			'</h1>' + 
			'<p>' + 
			    "{{i18n PLUGINS_FORMS_STATISTICS_GLOBAL_TEXT_1}}" + 
				stats.nbEntries + 
				( stats.nbEntries == 1 ? "{{i18n PLUGINS_FORMS_STATISTICS_GLOBAL_TEXT_SINGLE}}" : "{{i18n PLUGINS_FORMS_STATISTICS_GLOBAL_TEXT_MULTIPLE}}") 
			'</p>'
	   ;
		
		var globalPanel = Ext.create('Ext.panel.Panel', {
			cls: 'form-global-panel',
			html: html
		});
		
		return globalPanel;
	},
	
	/**
	 * Gets the panel for a question.
	 * @param {Object} question The question object.
	 * @param {Number} index The index of this question.
	 * @private
	 */
	_getQuestionPanel: function(question, index)
	{
		var title = "{{i18n PLUGINS_FORMS_STATISTICS_QUESTION_PANEL_TITLE_1}}" + index + "{{i18n PLUGINS_FORMS_STATISTICS_QUESTION_PANEL_TITLE_2}}" + question.title;
	    if (question.mandatory)
	    {
	        title = title + "{{i18n PLUGINS_FORMS_STATISTICS_QUESTION_PANEL_TITLE_MANDATORY}}";
	    }
	    var type = question.typeId;
	    
	    var questionPanel;
	    
	    if (type == 'form.ChoicesList')
	    {
	        questionPanel = this._getChoiceQuestionPanel(question, title);
	    }
	    else if (type == 'form.Matrix')
	    {
	        questionPanel = this._getMatrixQuestionPanel(question, title);
	    }
	    else
	    {
	        questionPanel = this._getDefaultQuestionPanel(question, title);
	    }
		
		return questionPanel;
	},
	
	/**
	 * Gets the panel for a default question.
	 * @param {Object} question The question object.
	 * @param {String} title The title of this panel.
	 * @private
	 */
	_getDefaultQuestionPanel: function(question, title)
	{
		var graphData = [];
	    var data = [];
	    
	    var choices = question.options[0].choices;
	    
        var count, 
        	totalCount = 0;
	    for (var i = 0; i < choices.length; i++)
	    {
	        var choiceValue = choices[i].value;
            if (choiceValue == 'answered' || choiceValue == 'empty')
            {
                count = parseInt(choices[i].count);
                choiceValueLabel = choiceValue == 'answered' ? "{{i18n PLUGINS_FORMS_STATISTICS_ANSWERED}}" : "{{i18n PLUGINS_FORMS_STATISTICS_NOT_ANSWERED}}"
                totalCount += count;
                graphData.push({
                    label: choiceValueLabel,
                    count: count
                });
                data.push([choiceValueLabel, count]);
            }
            else if (choiceValue == 'true' || choiceValue == 'false')
            {
                count = parseInt(choices[i].count);
                choiceValueLabel = choiceValue == 'true' ? "{{i18n PLUGINS_FORMS_STATISTICS_TRUE}}" : "{{i18n PLUGINS_FORMS_STATISTICS_FALSE}}"
                totalCount += count;
                graphData.push({
                    label: choiceValueLabel,
                    count: count
                });
                data.push([choiceValueLabel, count]);
            }
	    }
		
		var gridPanel = Ext.create('Ext.grid.Panel', {
			cls: 'question-text-grid',
        	scrollable: true,
        	region: 'center',
        	store: {
        		autoDestroy: true,
                fields: ['label', 'count'],
        		data: data
        	},
        	
        	stateful: true,
     		stateId: this.self.getName() + "$grid-question-text-" + question.id,
        	columns: [
                {stateId: "grid-question-text-" + question.id + '-column-text', header: "{{i18n PLUGINS_FORMS_STATISTICS_TEXT_LABEL}}", sortable: false, dataIndex: 'label'},
                {stateId: "grid-question-choice-" + question.id + '-column-count', header: "{{i18n PLUGINS_FORMS_STATISTICS_CHOICE_COUNT}}", sortable: true, dataIndex: 'count', width: 100,
                    renderer: function (value, record) {
                        // BIG HACK because graph does not handle well the value 0.
                        if (value == 0.00000001)
                        {
                            return 0;
                        }
                        return value;
                    }
                },
                {stateId: "grid-question-choice-" + question.id + '-column-proportion', header: "{{i18n PLUGINS_FORMS_STATISTICS_CHOICE_PERCENT}}", sortable: true, dataIndex: 'count', width: 100, 
                    renderer: function(value) {
                        var percentage = (totalCount == 0) ? 0 : Math.round(value*100 / totalCount);
                        return percentage + '%';
                    }
                }],
        	forceFit: true,
        	disableSelection: true
		});
		
		var graphPanel = Ext.create('Ext.chart.PolarChart', {
			region: 'east',
			width: 400,
			
			store: {
				fields: ['label', 'count'],
				data: graphData
			},
			
			legend: {
		        docked: 'right',
		        toggleable: false
		    },
		    
		    series: [{
		    	type: 'pie',
		    	xField: 'count',
		    	label: {
		    		field: 'label',
		    		calloutLine: {
                    	color: 'rgba(0,0,0,0)' // Transparent to hide callout line
                    },
                    renderer: function(val) {
                        return ''; // Empty label to hide text
                    }
		    	},
		    	tips: {
                    trackMouse: true,
                    renderer: function(tooltip, item) {
                    	var percentage = (totalCount == 0) ? 0 : Math.round(item.get('count')*100 / totalCount);
                        tooltip.setHtml(item.get('label') + '<br/>' + item.get('count') + '<br/>' + percentage + '%');
                    }
                }
		    }]
			
		});
		
		var questionPanel = Ext.create('Ext.panel.Panel', {
			cls: 'form-question-panel question-text',
	        title: title,
	        layout: 'border',
	        collapsible: true,
	        scrollable: false,
	        height: 200,
	        items: [ gridPanel, graphPanel ]
		});
		
		return questionPanel;
	},
	
	/**
	 * Gets the panel for a CHOICE question.
	 * @param {Object} question The question object.
	 * @param {String} title The title of this panel.
	 * @private
	 */
	_getChoiceQuestionPanel: function(question, title)
	{
	    var data = [];
	    
	    var choices = question.options[0].choices;
	    
	    var count,
            hiddenTab = [], 
	    	totalCount = 0;
	    for (var i = 0; i < choices.length; i++)
	    {
	    	count = choices[i].count;
	    	totalCount += count; 
	    	
            hiddenTab.push(count == 0)
            
	    	data.push([
	    		choices[i].label,
	    		count,
	    		choices[i].value
	    	]);
	    }
	    
	    var store = Ext.create('Ext.data.Store', {
	    	fields: [
                { name: 'label' },
                { 
                    name: 'count',
                    convert: function (value, record) {
                        // BIG HACK because graph does not handle well the value 0.
                        if (value == 0)
                        {
                            return 0.00000001;
                        }
                        return value;
                    } 
                },
                { name: 'value'}
            ],
			data: data
	    });

        var gridPanel = Ext.create('Ext.grid.Panel', {
            cls: 'question-text-grid',
            scrollable: true,
            region: 'center',
            store: store,
            
            stateful: true,
            stateId: this.self.getName() + "$grid-question-choice-" + question.id,
            columns: [
                {stateId: "grid-question-choice-" + question.id + '-column-label', header: "{{i18n PLUGINS_FORMS_STATISTICS_CHOICE_LABEL}}", sortable: true, dataIndex: 'label', width: 600,
                    renderer: Ext.bind(this._renderLabelWithColor, this)
                },
                {stateId: "grid-question-choice-" + question.id + '-column-count', header: "{{i18n PLUGINS_FORMS_STATISTICS_CHOICE_COUNT}}", sortable: true, dataIndex: 'count', width: 100,
                    renderer: function (value, record) {
                        // BIG HACK because graph does not handle well the value 0.
                        if (value == 0.00000001)
                        {
                            return 0;
                        }
                        return value;
                    }
                },
                {stateId: "grid-question-choice-" + question.id + '-column-proportion', header: "{{i18n PLUGINS_FORMS_STATISTICS_CHOICE_PERCENT}}", sortable: true, dataIndex: 'count', width: 100, 
                    renderer: function(value) {
                        var percentage = (totalCount == 0) ? 0 : Math.round(value*100 / totalCount);
                        return percentage + '%';
                    }
                }
            ],
            forceFit: true,
            disableSelection: true
        });

		var graphPanel = Ext.create('Ext.chart.PolarChart', {
			region: 'east',
			width: 400,
			
			store: store,
			
		    series: [{
		    	type: 'pie',
		    	xField: 'count',
		    	label: {
		    		field: 'label',
		    		calloutLine: {
                    	color: 'rgba(0,0,0,0)' // Transparent to hide callout line
                    },
                    renderer: function(val) {
                        return ''; // Empty label to hide text
                    }
		    	},
                // BIG HACK because graph does not handle well the value 0.
                hidden: hiddenTab,
		    	tooltip: {
                    trackMouse: true,
                    renderer: function(tooltip, item) {
                    	var percentage = (totalCount == 0) ? 0 : Math.round(item.get('count')*100 / totalCount);
                    	var value = item.get('label');
                        var title = value;
                        if (value == '__internal_other')
                        {
                            title = "{{i18n plugin.forms:PLUGINS_FORMS_DISPLAY_OTHER_OPTION_COMBOBOX}}";
                        }
                        else if (value == '__internal_not_answered')
                        {
                            title = "{{i18n plugin.forms:PLUGINS_FORMS_STATISTICS_NOT_ANSWERED}}";
                        }
                        tooltip.setHtml(title + '<br/>' + item.get('count') + '<br/>' + percentage + '%');
                    }
                }
		    }]
			
		});
		
		var questionPanel = Ext.create('Ext.panel.Panel', {
			cls: 'form-question-panel question-choice',
	        title: title,
	        layout: 'border',
	        collapsible: true,
	        scrollable: false,
	        height: Math.max(200, 55+21 * choices.length),
	        items: [ gridPanel, graphPanel ]
		});
		
		return questionPanel;
	},
    
    /**
     * Render label to add color hint for legend grapg
     * @param {String} value The label.
     * @param {Object} record The record
     * @private
     */
    _renderLabelWithColor: function(value, record)
    {
        var colors = Ext.Factory.chartTheme("default").getColors()
        var index = record.recordIndex % colors.length;
        var title = value;
        if (value == '__internal_other')
        {
            title = "{{i18n plugin.forms:PLUGINS_FORMS_DISPLAY_OTHER_OPTION_COMBOBOX}}";
        }
        else if (value == '__internal_not_answered')
        {
            title = "{{i18n plugin.forms:PLUGINS_FORMS_STATISTICS_NOT_ANSWERED}}";
        }
        return "<span style=\"width:10px;height:10px;background-color:" + colors[index] + ";display: inline-block;border-radius:50%;margin-right:5px;margin-bottom:-1px;\"></span>" + title;
    },
	
	/**
	 * Gets the panel for a MATRIX question.
	 * @param {Object} question The question object.
	 * @param {String} title The title of this panel.
	 * @private
	 */
	_getMatrixQuestionPanel: function(question, title)
	{
		var data = [],
			yFields = [],
			titles = [];
		var columns = [{
			stateId: "grid-question-matrix-" + question.id + '-column-label', 
			header: "{{i18n PLUGINS_FORMS_STATISTICS_MATRIX_OPTION}}", 
			sortable: true, 
			dataIndex: 'label', 
			width: 600
		}];
	    
	    var choices = question.options[0].choices;
	    
	    for (var i = 0; i < choices.length; i++)
	    {
	    	columns.push({
	    		stateId: "grid-question-matrix-" + question.id + '-column-choice-' + choices[i].value, 
	    		header: this._renderLabelWithColor(choices[i].label, {recordIndex : i}), 
	    		sortable: true, 
	    		dataIndex: choices[i].value, 
	    		width: 100,
	    		renderer: function(value, metaData, record, rowIndex) {
	    			var percentage = (totalCount[rowIndex] == 0) ? 0 : Math.round(value*100 / totalCount[rowIndex]);
	    			return value + ' (' + percentage + '%' + ')';
	    		}
	    	});
	    	
	    	yFields.push(choices[i].value);
	    	titles.push(choices[i].label);
	    }
	    
	    var options = question.options,
	    	optionData,
	    	choices,
	    	totalCount = [];
	    for (var i = 0; i < options.length; i++) {
	    	optionData = {"label": options[i].label};
	    	
	    	choices = options[i].choices;
	    	totalCount[i] = 0;
	    	for (var j = 0; j < choices.length; j++) {
	    		count = parseInt(choices[j].count);
	    		totalCount[i] += count;
	    		optionData[choices[j].value] = count;
	    	}
	    	data.push(optionData);
	    }
	    
		var store = Ext.create('Ext.data.Store', {
			data: data
	    });
	    
		var gridPanel = Ext.create('Ext.grid.Panel', {
			cls: 'question-text-grid',
        	scrollable: true,
        	region: 'center',
        	store: store,
        	
        	stateful: true,
     		stateId: this.self.getName() + "$grid-question-matrix-" + question.id,
        	columns: columns,
        	forceFit: true,
        	disableSelection: true
		});
		
		var graphPanel = Ext.create('Ext.chart.CartesianChart', {
			region: 'east',
			width: 400,
			
			store: store,
			
			flipXY: true,
			axes: [{
		        type: 'numeric',
		        position: 'bottom',
		        grid: true,
		        minimum: 0
		    }, {
		        type: 'category',
		        position: 'left'
		    }],
		    
		    series: [{
		    	type: 'bar',
		    	title: titles,
		    	xField: 'label',
		    	yField: yFields,
		    	axis: 'bottom',
                tooltip: {
                    trackMouse: true,
                    renderer: Ext.bind(this._renderTooltipForMatrixGraph, this, [totalCount, choices], 1),
                }
		    }]
			
		});
		
		var questionPanel = Ext.create('Ext.panel.Panel', {
			cls: 'form-question-panel question-matrix',
	        title: title,
	        layout: 'border',
	        collapsible: true,
	        scrollable: false,
	        height: Math.max(250, 100+21 * options.length),
	        items: [ gridPanel, graphPanel ]
		});
		
		return questionPanel;
	},
	
    /**
     * Render tooltip for matrix graph
     * @param {Object} tooltip The tooltip.
     * @param {Object} totalCount The list of count for each choices.
     * @param {Object} choices The list of choices.
     * @param {Object} record The record.
     * @param {Object} serie The selected serie.
     * @private
     */
    _renderTooltipForMatrixGraph: function(tooltip, totalCount, choices, record, serie)
    {
        var html = "<ul>";
        for (var i in record.data)
        {
            var value = record.data[i];
            if (value != 0)
            {
                for (var j in choices)
                {
                    if (choices[j].value == i)
                    {
                        var label = choices[j].label;
                        var percentage = (totalCount[serie.index] == 0) ? 0 : Math.round(value*100 / totalCount[serie.index]);
                        html += "<li>" + label + ": " + value + " (" + percentage + "%)</li>";
                    }
                }
            }
        }
        html += "</ul>";
        tooltip.setHtml(html);
    },
    
	/**
     * Listener on creation message.
     * @param {Ametys.message.Message} message The creation message.
     * @private
     */
	_onMessageCreated: function(message)
	{
		var pageTarget = message.getTarget(Ametys.message.MessageTarget.FORM_PAGE);
		if (pageTarget && this._currentSelectionTargets && this._currentSelectionTargets.length > 0 && pageTarget.getParameters().formId == this._currentSelectionTargets[0].getParameters().id)
		{
			this.showOutOfDate();
		}
		
		var questionTarget = message.getTarget(Ametys.message.MessageTarget.FORM_QUESTION);
		if (questionTarget && this._currentSelectionTargets && this._currentSelectionTargets.length > 0 && questionTarget.getParameters().formId == this._currentSelectionTargets[0].getParameters().id)
		{
			this.showOutOfDate();
		}
	},
	
	/**
     * Listener on edition message.
     * @param {Ametys.message.Message} message The edition message.
     * @private
     */
	_onMessageModified: function(message)
	{
		var target = message.getTarget(Ametys.message.MessageTarget.FORM_TARGET);
		if (target && this._currentSelectionTargets && this._currentSelectionTargets.length > 0 && target.getParameters().id == this._currentSelectionTargets[0].getParameters().id)
		{
			this.showOutOfDate();
		}
	},
	
	/**
     * Listener on deletion message.
     * @param {Ametys.message.Message} message The deletion message.
     * @private
     */
	_onMessageDeleted: function(message)
	{
		var target = message.getTarget(Ametys.message.MessageTarget.FORM_TARGET);
		if (target && this._currentSelectionTargets && this._currentSelectionTargets.length > 0 && target.getParameters().id == this._currentSelectionTargets[0].getParameters().id)
		{
			this.close();
		}
	}
	
});