/*
 *  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.
 */
 
 /**
 * Actions on workflows
 */
Ext.define('Ametys.plugins.workflow.actions.WorkflowsListActions', {
    singleton: true,
    
    /**
    * @private
    * @property {String[]} _unavailableNames list of used workflow name
    */
    
    /**
    * @private
    * @property {Boolean} _idChanged false by default, set to true if user has typed in the id field 
    */
    
    /**
    * @private
    * @property {String} _mode 'new' or 'edit' 
    */
    
    /**
    * @private
    * @property {String} _workflowName id of currently selected workflow
    */
   
    /**
    * @private
    * @property {Ametys.window.DialogBox} _creationWorkflowBox the creation dialog box for workflows
    */
    
    /**
    * @private
    * @property {Ametys.window.DialogBox} _duplicateWorkflowBox the duplication dialog box for workflows
    */
    
    /**
    * @private
    * @property {Ametys.window.DialogBox} _editWorkflowBox the creation dialog box for workflows
    */
    
    /**
     * Open the workflowPreviewTool for this workflow element and the WorkflowEditorTool if it's not open
     * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
     */
    showWorkflow : function(controller)
    {
        var target = Ametys.message.MessageTargetHelper.findTarget(controller.getMatchingTargets(), Ametys.message.MessageTarget.WORKFLOW_OBJECT);
        if (!target)
        {
            return;
        }
        var targetParameters = target.getParameters();
        var workflowName = targetParameters.id;
        
        if (Ametys.tool.ToolsManager.getTool("uitool-workflow-editor$" + workflowName) == null)
        {
            Ametys.tool.ToolsManager.openTool("uitool-workflow-editor", {id: workflowName});
            Ametys.tool.ToolsManager.openTool("uitool-workflow-preview", {workflowId: workflowName, elementType: "workflow"});
        }
        else
        {
            var targetStep = target.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_STEP});
            if (targetStep)
            {
                var stepId = targetStep.getParameters().id;
                var stepLabel = targetStep.getParameters().label;
                var targetAction = targetStep.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_ACTION});
                if (targetAction)
                {
                    var actionId = targetAction.getParameters().id;
                    var actionLabel = targetAction.getParameters().label;
                    Ametys.tool.ToolsManager.openTool("uitool-workflow-preview", {workflowId: workflowName, elementType: "action", stepId: stepId, stepLabel: stepLabel, actionId: actionId, actionLabel: actionLabel});
                }
                else
                {
                    Ametys.tool.ToolsManager.openTool("uitool-workflow-preview", {workflowId: workflowName, elementType: "step", stepId: stepId, stepLabel: stepLabel});
                }
            }
            else
            {
                Ametys.tool.ToolsManager.openTool("uitool-workflow-preview", {workflowId: workflowName, elementType: "workflow"});
            }
        }
    },
    
    /**
     * Create a new  workflow
     * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
     */
    createWorkflow: function(controller)
    {
        this._mode = "new";
        Ametys.plugins.workflow.dao.WorkflowsDAO.getWorkflowInfos([null], Ext.bind(this._getWorkflowsNameCB, this)); 
    },
    
    /**
     * @private
     * Create a dialog box
     * @return {Ametys.window.DialogBox} the dialog box
     */
    _createDialogBox: function()
    {
        var dialogBox = Ext.create('Ametys.window.DialogBox',  {
            title : this._getTitle(),
            iconCls: this._getIcon(),
            width:400,
            
            layout: 'anchor',
            defaults:
            {
                msgTarget: "side",
                cls: 'ametys',
                padding: 5,
                anchor: '100%',
                labelAlign: "top",
                labelSeparator: "",
                labelStyle: "font-weight:bold",
            },
            items : this._getItems(),
            defaultFocus: 'label',
            selectDefaultFocus: true,
            closeAction: 'hide',
            
            referenceHolder: true,
            defaultButton: 'validate',
            
            buttons : [
                {
                    reference: 'validate',
                    text: "{{i18n PLUGINS_WORKFLOW_CREATE_DIALOG_OK_BTN}}",
                    handler: Ext.bind(this._ok, this)
                },
                {
                    text: "{{i18n PLUGINS_WORKFLOW_CREATE_DIALOG_CANCEL_BTN}}",
                    handler: Ext.bind(this._hideDialogBox, this)
                }
            ]
        });
        
        if (this._mode == "new")
        {
            var labelField = dialogBox.getComponent('label');
            var idField = dialogBox.getComponent('id');
            labelField.addListener("change", function(field, value) 
             {
                 if (!this._idChanged && value && Object.values(value).length > 0)
                 {
                     var label = Object.values(value)[0];
                     var id = (Ext.String.deemphasize(label)).replace(/[^a-z0-9]/gi, "-");
                     id = id.toLowerCase();
                     idField.setValue(id);
                 }
             }, this);
             idField.addListener("keyup", function(field, value) 
             {
                 this._idChanged = true;
             }, this);
        }
        
        return dialogBox;
    },
    
    /**
     * @private
     * Get the dialog box depending on the mode
     * @return {Ametys.window.DialogBox} the dialog box
     */
    _getDialogBox: function()
    {
        var box="";
        switch(this._mode)
        {
            case 'edit':
                box = this._editWorkflowBox;
                break;
            case 'new':
                box = this._creationWorkflowBox;
                break;
            case 'duplicate':
            default:
                box = this._duplicateWorkflowBox;
                break;
        }
        return box;
    },
    
    /**
     * @private
     * Get the dialog box title depending on the mode
     * @returns {String} the title
     */
    _getTitle: function()
    {
        var title="";
        switch(this._mode)
        {
            case 'edit':
                title = "{{i18n PLUGINS_WORKFLOW_RENAME_WORKFLOW_DIALOG_TITLE}}";
                break;
            case 'new':
                title = "{{i18n PLUGINS_WORKFLOW_CREATE_WORKFLOW_DIALOG_TITLE}}";
                break;
            case 'duplicate':
            default:
                title = "{{i18n PLUGINS_WORKFLOW_DUPLICATE_WORKFLOW_DIALOG_TITLE}}";
                break;
        }
        return title;
    },
    
    /**
     * @private
     * Get the dialog box title depending on the mode
     * @returns {String} the icon name
     */
    _getIcon: function()
    {
        var icon="";
        switch(this._mode)
        {
            case 'edit':
                icon = "ametysicon-text1";
                break;
            case 'new':
                icon = "ametysicon-add64";
                break;
            case 'duplicate':
            default:
                icon = "ametysicon-file229"
                break;
        }
        return icon;
    },
    
    /**
     * @private
     * Get common items between new and edit mode
     * @returns {Ext.form.field[]} the box items
     */
    _getItems: function()
    {
        var items = [
            {
                xtype: "multilingualstring",
                name: 'label',
                itemId: 'label',
                allowBlank: false,
                fieldLabel: "{{i18n PLUGINS_WORKFLOW_CREATE_WORKFLOW_DIALOG_LABEL}} *",
                ametysDescription: "{{i18n PLUGINS_WORKFLOW_CREATE_WORKFLOW_DIALOG_LABEL_DESC}}"
            }
        ];
        if (this._mode == "new" || this._mode == "duplicate")
        {
            items = items.concat(
                {
                    xtype: "textfield",
                    name: 'id',
                    itemId: 'id',
                    allowBlank: false,
                    fieldLabel: "{{i18n PLUGINS_WORKFLOW_CREATE_WORKFLOW_DIALOG_ID}} *",
                    ametysDescription: "{{i18n PLUGINS_WORKFLOW_CREATE_WORKFLOW_DIALOG_ID_DESC}}",
                    enableKeyEvents: true,
                    regex: /^[a-zA-Z0-9\-]*$/,
                    regexText: "{{i18n PLUGINS_WORKFLOW_INVALID_CHARACTER_WORKFLOW_NAME_ERROR}}",
                    validator: Ext.bind(this._idValidator, this)
                }
            );
        }
        return items;
    },
    
    /**
     * @private
     * Validator for id field
     * @param {String} id the id value to check
     * @returns {Object} true if pass validation, error msg if not
     */
    _idValidator: function(id)
    {
        return this._mode == "new" && this._unavailableNames.includes(id)
                ? "{{i18n PLUGINS_WORKFLOW_CREATE_WORKFLOW_DIALOG_ID_ERROR}}"
                : true;
    },
    
    /**
      * @private
      * Callback of getWorkflowsName set form values
      * @param {Object} response the server response
      * @param {Object} response.workflowNames list of already used workflow names
      */
     _getWorkflowsNameCB: function(response)
     {
         if (!this._creationWorkflowBox)
         {
             this._creationWorkflowBox = this._createDialogBox();
         }
         this._idChanged = false;
         
         this._unavailableNames = response.workflowNames;
         this._creationWorkflowBox.getComponent('id').reset();
         this._creationWorkflowBox.getComponent('label').reset();
         this._creationWorkflowBox.show();
     },
    
    /**
     * @private
     * Create or edit transition with field values 
     */
    _ok: function ()
    {
        var box = this._getDialogBox();
        if (this._hasInvalid(box))
        {
            return;
        }
        var labels = box.getComponent('label').getValue();
        
        if (this._mode == "edit")
        {
            Ametys.plugins.workflow.dao.WorkflowsDAO.renameWorkflow([this._workflowName, labels], Ext.bind(this._hideDialogBox, this));
        }
        else
        {
            this._workflowName = box.getComponent('id').getValue();
            if (this._mode == "new")
            {
                Ametys.plugins.workflow.dao.WorkflowsDAO.createWorkflow([labels, this._workflowName], Ext.bind(this._hideDialogBox, this));
            }
            else
            {
                Ametys.plugins.workflow.dao.WorkflowsDAO.duplicateWorkflow([this._workflowName, labels, this._duplicateId], Ext.bind(this._hideDialogBox, this));
            }
        }
    },
    
    /**
     * @private
     * Return true if a field is invalid
     * {Ametys.window.DialogBox} the dialog box
     */
    _hasInvalid: function(box)
    {
        var fields = box.query("[isFormField]");
        var hasInvalid = false;
        for (let field of fields)
        {
            if (!field.isValid())
            {
                hasInvalid = true;
            }
        }
        return hasInvalid;
    },

    /**
     * Hide the dialog box
     * @param {Object} response the server response
     * @param {String} response.message an error message if defined
     */
    _hideDialogBox: function (response)
    {
        if (!response || !response.message)
        {
            var dialogBox = this._getDialogBox();
            dialogBox.hide();
        }
    },
    
    /**
     * Rename the workflow
     * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
     */
     renameWorkflow: function(controller)
     {
        var target = Ametys.message.MessageTargetHelper.findTarget(controller.getMatchingTargets(), Ametys.message.MessageTarget.WORKFLOW_OBJECT);
        if (!target)
        {
            return;
        }
        var targetParameters = target.getParameters();
        this._workflowName = targetParameters.id;
        this._mode = "edit";
        
        Ametys.plugins.workflow.dao.WorkflowsDAO.getWorkflowInfos([this._workflowName], Ext.bind(this._getWorkflowLabelsCB, this));
     },
     
     /**
      * Edit an element of workflow
      * @param {Ametys.plugins.workflow.controllers.EditOrDeleteElementController} controller The controller calling this function
      */
     editElement: function(controller)
     {
         var stepTarget = Ametys.message.MessageTargetHelper.findTarget(controller.getMatchingTargets(), Ametys.message.MessageTarget.WORKFLOW_STEP);
         var functionTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_FUNCTION});
         if (functionTarget)
         {
             Ametys.plugins.workflow.actions.WorkflowFunctionAction.editFunction(functionTarget);
         }
         else
         {
             var conditionTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_CONDITION});
             if (conditionTarget)
             {
                 Ametys.plugins.workflow.actions.WorkflowConditionAction.editCondition(conditionTarget);
             }
             else
             {
                 var resultTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_RESULT});
                 if (resultTarget)
                 {
                     Ametys.plugins.workflow.actions.WorkflowResultAction.editConditionalResult(resultTarget);
                 }
                 else
                 {
                     var propertyTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_PROPERTY});
                     if (propertyTarget)
                     {
                         Ametys.plugins.workflow.actions.WorkflowEditionAction.editProperty(propertyTarget);
                     }
                     else
                     {
                         var actionTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_ACTION});
                         if (actionTarget)
                         {
                             Ametys.plugins.workflow.actions.WorkflowTransitionAction.edit(actionTarget);
                         }
                         else
                         {
                             Ametys.plugins.workflow.actions.WorkflowStepAction.edit(stepTarget);
                         }
                     }
                 }
             }
         }
     },
     
     /**
      * Delete an element of workflow
      * @param {Ametys.plugins.workflow.controllers.EditOrDeleteElementController} controller The controller calling this function
      */
     deleteElement: function(controller)
     {
         var stepTarget = Ametys.message.MessageTargetHelper.findTarget(controller.getMatchingTargets(), Ametys.message.MessageTarget.WORKFLOW_STEP);
         var functionTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_FUNCTION});
         if (functionTarget)
         {
             Ametys.plugins.workflow.actions.WorkflowFunctionAction.deleteFunction(functionTarget);
         }
         else
         {
             var conditionTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_CONDITION});
             if (conditionTarget)
             {
                 Ametys.plugins.workflow.actions.WorkflowConditionAction.deleteCondition(conditionTarget);
             }
             else
             {
                 var operatorTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_CONDITIONS_OPERATOR});
                 if (operatorTarget)
                 {
                     Ametys.plugins.workflow.actions.WorkflowConditionAction.deleteOperator(operatorTarget);
                 }
                 else
                 {
                     var resultTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_RESULT});
                     if (resultTarget)
                     {
                         Ametys.plugins.workflow.actions.WorkflowResultAction.deleteConditionalResult(resultTarget);
                     }
                     else
                     {
                         var propertyTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_PROPERTY});
                         if (propertyTarget)
                         {
                             Ametys.plugins.workflow.actions.WorkflowEditionAction.deleteProperty(propertyTarget);
                         }
                         else
                         {
                             var actionTarget = stepTarget.getSubtarget(function(target){return target.getId() == Ametys.message.MessageTarget.WORKFLOW_ACTION});
                             if (actionTarget)
                             {
                                 Ametys.plugins.workflow.actions.WorkflowTransitionAction.removeTransition(actionTarget, stepTarget);
                             }
                             else
                             {
                                 Ametys.plugins.workflow.actions.WorkflowStepAction.delete(stepTarget);
                             }
                         }
                     }
                 }
             }
         }
     },
     
     /**
      * @private
      * Callback of getWorkflowInfos set edit box value
      * @param {Object} response the server response
      * @param {Object} response.labels the multilingual labels defined for current workflow
      */
     _getWorkflowLabelsCB: function(response)
     {
         if (!this._editWorkflowBox)
         {
             this._editWorkflowBox = this._createDialogBox();
         }
         
         this._editWorkflowBox.getComponent('label').setValue(response.labels);
         this._editWorkflowBox.show();
     },
     
    /**
     * Save the changes on current workflow
     * @param {Ametys.plugins.workflow.controllers.SaveChangesController} controller The controller calling this function
     */
    saveChanges: function(controller)
    {
        var target = Ametys.message.MessageTargetHelper.findTarget(controller.getMatchingTargets(), Ametys.message.MessageTarget.WORKFLOW_OBJECT);
        if (!target)
        {
            return;
        }
        var workflowName = target.getParameters().id;
        Ametys.plugins.workflow.dao.WorkflowsDAO.saveChanges([workflowName]);
    },
    
    /**
     * Open confirm box to restore the workflow in the last saved state
     * @param {Ametys.plugins.workflow.controllers.SaveChangesController} controller The controller calling this function
     */
    reinit: function(controller)
    {
        var target = Ametys.message.MessageTargetHelper.findTarget(controller.getMatchingTargets(), Ametys.message.MessageTarget.WORKFLOW_OBJECT);
        if (!target)
        {
            return;
        }
        var workflowName = target.getParameters().id;
        
        Ametys.Msg.confirm("{{i18n UITOOL_WORKFLOW_EDITOR_REINIT_LABEL}}",
                Ext.String.format("{{i18n PLUGINS_WORKFLOW_REINIT_CONFIRM}}"),
                Ext.bind(this._doReinit, this, [workflowName], 1),
                this
        );
    },
    
    /**
     * @private
     * Restore the workflow in the last saved state
     * @param {String} btn The pressed button. Can only be 'yes'/'no'
     * @param {String} workflowName The workflow's unique name
     */
    _doReinit: function(btn, workflowName)
    {
        if (btn == 'yes')
        {
            Ametys.plugins.workflow.dao.WorkflowsDAO.reinit([workflowName]);
        }  
    },
    
    /**
     * Duplicate workflow in selection
     * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
     */
    duplicateWorkflow: function(controller)
    {
        var target = Ametys.message.MessageTargetHelper.findTarget(controller.getMatchingTargets(), Ametys.message.MessageTarget.WORKFLOW_OBJECT);
        if (!target)
        {
            return;
        }
        this._mode = "duplicate";
        this._duplicateId = target.getParameters().id;
        Ametys.plugins.workflow.dao.WorkflowsDAO.getWorkflowInfos([this._duplicateId ], Ext.bind(this._setDuplicateId, this)); 
    },
    
    /**
     * @private
     * Callback of getWorkflowsDuplicateInfos, set field values and show duplication dialog box
     * @param {Object} response the server response
     * @param {String[]} response.workflowNames the list of unavailable workflow names
     * @param {String[]} response.labels the workflow to duplicate's labels
     */
    _setDuplicateId: function(response)
    {
        if (!this._duplicateWorkflowBox)
        {
            this._duplicateWorkflowBox = this._createDialogBox();
        }
        this._unavailableNames = response.workflowNames;
        var increment = 1;
        var id = this._duplicateId + "-" + increment;
        while(this._unavailableNames.includes(id))
        {
            increment++;
            id = this._duplicateId + "-" + increment;
        }
        
        this._duplicateWorkflowBox.getComponent('label').setValue(response.labels);
        this._duplicateWorkflowBox.getComponent('id').setValue(id);
        this._duplicateWorkflowBox.show();
    }
});