/*
 *  Copyright 2024 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.
 */

 /**
  * CoursePart tool panel
  * @private
  */
Ext.define('Ametys.plugins.odf.pilotage.tool.CoursePartsTreeGridPanel', {
    extend: 'Ametys.plugins.odf.tree.AbstractODFTreeGridPanel',
    
    mixins: {
        editContentsView: "Ametys.cms.content.EditContentsView"
    },
    
    constructor: function(config)
    {
        config.serverRole = config.serverRole || "org.ametys.plugins.odfpilotage.helper.CoursePartsTreeHelper";
        
        config.cls = Ext.Array.from(config.cls);
        config.cls.push("contentviewtreegrid");
        config.cls.push("treegrideditionfirst");
        config.cls.push("coursepartstreegrid");

        config.saveBarPosition = 0;

        var natureColumns = this._getNaturesColumns(config.natures, config.nbHours);
        config.columns = this._getGridConfig(natureColumns);
        
        config.methodArguments = ['contentId', 'path', 'tree'];
        
        this.callParent(arguments);

        this.mixins.editContentsView.constructor.call(this, config);
        this.getStore().on('load', this._onLoad, this);
    },
    
    _createEditPlugin: function(config) {
        let plugin = this.mixins.editContentsView._createEditPlugin.apply(this, arguments);
        plugin.editAfterSelect = false;
        return plugin;
    },
    
    _onLoad: function(store, records, successful, operation, eOpts)
    {
        // Checking if the new record is not about an existing record with the same contentId
        let modifiedContents = {}
        for (let modifiedRecord of this.store.getModifiedRecords()) // Maybe we should use this.getModifiedRecords() (from EditContentsView) to also include rootNode  
        {
            modifiedContents[this._getContentId(modifiedRecord)] = modifiedRecord;
        }
        for (let record of records)
        {
            this._loadRecord(record, modifiedContents);
        }
    },
        
    _loadRecord: function(record, modifiedContents) 
    {
        if (!modifiedContents || Object.keys(modifiedContents).length == 0)
        {
            return;
        }
        
        let modifiedRecord = modifiedContents[this._getContentId(record)];
        let changes;
        if (modifiedRecord)
        {
            changes = modifiedRecord.getChanges();
        }
        
        let changesKeys = changes ? Object.keys(changes) : null;
        if (changesKeys)
        {
            for (let changeFieldName of changesKeys)
            {
                if (modifiedRecord.get(changeFieldName + "_repeater") !== undefined)
                {
                    changes[changeFieldName + "_repeater"] = modifiedRecord.get(changeFieldName + "_repeater");
                }
                if (modifiedRecord.get(changeFieldName + "_content") !== undefined)
                {
                    changes[changeFieldName + "_content"] = modifiedRecord.get(changeFieldName + "_content");
                }
            }
            changesKeys = Object.keys(changes);
            
            for (let changeFieldName of changesKeys)
            {
                record.set(changeFieldName, changes[changeFieldName]);
            }
        }
        
        let me = this;
        window.setTimeout(function() {
            for (let childRecord of record.childNodes || [])
            {
                me._loadRecord(childRecord, modifiedContents);
            }
        }, 1)
    },    
        
    
    _getContentId: function(record)
    {
        return record.get('contentId');
    },
    
    _findRecords(contentId) {
        let records = [];
        
        // Using byIdMap allow to search even in filtered items
        for (let id in this.store.byIdMap)
        {
            if (this.store.byIdMap[id].get('contentId') == contentId)
            {
                records.push(this.store.byIdMap[id]);
            }
        }
        
        return records;        
    },
    
    _validateEdit: function (editor, context)
    {
        let me = this;
        
        let b = this.mixins.editContentsView._validateEdit.apply(this, arguments); // Cannot call callParent on mixins
        if (!b)
        {
            return false;
        }
        
        function _getOriginalValue(record, dataIndex)
        {
            if (record.modified && record.modified[dataIndex] !== undefined)
            {
                return record.modified[dataIndex];
            }
            else
            {
                return record.get(dataIndex);
            }
        }
        
        if (editor.activeEditor.field.getValue() == null && _getOriginalValue(context.record, context.column.dataIndex) != null && !me.forceComplete) 

        {
            Ametys.MessageBox.confirm ("{{i18n plugin.odf:PLUGINS_ODF_WIDGET_SELECT_COURSEPART_REMOVE_TITLE}}", 
                                "{{i18n plugin.odf:PLUGINS_ODF_WIDGET_SELECT_COURSEPART_REMOVE_CONFIRM}}",
                function (answer) {
                    if (answer == "yes")
                    {
                        if (editor.editing)
                        {
                            me.forceComplete = true;
                            editor.completeEdit();
                        }
                        else
                        {
                            context.record.set(context.column.dataIndex, null);
                        }
                    }
                    else
                    {
                        if (editor.editing)
                        {
                            editor.cancelEdit();
                        }
                    }
                }
            )
            
            return false;
        }
        
        me.forceComplete = false;
        return true;
    },
    
    _saveEditionWithContents: function(contents, callback)
    {
        Ametys.plugins.odf.pilotage.helper.ODFTreeGridPanelHelper.prototype._saveEditionWithContents.call(this, contents, callback);
    },
    
    /**
     * @private
     * continue the save edition process
     * @param {Object} response The response object
     * @param {Object} args The arguments of the sendMessage
     */
    _saveEditionCB: function(response, args)
    {
        var content = args['content'];
        var callback = args['callback'];
        
        // Object content may be outdated, just trust its id
        var contentId = content.getId();
        let records = this._findRecords(contentId);
        
        // If there are errors, show them
        if (response.errors && Object.keys(response.errors).length > 0)
        {
            let title = "{{i18n plugin.odf-pilotage:PLUGINS_ODF_PILOTAGE_TOOL_COURSEPARTS_SAVE_ACTION_ERROR}}";
            
            let contentTitle = records[0].data.title;
            
            // If the error is on the course level, show the error of the course
            if (response.errors.course)
            {
                Ametys.log.ErrorDialog.display({
                    title: title,
                    text: Ext.String.format("{{i18n plugin.odf-pilotage:PLUGINS_ODF_PILOTAGE_TOOL_COURSEPARTS_SAVE_ACTION_ERROR_COURSE_LOCKED}}", contentTitle)
                });
            }
            // If there are errors on the number of hours fields, show them
            else
            {
                let nbErrors = Object.keys(response.errors).length;
                var msg = Ext.String.format("{{i18n plugin.odf-pilotage:PLUGINS_ODF_PILOTAGE_TOOL_COURSEPARTS_SAVE_ACTION_FAILED_DESC}}", contentTitle);
                msg += nbErrors  == 1 ? "{{i18n plugin.odf-pilotage:PLUGINS_ODF_PILOTAGE_TOOL_COURSEPARTS_SAVE_ACTION_FAILED_DESC_SINGLE}}" : nbErrors + "{{i18n plugin.odf-pilotage:PLUGINS_ODF_PILOTAGE_TOOL_COURSEPARTS_SAVE_ACTION_FAILED_DESC_MULTI}}";
                
                var detailedMsg = "";
                for (let [changeKey, error] of Object.entries(response.errors))
                {
                    detailedMsg += error.title + " - " + error.errorMsg + "\n";
                }
                
                Ametys.log.ErrorDialog.display({
                    title: title,
                    text: msg,
                    details: detailedMsg
                });
            }
        }
        
        // If there are no successes, quit
        if (!response.success)
        {
            callback(false);
            return;
        }
        
        // graphically finish the save process
        for (let r of records) 
        {
            // Retrieve the values not to commit because they have errors
            let uncommittedValues = null;
            if (response.errors)
            {
                uncommittedValues = {};

                let revertValues = {};
                for (let keyModified of Object.keys(response.errors))
                {
                    uncommittedValues[keyModified] = r.get(keyModified);
                    // Revert the changes on theses values to be able to commit
                    revertValues[keyModified] = r.modified[keyModified];
                }
                r.set(revertValues);
            }
            
            // Commit the changes
            r.commit(null, Object.keys(r.modified)); 

            // Re set the values that could not be commited
            if (uncommittedValues)
            {
                r.set(uncommittedValues);
            }

        }
        
        this._showHideSaveBar();

        Ext.create("Ametys.message.Message", {
            type: Ametys.message.Message.MODIFIED,
            
            targets: {
                id: this.messageTarget,
                parameters: { ids: [contentId] }
            }
        });
        
        Ext.create("Ametys.message.Message", {
            type: Ametys.message.Message.LOCK_CHANGED,
            
            targets: {
                id: this.messageTarget,
                parameters: { ids: [contentId] }
            }
        });
        
        Ext.create("Ametys.message.Message", {
            type: Ametys.message.Message.WORKFLOW_CHANGED,
            
            targets: {
                id: this.messageTarget,
                parameters: { ids: [contentId] }
            }
        });

        callback(true);
    },
    
    /**
     * Function creating the JSON structure of hourly volumes grid
     * @param {Object} natures The list of hourly volumes codes
     * @private
     */
    _getNaturesColumns: function(natures, nbHours)
    {
        var json = [];
        var i = 0;
        for (var [key, nature] of Object.entries(natures))
        {
            let cfg = {
                text: key,
                editor: {
                    msgTarget: 'qtip',
                    cls: 'ametys',
                    xtype: Ametys.form.WidgetManager.getWidgetXType (nbHours.widget, nbHours.type.toLowerCase(), false, false),
                },
                stateId: nature.id,
                dataIndex: nature.id,
                align: 'center',
                sortable: false,
                renderer: this._renderVolumeOfHours,
                tooltip: nature.label,
                hidden: nature.archived,
                lockable: false
            };
            
            if (nbHours['widget-params'])
            {
                cfg.editor = Ext.apply(cfg.editor, nbHours['widget-params']);
            }
            
            json[i++] = cfg;
        }
        return json;
    },
    
    /**
     * Render the hourly volume column.
     * @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
     * @param {Number} rowIndex The index of the current row
     * @param {Number} cellIndex The index of the current cell
     * @param {Ext.data.Store} store The store
     * @param {Ext.view.View} view The current view
     * @private
     */
    _renderVolumeOfHours: function(value, metadata, record, rowIndex, cellIndex, store, view)
    {
        let headerCt = this.getHeaderContainer();
        let column = headerCt.getHeaderAtIndex(cellIndex);
        
        // Tooltip and style
        if (!(record.data['notEditableData'] === true)
            && !(record.data['notEditableDataIndex'] && Ext.Array.contains(record.data['notEditableDataIndex'], column.dataIndex))
            && !(record.data[column.dataIndex + '_external_status'] == 'external'))
        {
            metadata.tdCls += ' editable';
        }
        
        // If value === -1, that means that there are multiple CoursePart for the nature
        if (value === -1)
        {
            let valuesOfColumn = record.data.volumesOfHours[column.dataIndex]['values'];
            if (valuesOfColumn.length > 1)
            {
                let sum = 0;
                let i = 0;
                for (let valueInMap of valuesOfColumn)
                {
                    i++;
                    if (valueInMap != undefined)
                    {
                        sum += valueInMap;
                    }
                }
                
                var msgForToolTip = Ext.String.format("{{i18n plugin.odf-pilotage:PLUGINS_ODF_PILOTAGE_TOOL_COURSEPARTS_MULTIPLE_COURSEPARTS_MESSAGE}}", record.data.volumesOfHours[column.dataIndex].name, record.data.title);

                metadata.tdAttr = 'data-qtip="' + msgForToolTip + '"';
                
                // Show the sum of all the courseParts in the field
                return sum.toFixed(2);
            }
        }
        else
        {
            metadata.tdAttr = 'data-qtip=""'; // reset qtip if a previous value was set in another rendering
        }

        if (value != undefined)
        {
            return value.toFixed(2);
        }
    },
    
    /**
     * Function creating the structure of the columns grid 
     * @param {Object} natures the structure of the hourly volumes columns
     * @private
     */
    _getGridConfig: function(natureColumns)
    {
        let gridConfig = [{
            xtype: 'treecolumn',
            text: "{{i18n plugin.odf-pilotage:PLUGINS_ODF_PILOTAGE_TOOL_COST_MODELING_GRID_MODEL}}",
            stateId: 'title',
            sortable: false,
            dataIndex: 'title',
            width: 400,
            locked: true,
            lockable: false
        }];
        
        for (let natureColumn of natureColumns)
        {
            gridConfig.push(natureColumn);
        }
        
        return gridConfig;
    }
})