/*
 *  Copyright 2021 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 class provides a widget to create a grid for matrixes
 */
Ext.define('Ametys.plugins.forms.widget.MatrixGrid', {
    extend : 'Ametys.form.AbstractField',
    
    xtype: 'matrix-grid',
    
    height: 250,
    
     /**
 	 * @private
 	 * @property {Ext.grid.Panel} _grid The grid containing the input choices.
 	 */
    constructor: function(config)
    {
    	this.callParent(arguments);
    },
    
    initComponent: function()
    {
        this._optGrid = this._createOptGrid();
		this._colGrid = this._createColGrid();
        
        this.items = [{xtype: 'container', height: 260, layout: { type: 'hbox', align: 'stretch' }, items: [this._optGrid, this._colGrid]}];

        this.layout = 'fit';
        this.cls = this.emptyCls;
        
        this.callParent(arguments);
    },
    
    getValue: function ()
    {
        var values = {};
        
        //Options
    	var options = {};
        this._optGrid.getStore().getData().each (function (record) {
        	options[record.get('value')] = record.get('label');
        });
        values.options = options;
        
        //Columns
        var columns = {};
        this._colGrid.getStore().getData().each (function (record) {
        	columns[record.get('value')] = record.get('label');
        });
        values.columns = columns;
        
        return Ext.JSON.encode(values);
    },
    
    setValue: function(value) 
    {   
		this.callParent(arguments);
		if (value != null)
		{
			var row = Ext.JSON.decode(value);
			var options = row.options;
			this._loadOptGridStore(options);
			var columns = row.columns;
			this._loadColGridStore(columns);
		}
    },
    
    /**
 	 * Fill the opt-grid with some values.
 	 * @param {Object} options The options to add in the opt-grid.
 	 * @private
 	 */
 	_loadOptGridStore: function(options)
 	{
 		var store = this._optGrid.getStore();
 		for (var key in options)
 		{
 			store.add({
 				label: options[key], 
 				value: key
 			});
 		}
 	},
 	
 	/**
 	 * Fill the col-grid with some values.
 	 * @param {Object} columns The columns to add in the col-grid.
 	 * @private
 	 */
 	_loadColGridStore: function(columns)
 	{
 		var store = this._colGrid.getStore();
 		for (var key in columns)
 		{
 			store.add({
 				label: columns[key], 
                value: key
 			});
 		}
 	},
    
    /**
 	 * Creates the bottom-left grid (options grid) of this dialog box.
 	 * @return {Ext.grid.Panel} The grid
 	 * @private
 	 */
 	_createOptGrid: function()
 	{
 		var columns = [
        	{
        		 xtype: 'widgetcolumn',
	             stateId: 'opt-grid-column-label', 
	             header: "{{i18n PLUGINS_FORMS_QUESTION_VALUE_SELECT_BOX_GRID_COL_LABEL}}",
	             menuDisabled: true,
	             sortable: false, 
	             dataIndex: 'label', 
	             widget: {
	                 xtype: 'textfield',
	                 listeners: {
	                    'change': Ext.bind(this._onWidgetChange, this, ['label'], 0),
	                    'focus': Ext.bind(this._onFocus, this)
	                 }
            	}
        	}
        ];
        
 		var grid = Ext.create('Ext.grid.Panel', {
 			flex: 0.5,
 			border: true,
	        scrollable: true,
	        
	        store: {
	        	fields: ['label', 'value'],
	        	trackRemoved: false
	        },
	        
	        columns: columns,
	        columnLines: true,
	        forceFit: true,
	        selModel: 'cellmodel',
	        
	        bbar: [
		        {
		        	xtype: 'button',
		        	itemId: 'addButton',
		        	flex: 1,
		        	text: "{{i18n PLUGINS_FORMS_QUESTIONS_DIALOG_CHOICE_BOX_GRID_ACTION_ADD_OPTION_LABEL}}",
		        	tooltip: "{{i18n PLUGINS_FORMS_QUESTIONS_DIALOG_CHOICE_BOX_GRID_ACTION_ADD_OPTION_DESCRIPTION}}",
		        	handler: Ext.bind(this._addOpt, this)
		        },
		        '-',
		        {
		        	xtype: 'button',
		        	itemId: 'removeButton',
		        	flex: 1,
		        	text: "{{i18n PLUGINS_FORMS_QUESTIONS_DIALOG_CHOICE_BOX_GRID_ACTION_DEL_OPTION_LABEL}}",
		        	tooltip: "{{i18n PLUGINS_FORMS_QUESTIONS_DIALOG_CHOICE_BOX_GRID_ACTION_DEL_OPTION_DESCRIPTION}}",
		        	handler: Ext.bind(this._removeOpt, this),
		        	disabled: true
		        }
	        ],
	        
	        listeners: {
	        	'selectionchange': Ext.bind(this._onOptSelectionChange, this),
	        	'edit': function(editor, context) {
	        		context.record.commit();
	        	}
	        }
 		});
 		
 		return grid;
 	},
    
 	/**
 	 * Creates the bottom-right grid (columns grid) of this dialog box.
 	 * @return {Ext.grid.Panel} The grid
 	 * @private
 	 */
 	_createColGrid: function()
 	{
 		var grid = Ext.create('Ext.grid.Panel', {
 			flex: 0.5,
 			border: true,
	        scrollable: true,
	        
	        store: {
	        	fields: ['label', 'value'],
	        	trackRemoved: false
	        },
	        
	        columns: [
	            {
	            	xtype: 'widgetcolumn',
	            	stateId: 'col-grid-column-label', 
	            	header: "{{i18n PLUGINS_FORMS_QUESTION_VALUE_SELECT_BOX_GRID_COL_COLUMN_LABEL}}", 
	            	menuDisabled: true,
	             	sortable: false, 
	            	dataIndex: 'label', 
	            	widget: {
		                 xtype: 'textfield',
		                 listeners: {
		                    'change': Ext.bind(this._onWidgetColumnChange, this, ['label'], 0),
		                    'focus': Ext.bind(this._onFocusColumn, this)
		                 }
	            	}
	            }
	        ],
	        columnLines: true,
	        forceFit: true,
	        selModel: 'cellmodel',
	        
	        bbar: [
		        {
		        	xtype: 'button',
		        	itemId: 'addButton',
		        	flex: 1,
		        	text: "{{i18n PLUGINS_FORMS_QUESTION_VALUE_SELECT_BOX_GRID_ACTION_ADD_COLUMN_LABEL}}",
		        	tooltip: "{{i18n PLUGINS_FORMS_QUESTION_VALUE_SELECT_BOX_GRID_ACTION_ADD_COLUMN_DESCRIPTION}}",
		        	handler: Ext.bind(this._addCol, this)
		        },
		        '-',
		        {
		        	xtype: 'button',
		        	itemId: 'removeButton',
		        	flex: 1,
		        	text: "{{i18n PLUGINS_FORMS_QUESTION_VALUE_SELECT_BOX_GRID_ACTION_DEL_LABEL}}",
		        	tooltip: "{{i18n PLUGINS_FORMS_QUESTION_VALUE_SELECT_BOX_GRID_ACTION_DEL_DESCRIPTION}}",
		        	handler: Ext.bind(this._removeCol, this),
		        	disabled: true
		        }
	        ],
	        
	        listeners: {
	        	'selectionchange': Ext.bind(this._onColSelectionChange, this),
	        	'edit': function(editor, context) {
	        		context.record.commit();
	        	}
	        }
 		});
 		
 		return grid;
 	},
 	
 	/**
     * @private
     * Listener when a widget option value has changed, in order to update the optGrid store.
     * @param {String} fieldName The name of the field to update in the record
     * @param {Ext.form.field.Field} widget The widget
     * @param {Object} newValue The new value
     */
    _onWidgetChange: function(fieldName, widget, newValue)
    {
        if (widget.getWidgetRecord)
        {
            var rec = widget.getWidgetRecord();
            if (rec != null)
            {
            	var currentPos = this._optGrid.getStore().indexOf(rec);
	            rec.set(fieldName, newValue);
	            rec.commit();
            }
        }
    },
    
    /**
     * Select the option row
     * @param {Object} widget this grid
     */
 	_onFocus: function(widget)
 	{
 		var record = widget.getWidgetRecord();
 		var recordRow = this._optGrid.getStore().indexOf(record);
 		this._optGrid.getSelectionModel().select(recordRow);
 		this._onOptSelectionChange();
 	},
 	
 	/**
     * @private
     * Listener when a widget column value has changed, in order to update the col grid store.
     * @param {String} fieldName The name of the field to update in the record
     * @param {Ext.form.field.Field} widget The widget
     * @param {Object} newValue The new value
     */
    _onWidgetColumnChange: function(fieldName, widget, newValue)
    {
        if (widget.getWidgetRecord)
        {
            var rec = widget.getWidgetRecord();
            if (rec != null)
            {
            	var currentPos = this._colGrid.getStore().indexOf(rec);
	            rec.set(fieldName, newValue);
	            rec.commit();
            }
        }
    },
    
    /**
     * Select the column row
     * @param {Object} widget this grid
     */
 	_onFocusColumn: function(widget)
 	{
 		var record = widget.getWidgetRecord();
 		var recordRow = this._colGrid.getStore().indexOf(record);
 		this._colGrid.getSelectionModel().select(recordRow);
 		this._onColSelectionChange();
 	},
 	
 	/**
 	 * Listener when the selection in the opt-grid has changed.
 	 * @private
 	 */
 	_onOptSelectionChange: function()
 	{
 		var sm = this._optGrid.getSelectionModel();
 		this._optGrid.down('#removeButton').setDisabled(!sm.hasSelection());
 	},
 	
 	/**
 	 * Listener when the selection in the col-grid has changed.
 	 * @private
 	 */
 	_onColSelectionChange: function()
 	{
 		var sm = this._colGrid.getSelectionModel();
 		this._colGrid.down('#removeButton').setDisabled(!sm.hasSelection());
 	},
 	
 	/**
 	 * Adds a new choice to the opt-grid, and starts the edition of its name.
 	 * @private
 	 */
 	_addOpt: function()
 	{
 		var store = this._optGrid.getStore(),
 			sm = this._optGrid.getSelectionModel(),
 			pos;
 			
 		if (sm.hasSelection())
 		{
 			pos = {row: sm.getPosition().rowIdx + 1, column: 0};
 		}
 		else
 		{
 			pos = {row: store.getCount(), column: 0};
 		}
 		
 		store.insert(pos.row, {
 			label: this._getUniqueLabel(this._optGrid, "{{i18n PLUGINS_FORMS_QUESTION_VALUE_SELECT_BOX_GRID_ACTION_ADD_DEFAULTLABEL}}"), 
 			value: this._getUniqueValue("opt", "opt_value", pos.row)
 		});
 		
 		var widgetCols = Ext.ComponentQuery.query('textfield',this._optGrid.getView());
        widgetCols[widgetCols.length - 1].focus(true)
 	},
 	
 	/**
 	 * Adds a new choice to the col-grid, and starts the edition of its name.
 	 * @private
 	 */
 	_addCol: function()
 	{
 		var store = this._colGrid.getStore(),
 			sm = this._colGrid.getSelectionModel(),
 			pos;
 			
 		if (sm.hasSelection())
 		{
 			pos = {row: sm.getPosition().rowIdx + 1, column: 0};
 		}
 		else
 		{
 			pos = {row: store.getCount(), column: 0};
 		}
 		
 		store.insert(pos.row, {
 			label: this._getUniqueLabel(this._colGrid, "{{i18n PLUGINS_FORMS_QUESTION_VALUE_SELECT_BOX_GRID_ACTION_ADD_COLUMN_DEFAULT_LABEL}}"), 
 			value: this._getUniqueValue("col", "col_value", pos.row)
 		});
 		
 		var widgetCols = Ext.ComponentQuery.query('textfield',this._colGrid.getView());
        widgetCols[widgetCols.length - 1].focus(true)
 	},
 	
 	/**
 	 * Removes the selected record of the col-grid.
 	 * @private
 	 */
 	_removeCol: function()
 	{
 		var store = this._colGrid.getStore(),
 			sm = this._colGrid.getSelectionModel();
 		
 		if (sm.hasSelection())
 		{
 			store.remove(sm.getSelection());
 		}
 	},
 	
 	/**
 	 * Removes the selected record of the opt-grid.
 	 * @private
 	 */
 	_removeOpt: function()
 	{
 		var store = this._optGrid.getStore(),
 			sm = this._optGrid.getSelectionModel();
 		
 		if (sm.hasSelection())
 		{
 			store.remove(sm.getSelection());
 		}
 	},
 	
 	/**
     * @private
     * Get a unique label for this option
     * @param {Object} grid the grid
     * @param {String} newLabel the new label
     * @return {String} a unique label
     */
    _getUniqueLabel: function(grid, newLabel)
    {
        var uniqueLabel = newLabel;
        var i = 1;
        var values = this._getGridLabels(grid);
        while (values.indexOf(uniqueLabel) != -1)
        {
            uniqueLabel = newLabel + " " + i;
            i++;
        }
        return uniqueLabel;
    },
    
    /**
     * @private
     * Get all the labels in the grid minus the row to ignore
     * @param {Object} grid the grid
     * @return {String[]} an array of all labels  
     */
    _getGridLabels : function(grid)
    {
        var gridLabels = [];
        var values = grid.getStore().getData().items;
        for (var index in values)
        {
            gridLabels.push(values[index].get('label'));
        }
        return gridLabels;
    },
 	
 	/**
 	 * @private
 	 * Get a unique value for this option
 	 * @param {String} value the default value
 	 * @param {Number} row position of the current cell
 	 * @return {String} a unique value
 	 */
 	_getUniqueValue: function(grid, newValue, row)
 	{
 		var uniqueValue = newValue;
 		var i = 1;
 		var values = this._getGridValues(grid, row);
		while (values.indexOf(uniqueValue) != -1)
		{
			uniqueValue = newValue + "_" + i;
			i++;
		}
 		return uniqueValue;
 	},
 	
 	/**
 	 * Get all the values in the grid minus the row to ignore
 	 * @param {Number} rowToIgnore row of current value
 	 * @return {String[]} an array of all the values to compare  
 	 */
 	_getGridValues : function(gridName, rowToIgnore)
 	{
 		var gridValues = [];
 		var grid = gridName == "opt" ? this._optGrid : this._colGrid;
 		var values = grid.getStore().getData().items;
 		for (var index in values)
 		{
 			if(index != rowToIgnore)
 			{
 				gridValues.push(values[index].get('value'));
 			}
 		}
 		return gridValues;
 	}
});