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

/**
 * Singleton class checking the validity of a {@link Ametys.form.ConfigurableFormPanel} in order to save it
 */
Ext.define('Ametys.form.SaveHelper', {
    singleton: true,
    
    /**
     * Get the form from message targets
     * @param {Ametys.message.MessageTarget[]} targets the message targets
     * @return the form is found, null otherwise
     */
    getForm: function(targets)
    {
        var target = targets[0];
        if (target != null)
        {
            var subtargets = target.getSubtargets(function(target) { return target.getId() ==  Ametys.message.MessageTarget.FORM }, 0);
            if (subtargets.length == 0)
            {
            	Ext.MessageBox.alert("{{i18n PLUGINS_CORE_UI_SAVE_NOFORM_TITLE}}", "{{i18n PLUGINS_CORE_UI_CONFIG_SAVE_NOFORM}}");
                return null;
            }

            return subtargets[0].getParameters().object.owner;
        }
        
        return null;
    },
    
    /**
     * Dialog asking to save current modifications.
     * @param {String} title the title of the dialog box. Set to null in order to use a generic title
     * @param {String} message the explanatory text of the dialog box. Set to null in order to use a generic description
     * @param {String} icon the path of the icon to display within the dialog box. Set to null in order to use a generic icon
     * @param {Function} callback the function invoked after the user selected one of the three above choices.
     * @param {Boolean} callback.save true means the user want to save. false means the user does not want to save. null means the user does not want to save nor quit.
     */
    promptBeforeQuit: function(title, message, icon, callback)
    {	
    	Ametys.form.SaveHelper.SaveBeforeQuitDialog.showDialog(title, message, icon, callback);
    },
    
    /**
     * Determine if a form is ready to be saved. This function is asynchronous since some user interaction may be required.
     * @param {Ametys.form.ConfigurableFormPanel} form the configurable form panel
     * @param {Function} callback function invoked after the saving process was authorized or not
     * @param {Function} callback.canSave true if the form can be saved.
     */
    canSave: function(form, callback)
    {
        var me = this,
	        fieldCheckersManager = form._fieldCheckersManager,
	        fieldCheckers = fieldCheckersManager._fieldCheckers;
        
		// Validate form
		var invalidFields = Ext.Array.merge(form.getInvalidFields(), form.getInvalidRepeaters());
		if (invalidFields.length > 0)
		{
            // At least one field is invalid
			Ametys.Msg.show({
				   title: "{{i18n PLUGINS_CORE_UI_SAVE_ACTION_INVALID_TITLE}}", 
				   msg: "{{i18n PLUGINS_CORE_UI_SAVE_ACTION_INVALIDFIELDS}}" + this.getInvalidFieldsAsReadableList(invalidFields),
				   buttons: Ext.Msg.OK,
				   icon: Ext.MessageBox.ERROR,
				   handler: function() {callback(false);}  
			});
		}
        else if (!this._areTestsOk(fieldCheckers))
        {
            // At least one test wasn't successful
            var msgBox = Ext.create('Ext.window.MessageBox', {closeAction: 'destroy'});
            
            msgBox.buttonText.yes = "{{i18n PLUGINS_CORE_UI_SAVE_TESTS_NOK_MBOX_SAVE}}";
            msgBox.buttonText.no = "{{i18n PLUGINS_CORE_UI_SAVE_TESTS_NOK_MBOX_RETRY}}";
            msgBox.buttonText.cancel = "{{i18n PLUGINS_CORE_UI_SAVE_TESTS_NOK_MBOX_CANCEL}}";
            msgBox.show({
                title: "{{i18n PLUGINS_CORE_UI_SAVE_TESTS_NOK_MBOX_TITLE}}", 
                msg: "{{i18n PLUGINS_CORE_UI_SAVE_TESTS_NOK_MBOX_MSG}}",
                buttons: Ext.Msg.YESNOCANCEL,
                icon: Ext.Msg.WARNING,
                fn: function(answer)
                {
                    if (answer == 'yes')
                    {
                    	this._handleWarnedFields(form, callback);
                    }
                    else if (answer == 'no')
                    {
                        Ext.getBody().mask("{{i18n PLUGINS_CORE_UI_SAVE_WAIT_MSG}}");
                        fieldCheckersManager.check(null, 
	            					               true, 
	            					               Ext.bind(function(success) 
	    					            		   { 
	        					            	  	 Ext.getBody().unmask(); 
	        					            	  	 if (success) 
	        					            	  	 { 
	        					            		 	this._handleWarnedFields(form, callback);
	    					            	  		 }  
	    					            		   }, me), false);
                    }
                },
                scope: this
            });
        }
        // All tests passed, check the warning on fields
        else
    	{
        	this._handleWarnedFields(form, callback);
    	}
		
        
        // Force the rendering of errors / warnings
        form._updateTabsStatus(true);
    },
    
    /**
     * Retrieves the given invalid fields in a displayable list to put in error dialog boxes
     * @param {Array} invalidFields The invalid fields
     * @return the fields in a displayable list
     */
    getInvalidFieldsAsReadableList: function(invalidFields)
    {
        let message = "<ul>";
        Ext.Array.each(invalidFields, function(invalidField) {
            message += "<li>";
            if (invalidField.includes('>'))
            {
                let invalidFieldSegments = invalidField.split('>');
                let lastSegment = invalidFieldSegments[invalidFieldSegments.length - 1];
                let firstSegments = Ext.Array.removeAt(invalidFieldSegments, invalidFieldSegments.length - 1);
                message += firstSegments.join('>')
                message += "><strong>" + lastSegment + "</strong>";
            }
            else
            {
                message += "<strong>" + invalidField + "</strong>";
            }
            message += "</li>";
        })
        message += "</ul>";
        
        return message;
    },
   
    /**
     * Display a dialog box listing the errors found by the server
     * @param {Ametys.form.ConfigurableFormPanel} form the configurable form panel
     * @param {String} errorTitle the title of the save error dialog
     * @param {Ametys.form.ConfigurableFormPanel} errorMsg the introduction message of the error dialog
     * @param {Object} fieldErrors the mapping of field names with the corresponding error message
     * @param {Array} globalErrors the global error messages
     */
    handleServerErrors: function(form, errorTitle, errorMsg, fieldErrors, globalErrors)
    {
    	var fieldNamePrefix = form.getFieldNamePrefix();
    	var detailedMsg = '';
    	
    	// First treat the global errors
    	if (globalErrors && globalErrors.length > 0)
    	{
    		for (var i=0; i < globalErrors.length; i++)
    		{
    			detailedMsg += '<li>' + globalErrors[i] + '</li>'
    		}
    	}
    	
    	// Then treat the errors on fields
    	if (!Ext.Object.isEmpty(fieldErrors))
    	{
    		for (var name in fieldErrors)
    		{
    			var fieldName = fieldNamePrefix + name;
    			var fd = form.getForm().findField(fieldName);
    			
    			detailedMsg += '<li><b>' + (fd != null ? fd.getFieldLabel() || fd.getInitialConfig("label") || fieldName : fieldName) + '</b>: ' + fieldErrors[name] + '</li>';
    		}
    		
    		form.markFieldsInvalid (fieldErrors);
    	}
    
    	Ametys.form.SaveHelper.SaveErrorDialog.showErrorDialog (errorTitle, errorMsg, '<ul>' + detailedMsg + '</ul>');
    },
    
    /**
     * @private
     * Determine if there are fields with warnings and so display a dialog box listing the fields in warning
     * @param {Ametys.form.ConfigurableFormPanel} form the configurable form panel
     * @param {Function} callback the callback function
     * @param {Function} callback.canSave true if there is no warning or if the user is fine with existing ones.
     */
    _handleWarnedFields: function(form, callback)
    {
    	var warnedFields = form.getWarnedFields();
		if (Ext.Object.getSize(warnedFields) > 0)
		{
            // At least one field has a warning
			var msgBox = Ext.create('Ext.window.MessageBox', {closeAction: 'destroy'});
            msgBox.buttonText.yes = "{{i18n PLUGINS_CORE_UI_SAVE_ACTION_WARNED_FIELDS_YES}}";
            msgBox.buttonText.no = "{{i18n PLUGINS_CORE_UI_SAVE_ACTION_WARNED_FIELDS_NO}}";
			
			msgBox.show({
		        title: "{{i18n PLUGINS_CORE_UI_SAVE_ACTION_WARNED_FIELDS_TITLE}}",
		        msg: "{{i18n PLUGINS_CORE_UI_SAVE_ACTION_WARNED_FIELDS_START}}" + this.getWarnedFieldsAsReadableList(warnedFields) + "{{i18n PLUGINS_CORE_UI_SAVE_ACTION_WARNED_FIELDS_END}}",
		        buttons: Ext.Msg.YESNO,
		        icon: Ext.Msg.WARNING,
		        fn: function(answer) {callback(answer == 'yes');},
		        scope: this
		    });
		}
		else
		{
			// All checks passed successfully
	  		callback(true); 
		}
    },
    
    /**
     * Retrieves the given warned fields in a displayable list to put in warning dialog boxes
     * @param {Array} warnedFields The warned fields
     * @return the fields in a displayable list
     */
    getWarnedFieldsAsReadableList: function(warnedFields)
    {
        let message = "<ul>";
        Ext.Object.each(warnedFields, function(warnedFieldLabel) {
            let warningMessages = warnedFields[warnedFieldLabel];
            
            let warningList = "<ul>";
            Ext.Array.each(warningMessages, function(warningMessage) {
                let text = Ext.isObject(warningMessage) && warningMessage.message
                    ? warningMessage.message
                    : warningMessage
                
                warningList += "<li>";
                warningList += text;
                warningList += "</li>";
            });
            warningList += "</ul>";
            
            message += "<li>";
            message += "<strong>" + warnedFieldLabel + "</strong>: " + warningList
            message += "</li>";
        });
        message += "</ul>";
        
        return message;
    },
    
    /**
     * @private
     * Are all the tests currently successful?
     * @param {Ametys.form.ConfigurableFormPanel.FieldChecker[]} fieldCheckers the field checkers
     * @return true if all the tests are currently successful, false otherwise
     */
    _areTestsOk: function(fieldCheckers)
    {
    	var testsOk = true;
		Ext.Array.each(fieldCheckers, function(fieldChecker) {
	        var status = fieldChecker.getStatus();
	        
	        if (Ext.getCmp(fieldChecker.buttonId).isVisible() 
	            && (status == Ametys.form.ConfigurableFormPanel.FieldChecker.STATUS_FAILURE 
	                || status == Ametys.form.ConfigurableFormPanel.FieldChecker.STATUS_NOT_TESTED
	                || status == Ametys.form.ConfigurableFormPanel.FieldChecker.STATUS_WARNING))
	        {
	        	testsOk = false;
                return false; // stop iteration
	        }
	    });
		
		return testsOk;
    }
});