/*
 *  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 controls a ribbon button for adding a task.
 * @private
 */
Ext.define('Ametys.plugins.coreui.schedule.AddTaskButtonController', {
    extend: 'Ametys.ribbon.element.ui.ButtonController',
    
    /**
     * @cfg {String} [taskLabel] The label of the task which will be created. Default to the controller label.
     */
    
    /**
     * @cfg {String} [taskDescription] The description of the task which will be created. Default to the controller description.
     */
    
    /**
     * @cfg {String} schedulable The id of the schedulable to create
     */
    
    /**
     * @cfg {Object} [schedulable-parameters] The parameters of the schedulable
     */
    
    /**
     * @cfg {String} [fire-process="NOW"] The way to fire the runnable. If equals to "CRON", then {@link #cfg-cron} must be non-null
     */
    
    /**
     * @cfg {String} [cron] The cron expression, only needed if {@link #cfg-fire-process} equals to "CRON"
     */
    
    /**
     * @cfg {Boolean} [log-tool-active=true] true to open the log tool when scheduling the task
     */
    
    /**
     * @cfg {String} [log-title] The title of the log tool to open when scheduling the task
     */
    
    /**
     * @cfg {String/String[]} [log-category] The logging categories for the ServerLog tool
     */
    
    /**
     * @cfg {String} [confirm-title] The title of the confirm dialog box. Must be non-null if {@link #cfg-confirm-msg} is non-null. If one is null, there will be no confirm dialog box.
     */
    
    /**
     * @cfg {String} [confirm-msg] The message content of the confirm dialog box. Must be non-null if {@link #cfg-confirm-title} is non-null. If one is null, there will be no confirm dialog box.
     */
    
    /**
     * @cfg {Boolean} [check-job-state=false] true to check te job state and send message bus when the task ends
     */
    
    /**
     * @cfg {Number} [check-job-state-delay=1000] time between 2 checks of the job state
     */
    
    /**
     * @property {Object} allParameters The declared parameters (already prefixed) of the associated schedulable
     */
    
    inheritableStatics: {
        /**
         * Add the configured task
         * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
         */
        act: function(controller)
        {
            // If some params of the schedulable are not given from the configuration, display a dialog box to the user 
            var fromConfSchedulableParameters = {},
                schedulableId = controller.schedulable;
            Ext.Object.each(controller['schedulable-parameters'] || {}, function(key, value) {
                fromConfSchedulableParameters[schedulableId + "$" + key] = value;
            });
            
            // Params already set in Ametys app-parameters
            var appParams = Ametys.getAppParameters();
            for (var key in appParams)
            {
                if (!fromConfSchedulableParameters[schedulableId + "$" + key])
                {
                    fromConfSchedulableParameters[schedulableId + "$" + key] = appParams[key];
                }
            }
            
            var nonGivenParamNames = Ext.Array.difference(Ext.Object.getKeys(controller.allParameters), Ext.Object.getKeys(fromConfSchedulableParameters));
            
            if (nonGivenParamNames.length == 0)
            {
                this._scheduleTask(controller, fromConfSchedulableParameters);
            }
            else
            {
                var data = {};
                Ext.Array.forEach(nonGivenParamNames, function(paramName) {
                    data[paramName] = controller.allParameters[paramName];
                });
                var formPanel = Ext.create('Ametys.form.ConfigurableFormPanel', {
                    itemId: 'form',
                    defaultFieldConfig: {
                        labelWidth: 180
                    },
                    defaultPathSeparator: this._separator,
                    scrollable: true,
                    flex: 1
                });
                formPanel.configure(data);
                formPanel.setValues(); // setValues must always be called for configurable form panel in order to complete its initialization
                
                Ext.create('Ametys.window.DialogBox', {
                    title: controller['get-parameters-title'] || controller.taskLabel || controller.label,
                    iconCls: controller['icon-glyph'],
                    
                    width: 550,
                    layout: {
                        type: "vbox",
                        align: "stretch"
                    },
                    
                    defaultFocus: 'form',
                    items: [{
                        xtype: 'component',
                        html: controller['get-parameters-msg'] || controller.taskDescription || '{{i18n PLUGINS_CORE_UI_TASKS_ADD_TASK_BUTTON_CONTROLLER_DIALOG_GET_PARAMETERS_MSG}}'
                    }, formPanel],
                    
                    referenceHolder: true,
                    defaultButton: 'validate',
                    closeAction: 'destroy',
                    
                    buttons:  [{
                        reference: 'validate',
                        itemId: 'button-validate',
                        text: "{{i18n PLUGINS_CORE_UI_TASKS_ADD_TASK_BUTTON_CONTROLLER_DIALOG_GET_PARAMETERS_BTN_OK}}",
                        handler: validate,
                        scope: this
                    }, {
                        itemId: 'button-cancel',
                        text: "{{i18n PLUGINS_CORE_UI_TASKS_ADD_TASK_BUTTON_CONTROLLER_DIALOG_GET_PARAMETERS_BTN_CANCEL}}",
                        handler: cancel
                    }]
                })
                .show();
                
                function validate(button)
                {
                    if (formPanel.isValid())
                    {
                        var fromUserSchedulableParameters = formPanel.getValues();
                        this._scheduleTask(controller, Ext.apply(fromUserSchedulableParameters, fromConfSchedulableParameters));
                        button.up('[xtype=dialog]').close();
                    }
                    else
                    {
                        var invalidFields = Ext.Array.filter(formPanel.getFieldNames(), function(fieldName) {
                            return !formPanel.getField(fieldName).isValid();
                        });
                        if (invalidFields.length > 0)
                        {
                            formPanel.getField(invalidFields[0]).focus();
                        }
                    }
                }
                
                function cancel(button)
                {
                    button.up('[xtype=dialog]').close();
                }
            }
        },
        
        /**
         * @protected
         * Launch the scheduling of the task (possibly with a confirm dialog box)
         * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
         * @param {Object} params The parameters to pass to the scheduler (prefixed)
         */
        _scheduleTask: function(controller, params)
        {
            var confirmTitle = controller['confirm-title']; 
            var confirmMsg = controller['confirm-msg'];
            function callback(answer)
            {
                if (answer == 'yes')
                {
                    var taskLabel = controller.taskLabel || controller.label;
                    var fireProcess = controller.fireProcess || "NOW";
                    
                    controller.serverCall(
                        "add",
                        [taskLabel, controller.taskDescription || controller.description, fireProcess, controller.cron, controller.schedulable, params],
                        this._scheduleTaskCb, 
                        {
                            errorMessage: true,
                            arguments:
                            {
                                categories: Ext.Array.from(controller['log-category']),
                                title: controller['log-title'],
                                logToolActive: controller['log-tool-active'] != false && controller['log-tool-active'] != "false",
                                checkState: controller['check-job-state'] == true || controller['check-job-state'] == "true",
                                checkStateDelay: controller['check-job-state-delay'] || 1000,
                                fireProcess: fireProcess,
                                taskLabel: taskLabel
                            }
                        }
                    );
                }
            }
            
            if (confirmTitle != null && confirmMsg != null)
            {
                Ametys.Msg.confirm(
                    confirmTitle,
                    confirmMsg,
                    callback,
                    this
                );
            }
            else
            {
                callback.call(this, 'yes');
            }
        },
        
        /**
         * @protected
         * Callback method for schedule task
         * @param {Object} response The server response
         * @param {Object} arguments The callback arguments
         * @param {String[]} arguments.categories The logging categories
         * @param {String} arguments.title The title of the log tool
         * @param {Boolean} arguments.logToolActive true to open the log tool
         * @param {Boolean} arguments.checkState true to check the state of the scheduled task
         * @param {Number} arguments.checkStateDelay The time between 2 checks of the state
         * @param {String} arguments.fireProcess The way used to fire the runnable
         * @param {String} arguments.taskLabel The label of the task
         */
        _scheduleTaskCb: function(response, arguments)
        {
            this._openTools(response, arguments);
            
            if (arguments.checkState && arguments.fireProcess == "NOW")
            {
                Ext.create('Ametys.message.Message', {
                    type: Ametys.message.Message.TASK_STARTED,
                    targets: [{
                        id: Ametys.message.MessageTarget.TASK,
                        parameters: {
                            id: response.id
                        }
                    }]
                });
            
                this._checkTaskState(response.id, arguments);
            }
        },
    },
    
    /**
     * @protected
     * Open the tools in callback
     * @param {Object} response The server response
     * @param {Object} arguments The callback arguments
     * @param {String[]} arguments.categories The logging categories
     * @param {String} arguments.title The title of the log tool
     * @param {Boolean} arguments.logToolActive true to open the log tool
     */
    _openTools: function(response, arguments)
    {
        function select(store, records, tool, recordId)
        {
            var record = store.getById(recordId);
            if (record != null)
            {
                tool.getGrid().getSelectionModel().select([record]);
            }
        }
        
        if (arguments.logToolActive)
        {
            Ametys.tool.ToolsManager.openTool('uitool-server-logs', {id: arguments.category, serverId: this.getId(), category: arguments.categories, title: arguments.title}, "cr");
        }
        
        if (Ametys.tool.ToolsManager.getFactory("uitool-scheduled-tasks") != null) // May have no right on scheduler
        {
            var scheduledTasksTool = Ametys.tool.ToolsManager.openTool('uitool-scheduled-tasks', {}, "cl");
            if (scheduledTasksTool)
            {
                scheduledTasksTool.getGrid().getStore().on("load", Ext.bind(select, this, [scheduledTasksTool, response.id], 2), this, {single: true});
            }
        }
    },
    
    /**
     * @protected
     * Checks the state of the task and send appropriate events on message bus
     * @param {String} taskId the tesk identifier
     * @param {Object} arguments The callback arguments
     * @param {Number} arguments.checkStateDelay The time between 2 checks of the state
     * @param {String} arguments.taskLabel The label of the task
     */
    _checkTaskState: function(taskId, arguments)
    {
        Ametys.plugins.core.schedule.Scheduler.getTasksInformation(
            [[taskId]], 
            this._checkTaskStateCb, 
            {
                errorMessage: Ext.String.format("{{i18n PLUGINS_CORE_UI_TASKS_ADD_TASK_BUTTON_CONTROLLER_CHECK_STATE_ERROR}}", arguments.taskLabel),
                scope: this, 
                arguments: arguments
            }
        );
    },
    
    /**
     * @protected
     * Callback method for checking the task state
     * @param {Object} response The server response
     * @param {Object} arguments The callback arguments
     * @param {Number} arguments.checkStateDelay The time between 2 checks of the state
     * @param {String} arguments.taskLabel The label of the task
     */
    _checkTaskStateCb: function(response, arguments)
    {
        if (response.length > 0)
        {
            var task = response[0];
            if (task.getState() == "running")
            {
                Ext.defer(this._checkTaskState, arguments.checkStateDelay, this, [task.getId(), arguments]);
            }
            else if (task.getState() == "success" || task.getState() == "failure")
            {
                Ext.create('Ametys.message.Message', {
                    type: Ametys.message.Message.TASK_ENDED,
                    parameters: {
                        state: task.getState()
                    },
                    targets: [{
                        id: Ametys.message.MessageTarget.TASK,
                        parameters: {
                            tasks: [task]
                        }
                    }]
                });
            }
            else if (task.getState() == "waiting")
            {
                Ext.create('Ametys.message.Message', {
                    type: Ametys.message.Message.TASK_AWAITING,
                    targets: [{
                        id: Ametys.message.MessageTarget.TASK,
                        parameters: {
                            tasks: [task]
                        }
                    }]
                });
            }
            // We do not add a message bus for disabled tasks because it  should not happen here
        }
    },
    
    constructor: function(config)
    {
        this.callParent(arguments);
        this._configureSchedulerParameters(config);
    },
    
    /**
     * @protected
     * Method to configure scheduler parameters.
     * @param {Object} config The constructor configuration
     */
    _configureSchedulerParameters: function(config)
    {
        this.serverCall("getParameters", [config.schedulable], function(parameters) {
            this.allParameters = parameters;
        }, {scope: this, errorMessage: true});
    },
    
    getServerId: function()
    {
        // When used as a generated menu, get the server id from the primary menu item itself for callable methods.
        // Indeed the client side element of a generated menu is a org.ametys.core.ui.SimpleMenu
        // instead of a org.ametys.core.ui.AddTaskClientSideElement 
        return this.getInitialConfig("primary-menu-item-id") || this.callParent(arguments);
    }
});