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

/**
 * Methods for parsing comments in a script to determine and ask the parameters
 */
Ext.define('Ametys.plugins.coreui.script.ScriptParameters',
{
    singleton: true,
    
    hasScriptParameters: function(script)
    {
        let re = new RegExp("/\\*\\$(.*?)\\*/", "gs");
        let comment = re.exec(script);
        return comment != null;
    },
    
    /**
     * Display a parameter dialog if necessary
     * @param {String} script The script containing potential parameters
     * @param {Function} callback The callback to call when the parameters are valued
     * @param {String} [title] The title of the dialog
     * @param {String} [description] An introduction text to display in the dialog
     * @param {Object} [existingValues] The existing values stored in a previous session to display in the dialog}
     */
    askScriptParameters: function(script, callback, title, description, existingValues)
    {
        let conf = {};
        
        // 1 - trouver un ensemble de /*$ */
        let re = new RegExp("/\\*\\$(.*?)\\*/", "gs");
        while (true)
        {
            let comment = re.exec(script);
            if (!comment)
            {
                break;
            }
            let localConf = Ametys.plugins.coreui.script.ScriptParameters._parseComment(comment[1]);
            if (localConf)
            {
                conf = {...conf, ...localConf};
            }
        } 
        
        if (Object.keys(conf).length == 0)
        {
            callback();
            return;
        }
        
        let form = Ext.create("Ametys.form.ConfigurableFormPanel", {
            border : false,
            scrollable : false,

            labelAlign: 'top',

            defaultFieldConfig : {
                labelWidth : 130,
                anchor : '100%'
            },

            bodyStyle : {
                paddingLeft : 0,
                paddingTop: 0,
                paddingBottom: 0
            },

            additionalWidgetsConf: {
                standaloneEdition: true // standalone edition (use case: a popup without ribbon (for example richtext buttons) or FO edition)  
            },

            displayGroupsDescriptions: false
        });
        
        form.configure(conf);
        form.setValues(existingValues);
        
        let dialog = Ext.create("Ametys.window.DialogBox", {
            title: title || "{{i18n plugin.core-ui:PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_DIALOG_TITLE}}",
            iconCls: "ametysicon-computer196",
            
            width: 500,
            maxHeight: 400,
            
            scrollable: 'vertical',
            
            layout: 'anchor',
            defaultType: 'textfield',
            defaults: {
                cls: 'ametys',
                labelAlign: 'right',
                labelSeparator: '',
                labelWidth: 130,
                anchor: '100%',
                style: 'margin-right:' + (Ametys.form.ConfigurableFormPanel.OFFSET_FIELDSET + 10 + Ametys.form.ConfigurableFormPanel.PADDING_TAB) + 'px'
            },
            
            items: [{
                    xtype: 'component',
                    itemId: 'helpmessage',
                    cls: 'a-text',
                    html: description || "{{i18n plugin.core-ui:PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_DIALOG_DESCRIPTION}}"
                },
                form,
            ],

            selectDefaultFocus: true,
            closeAction: 'destroy',
            
            referenceHolder: true,
            defaultButton: 'validate',
            
            listeners:
            {
                scope: this,
                close: function() { }
            },
            
            buttons : [{
                reference: 'validate',
                text :"{{i18n plugin.core-ui:PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_DIALOG_BUTTON_OK}}",
                handler: Ext.bind(function() {
                    if (form.isValid())
                    {
                        let params = {};
                        
                        let formValues = form.getJsonValues();
                        for (let paramName of Object.keys(formValues))
                        {
                            params[paramName] = {
                                type: form.getField(paramName).type, // should be the same as conf[paramName].type, 
                                value: formValues[paramName]
                            };
                        }
                        
                        callback(params);
                        dialog.close();
                    }
                }, this), 
                scope: this
            }, {
                text :"{{i18n plugin.core-ui:PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_DIALOG_BUTTON_CANCEL}}",
                handler: Ext.bind(function() {
                    dialog.close();
                }, this) 
            }]
        });
        
        dialog.show();
    },

    __LINE_REGEXP: new RegExp(
          "^"
        + "\\s*\\*?\\s*"                      // Optional leading *  
        + "@param\\s+"                       // @param
        + "\\{([a-zA-Z0-9-]+)(\\[\\])?(;[^}]+)?\\}\\s+"  // {Type[];params}
        + "(\\[?)"                          // Optionnal opening bracket
        + "([$_a-zA-Z][$_a-zA-Z0-9]*)"      // Name
        + "(?:=("                           // Optionnal default value      
                + "null|undefined|"             // Null or undefined   
                + "true|false|"                 // Boolean
                + "\"[^\"]*\"|'[^']*'|"         // String 
                + "[0-9._]+|"                   // Long or Double
                + "\\[.*?\\]|\\{.*\\}"        // Object or Array
        + "))?"
        + "(\\]?)"                          // Optionnal closing bracket
        + "(?:\\s+(.+))"                     // Description
        + "$"
    ),
    __EMPTY_LINE_REGEXP: new RegExp("^\\s*\\*\\s*$"),
    __TEXT_LINE_REGEXP: new RegExp("^\\s*\\*\\s*([^@]+)$"),
    
    _parseComment: function(comment)
    {
        
        let conf = {};
        let lastLocalConf = null;
        
        // 2 - pour chaque ensemble, trouver chaque ligne
        let params = comment.split(/\r?\n/);
        for (let param of params)
        {
            if (param.trim().length > 0) 
            {
                // 3 - pour chaque ligne, trouver le nom, la valeur par défaut, le type, la description...
                let result;
                if (result = Ametys.plugins.coreui.script.ScriptParameters.__EMPTY_LINE_REGEXP.exec(param))
                {
                    // nothing
                }
                else if (result = Ametys.plugins.coreui.script.ScriptParameters.__TEXT_LINE_REGEXP.exec(param))
                {
                    // description of the preceding if any
                    let text = result[1];
                    if (lastLocalConf)
                    {
                        lastLocalConf.description = text + (lastLocalConf.description ? "<br/>" + lastLocalConf.description : "");
                    }
                }                
                else if (result = Ametys.plugins.coreui.script.ScriptParameters.__LINE_REGEXP.exec(param))
                {
                    let type = result[1];
                    let multiple = result[2] == "[]";
                    let typeParams = result[3];
                    let openingBracket = result[4];
                    let name = result[5];
                    let defaultValue = result[6];
                    let closingBracket = result[7];
                    let label = result[8];
                    
                    if (openingBracket && !closingBracket || !openingBracket && closingBracket)
                    {
                        Ametys.log.ErrorDialog.display({
                            title: "{{i18n PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS}}", 
                            text: "{{i18n PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_ERROR}}<br/><code>" + param + "</code>",
                            category: "Ametys.plugins.coreui.script.ScriptParameters"
                        });
                        throw new Error("Cannot parse the following param in variable comment (bracket issue): " + param);
                    }
                    
                    let optional = openingBracket && closingBracket;
                    
                    let params = {};
                    if (typeParams)
                    {
                        for (let p of typeParams.split(";"))
                        {
                            if (p.trim().length > 0)
                            {
                                let i = p.indexOf(":");
                                if (i == -1)
                                {
                                    Ametys.log.ErrorDialog.display({
                                        title: "{{i18n PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS}}", 
                                        text: "{{i18n PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_ERROR}}<br/><code>" + param + "</code>",
                                        category: "Ametys.plugins.coreui.script.ScriptParameters"
                                    });
                                    throw new Error("Cannot read the following type parameter: " + p);
                                }
                                params[p.substring(0, i)] = p.substring(i + 1);
                            }
                        }
                    }
                    
                    lastLocalConf = Ametys.plugins.coreui.script.ScriptParameters._convertToConf(optional, multiple, defaultValue, type, params, label); 
                    conf[name] = lastLocalConf;
                }
                else
                {
                    Ametys.log.ErrorDialog.display({
                        title: "{{i18n PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS}}", 
                        text: "{{i18n PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_ERROR}}<br/><code>" + param + "</code>",
                        category: "Ametys.plugins.coreui.script.ScriptParameters"
                    });
                    throw new Error("Cannot parse the following line in variable comment: " + param);
                }
            }
        }
        
        return conf; 
    },

    _convertToConf: function(optional, multiple, defaultValue, type, typeParams, description)
    {
        let lcType = type.toLowerCase();
        let typedDefaultValue;
        try
        {
            if (defaultValue !== undefined)
            {
                typedDefaultValue = JSON.parse(defaultValue);
            }
        }
        catch (e)
        {
            Ametys.log.ErrorDialog.display({
                title: "{{i18n PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_DEFAULTVALUE}}", 
                text: "{{i18n PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_DEFAULTVALUE_ERROR}}<br/><code>" + defaultValue + "</code>",
                details: e,
                category: "Ametys.plugins.coreui.script.ScriptParameters"
            });
            throw new Error("Error with default value " + defaultValue)
        }    
        
        return {
            "label": description,
            "description": (!!defaultValue ? "{{i18n plugin.core-ui:PLUGINS_CORE_UI_TOOLS_SCRIPT_PARAMETERS_DIALOG_DEFAULTVALUE}} " + defaultValue : undefined),
            "type": lcType,
            "multiple": multiple,
            "default-value": typedDefaultValue,
            "widget": typeParams ? typeParams.widget : undefined,
            "widget-params": typeParams,
            "validation": {
                "mandatory": !optional
            }
        }
    }
});