/*
 *  Copyright 2016 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 is a singleton class to open a dialog box for adding or edting a task
 * @private
 */
Ext.define('Ametys.plugins.coreui.schedule.EditTaskHelper', {
    singleton: true,
    
    /**
     * @private
     * @property {String} _mode The mode. Can only be 'add' or 'edit.
     */
    
    /**
     * @private
     * @property {String} _taskId If in edition mode, the id of the task being edited.
     */
    
    /**
     * @private
     * @property {Ametys.window.DialogBox} _box The dialog box.
     */
    
    /**
     * @private
     * @property {String} _separator The separator for the {@link Ametys.form.ConfigurableFormPanel}s of the dialog box
     */
    _separator: '/',
    
    /* Found on https://regex101.com/ , see also http://stackoverflow.com/questions/2362985/verifying-a-cron-expression-is-valid-in-java */
    /**
     * @private
     * @property {RegExp} _cronRegex The regular expression for cron validation
     */
    _cronRegex: /^\s*($|#|\w+\s*=|(\?|\*|(?:[0-5]?\d)(?:(?:-|\/|\,)(?:[0-5]?\d))?(?:,(?:[0-5]?\d)(?:(?:-|\/|\,)(?:[0-5]?\d))?)*)\s+(\?|\*|(?:[0-5]?\d)(?:(?:-|\/|\,)(?:[0-5]?\d))?(?:,(?:[0-5]?\d)(?:(?:-|\/|\,)(?:[0-5]?\d))?)*)\s+(\?|\*|(?:[01]?\d|2[0-3])(?:(?:-|\/|\,)(?:[01]?\d|2[0-3]))?(?:,(?:[01]?\d|2[0-3])(?:(?:-|\/|\,)(?:[01]?\d|2[0-3]))?)*)\s+(\?|\*|(?:0?[1-9]|[12]\d|3[01])(?:(?:-|\/|\,)(?:0?[1-9]|[12]\d|3[01]))?(?:,(?:0?[1-9]|[12]\d|3[01])(?:(?:-|\/|\,)(?:0?[1-9]|[12]\d|3[01]))?)*)\s+(\?|\*|(?:[1-9]|1[012])(?:(?:-|\/|\,)(?:[1-9]|1[012]))?(?:L|W)?(?:,(?:[1-9]|1[012])(?:(?:-|\/|\,)(?:[1-9]|1[012]))?(?:L|W)?)*|\?|\*|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:(?:-)(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?(?:,(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(?:(?:-)(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)*)\s+(\?|\*|(?:[0-6])(?:(?:-|\/|\,|#)(?:[0-6]))?(?:L)?(?:,(?:[0-6])(?:(?:-|\/|\,|#)(?:[0-6]))?(?:L)?)*|\?|\*|(?:MON|TUE|WED|THU|FRI|SAT|SUN)(?:(?:-)(?:MON|TUE|WED|THU|FRI|SAT|SUN))?(?:,(?:MON|TUE|WED|THU|FRI|SAT|SUN)(?:(?:-)(?:MON|TUE|WED|THU|FRI|SAT|SUN))?)*)(|\s)+(\?|\*|(?:|\d{4})(?:(?:-|\/|\,)(?:|\d{4}))?(?:,(?:|\d{4})(?:(?:-|\/|\,)(?:|\d{4}))?)*))$/,
    
    /**
     * Open the helper for creating/editing a task
     * @param {Object} [valuesToFill] If in edit mode, an object containing the data to fill the form
     * @param {String} [mode] The edition mode for the dialog box. Can only be 'add' (for creation) or 'edit' (for edition).
     * @param {String} [taskId] If in edition mode, the id of the task being edited.
     */
    open: function(valuesToFill, mode, taskId)
    {
        this._mode = mode || 'add';
        this._taskId = taskId;
        Ametys.plugins.core.schedule.Scheduler.getEditionConfiguration([], this._getFieldsCb, {scope: this, arguments: [valuesToFill]});
    },
    
    /**
     * @private
     * After retrieving from server the fields needed for the creation of a task, draw and show the box
     * @param {Object} response The server response
     * @param {Object[]} arguments The callback arguments
     */
    _getFieldsCb: function(response, arguments)
    {
        this._box = Ext.create('Ametys.window.DialogBox', {
            title: this._mode == 'add' ? "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_ADD_TITLE}}" : "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_EDIT_TITLE}}",
            iconCls: 'ametysicon-gear39',
            
            width: 600,
            maxHeight: 600,
            layout: {
                type: "vbox",
                align: "stretch"
            },
            
            defaultFocus: 'form',
            items: this._getItems(response.fireProcesses, response.schedulables),
            
            referenceHolder: true,
            defaultButton: 'validate',
            
            buttons:  [{
            	reference: 'validate',
                itemId: 'button-validate',
                text: "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_ACTIONS_VALIDATE}}",
                handler: Ext.bind(this.validate, this)
            }, {
                itemId: 'button-cancel',
                text: "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_ACTIONS_CANCEL}}",
                handler: Ext.bind(this.cancel, this)
            }]
        });
        
        this._box.show();
        
        if (this._mode == 'edit')
        {
            var valuesToFill = arguments[0];
            this._fillFields(valuesToFill);
        }
        else
        {
            this._getFormPanel().setValues({values: null});            
        }
    },
    
    /**
     * @private
     * Gets the items of the box
     * @param {Object} fireProcesses An array of objects containing the different fire processes
     * @param {Object[]} schedulables An array of objects containing information about creation of a type of task (called schedulable)
     * @return {Ext.Component[]} The items of the dialog box
     */
    _getItems: function(fireProcesses, schedulables)
    {
        var chooseSchedulableId = "schedulableId";
        
        var data = {};
        data[chooseSchedulableId] = {
            label: "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_SCHEDULABLE_LABEL}}",
            description: "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_SCHEDULABLE_DESCRIPTION}}",
            multiple: false,
            type: 'string',
            enumeration: [],
            validation: {
                mandatory: true
            },
            'can-not-write': this._mode == 'edit',
            'widget-params': {
                listeners: {
                    'change': function(combo, newSchedulableId, oldSchedulableId)
                    {
                        function _getSchedulableLabel(schedulableIdToFind)
                        {
                            if (schedulableIdToFind == null)
                            {
                                return null;
                            }
                            
                            return Ext.Array.findBy(combo.getStore().getRange(), function(record) {
	                            return record.get('value') === schedulableIdToFind;
	                        }).get('text');
                        }
                        
                        // Try to change the task label in accordance with the new selected Schedulable (only if it was not overridden by user)
                        var taskLabelField = this._getFormPanel().getField('label');
                        // only change if the current one is the "old default" or empty
                        var currentTaskLabel = taskLabelField.getValue();
                        var oldSchedulableLabel = _getSchedulableLabel(oldSchedulableId);
                        if (Ext.String.trim(currentTaskLabel) === "" || currentTaskLabel === oldSchedulableLabel)
                        {
                            var newSchedulableLabel = _getSchedulableLabel(newSchedulableId);
	                        taskLabelField.setValue(newSchedulableLabel);
                        }
                    },
                    scope: this
                }
            }
        };
        
        Ext.apply(data, {
            fireProcess: {
                label: "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_FIRE_PROCESS_LABEL}}",
                description: "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_FIRE_PROCESS_DESCRIPTION}}",
                multiple: false,
                type: "string",
                enumeration: fireProcesses,
                'default-value': 'NOW',
                validation: {
                    mandatory: true
                }
            },
            cron: {
                label: "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_CRON_LABEL}}",
                description: "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_CRON_DESCRIPTION}}",
                validation: {
                    mandatory: true,
                    regexp: this._cronRegex,
                    invalidText: "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_CRON_INVALID}}"
                },
                type: "string",
                disableCondition: {
                    condition: [{
                        id: "fireProcess",
                        operator: "neq",
                        value: "CRON"
                    }]
                }
            },
            label: {
                label: "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_LABEL_LABEL}}",
                description: "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_LABEL_DESCRIPTION}}",
                validation: {
                    mandatory: true
                },
                type: "string"
            },
            description: {
                label: "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_DESCRIPTION_LABEL}}",
                description: "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_DESCRIPTION_DESCRIPTION}}",
                type: "string",
                widget: "edition.textarea"
            },
            launchUser: {
                label: "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_LAUNCH_USER_LABEL}}",
                description: "{{i18n PLUGINS_CORE_UI_TASKS_DIALOG_LAUNCH_USER_DESCRIPTION}}",
                type: "user",
                widget: "edition.user"
            }
        });
        
        this._addParametersToData(data, schedulables, chooseSchedulableId);
        
        var formPanel = Ext.create('Ametys.form.ConfigurableFormPanel', {
            itemId: 'form',
            defaultFieldConfig: {
                labelWidth: 180
            },
            defaultPathSeparator: this._separator,
            scrollable: true,
            flex: 1
        });
        formPanel.configure(data);
        
        return [formPanel];
    },
    
    /**
     * @private
     * Create data for the configurable form panel
     * @param {Object} data The data object to modify for configuring the {@link Ametys.form.ConfigurableFormPanel}
     * @param {Object[]} schedulables An array of object containing the information for the fields
     * @param {String} chooseSchedulableId The name of the field for choosing the task type (some fields are disabled depending on the value of this field)
     */
    _addParametersToData: function(data, schedulables, chooseSchedulableId)
    {
        Ext.Array.forEach(schedulables, function(schedulable) {
            // Add an entry into the combobox for selecting the schedulable
            data[chooseSchedulableId].enumeration.push({
                label: schedulable.label,
                value: schedulable.id
            });
            
            // Add the fields for each parameter
            Ext.Object.each(schedulable.parameters, function(parameterId, parameter) {
                // Add a disable condition
                Ametys.form.Widget.addDisableCondition(parameter, chooseSchedulableId, "neq", schedulable.id, true)
                
                // The field is ready
                data[parameterId] = parameter;
            }, this);
            
        }, this);
    },
    
    /**
     * Handler when clicking on 'cancel' button
     */
    cancel: function()
    {
        this._box.hide();
    },

    /**
     * Handler for 'Ok' action
     */
    validate: function()
    {
        var isValidForm = this._getFormPanel().isValid();
        if (!isValidForm)
        {
            return;
        }
        
        var values = this._getFormValues();
        if (this._mode == 'add')
        {
            Ametys.plugins.core.schedule.Scheduler.add([values.label, values.description, values.fireProcess, values.cron, values.schedulableId, values.launchUser, values.params], this._validateCb, {scope: this});
        }
        else
        {
            Ametys.plugins.core.schedule.Scheduler.edit([this._taskId, values.label, values.description, values.fireProcess, values.cron, values.launchUser, values.params], this._validateCb, {scope: this});
        }
    },
    
    /**
     * @private
     * Gets the configurable form panel of the dialog box.
     * @return {Ametys.form.ConfigurableFormPanel} The configurable form panel
     */
    _getFormPanel: function()
    {
        return this._box.items.getByKey('form');
    },
    
    /**
     * @private
     * Gets the values of the form
     * @return {Object} The form values
     */
    _getFormValues: function()
    {
        var result = {};
        
        var formPanel = this._getFormPanel();
        var values = formPanel.getValues();
        
        // Extract label, id and model id
        result['label'] = values.label;
        result['description'] = values.description;
        result['launchUser'] = formPanel.getField('launchUser').getValue();
        result['fireProcess'] = values.fireProcess;
        result['cron'] = values.cron;
        result['schedulableId'] = values.schedulableId;
        
        // The parameters are the remaining entries
        delete values['label'];
        delete values['description'];
        delete values['launchUser'];
        delete values['fireProcess'];
        delete values['cron'];
        delete values['schedulableId'];
        result['params'] = values;
        
        return result;
    },
    
    /**
     * @private
     * Callback function called after the 'Ok' action is processed. Close the dialog box if no error.
     * @param {Object} response The server response
     */
    _validateCb: function(response)
    {
        if (!response.error)
        {
            this._box.close();
        }
    },
    
    /**
     * @private
     * Fills the forms of the dialog box with values.
     * @param {Object} valuesToFill The data to fill
     */
    _fillFields: function(valuesToFill)
    {
        var values = {};
        var schedulableId = valuesToFill['schedulableId'];
        values['label'] = valuesToFill['label'];
        values['description'] = valuesToFill['description'];
        values['launchUser'] = valuesToFill['launchUser'];
        values['fireProcess'] = valuesToFill['fireProcess'];
        values['cron'] = valuesToFill['cron'];
        values['schedulableId'] = schedulableId;
        Ext.Object.each(valuesToFill['params'], function(paramName, paramValue) {
            values[paramName] = paramValue;
        }, this);
        
        this._getFormPanel().setValues({values: values});
    }
});