/*
 *  Copyright 2013 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 helper provides a dialog box to choose a resource from local hard drive and upload it
 * See #open method.
 */
Ext.define('Ametys.explorer.resources.helper.ResourceUpload', {
	singleton: true,
	
	/**
	 * @property {Boolean} _initialized True if the dialog box has been initialized.
	 * @private
	 */
	/**
	 * @property {Ext.form.Basic} _form The inner basic form.
	 * @private
	 */
	/**
	 * @property {Ametys.window.DialogBox} _box The dialog box
	 * @private
	 */
	
	/**
	 * @property {Object} The error messages
	 * @private
	 */
	_errorMsg: {
			'locked': "{{i18n PLUGINS_EXPLORER_FILE_HANDLE_ADD_LOCKED}}",
            'infected': "{{i18n plugin.core-ui:PLUGINS_CORE_UI_FILE_HANDLE_INFECTED}}",
			'rejected': "{{i18n plugin.core-ui:PLUGINS_CORE_UI_FILE_HANDLE_REJECTED}}",
			'already-exist': "{{i18n plugin.core-ui:PLUGINS_CORE_UI_FILE_HANDLE_ADD_ALREADY_EXISTS}}",
			'unknown-resource': "{{i18n PLUGINS_EXPLORER_FILE_HANDLE_UPDATE_NO_EXIST}}",
			'unknown-collection': "{{i18n PLUGINS_EXPLORER_FILE_HANDLE_ADD_NO_EXIST}}",
			'unzip-error': "{{i18n PLUGINS_EXPLORER_FILE_HANDLE_ADD_UNZIP_ERROR}}" 
	},
	
	
	
	/**
	 * Allow the user to upload a resource file from local hard drive
	 * @param {String} parentId The id of parent folder
	 * @param {String} id The id of the resource to replace. Can be null.
	 * @param {Function} callback The callback function called when the resource was uploaded. Has the following parameters
	 * @param {String} callback.id The id of uploaded resource
	 * @param {String} callback.parentId The id of parent folder
	 * @param {Boolean} callback.reload True if a reload is needed
	 */
	open: function (parentId, id, callback)
	{
		this._cbFn = callback;
		
		this._delayedInitialize ();
		this._box.show();
		
		this._initForm (parentId, id);
	},
	
	/**
	 * @private
	 * Initialize the dialog box
	 * @return {Boolean} true if the box has been initialized (or if it was already initialized).
	 */
	_delayedInitialize: function()
	{
		if (this._initialized)
		{
			return true;
		}
		
		var formPanel = Ext.create('Ext.form.Panel', {
			border: false, 
			scrollable: true,
			defaults: {
				cls: 'ametys',
                anchor: '100%',
                msgTarget: 'title'
			},
			fieldDefaults : {
				labelAlign: 'top',
				labelSeparator: '',
				labelWidth: 50
			},
			
			items : [{
				xtype: 'fileuploadfield',
				itemId: 'form-file',
				name: 'filenames',
                multipleFile: true,
				emptyText: "{{i18n plugin.explorer:PLUGINS_EXPLORER_FILE_HANDLE_ADD_EMPTY_TEXT}}",
				fieldLabel: "{{i18n plugin.explorer:PLUGINS_EXPLORER_FILE_HANDLE_ADD_FILE}}",
				buttonText: "{{i18n plugin.core-ui:PLUGINS_CORE_UI_FILE_HANDLE_ADD_BROWSE}}",
				allowBlank: false,
				listeners: {
					change: {fn: this._onFileChange, scope: this}
				}
			}, {
				xtype: 'checkboxfield',
				itemId: 'form-unzip-display',
				name: 'unzip-display',
				boxLabel: "{{i18n PLUGINS_EXPLORER_FILE_HANDLE_ADD_UNZIP}}",
				hideLabel: true,
				checked: false,
				hidden: true,
				listeners: {
					change: {fn: this._onCheck, scope: this}
				}
			}, {
				xtype: 'component',
				itemId: 'update-file-hint',
				html: "{{i18n plugin.explorer:PLUGINS_EXPLORER_FILE_HANDLE_UPDATE_HINT}}",
				hidden: true
			}, {
				xtype: 'hiddenfield',
				name: 'collection'
			}, {
				xtype: 'hiddenfield',
				name: 'id'
			}, {
				xtype: 'hiddenfield',
				name: 'unzip'
			}, {
				// TODO (08/Jul/13) is this useful / smth planned? There is only a 'to do' comment.
				xtype: 'hiddenfield',
				name: 'keywords'
			}]
		});
		
		this._form = formPanel.getForm();
		
		this._box = Ext.create('Ametys.window.DialogBox', {
			title :"{{i18n plugin.explorer:PLUGINS_EXPLORER_FILE_HANDLE_ADD}}",
			iconCls : 'ametysicon-upload119',
			
			layout :'fit',
			width : 430,
			
			items: formPanel,
			
			defaultFocus: 'form-file',
			closeAction: 'hide',
			
			buttons: [{
				id: 'upload-file-btn-ok',
				text :"{{i18n plugin.core-ui:PLUGINS_CORE_UI_DIALOG_OK}}",
				handler : Ext.bind(this._add, this)
			},{
				id: 'upload-file-btn-update',
				text :"{{i18n plugin.core-ui:PLUGINS_CORE_UI_DIALOG_REPLACE}}",
				hidden: true,
				handler : Ext.bind(this._update, this)
			},{
				id: 'upload-file-btn-add-rename',
				text :"{{i18n plugin.core-ui:PLUGINS_CORE_UI_DIALOG_ADDANDRENAME}}",
				hidden: true,
				handler : Ext.bind(this._addAndRename, this)
			},{
				text :"{{i18n plugin.core-ui:PLUGINS_CORE_UI_DIALOG_CANCEL}}",
				handler : Ext.bind(function() {this._box.hide();}, this)
			}],

            listeners: {
                'show': function() {
                    var me = this;
                    window.setTimeout(function() {
                        me._form.findField('filenames').fileInputEl.dom.click();
                    },1); // Timeout so listeners are correctly attached
                },
                scope: this
            }			
		});
		
		this._initialized = true;
		return true;
	},
	
	/**
	 * @private
	 * Initialize or reintialize the form with the appropriate values.
	 * @param {String} parentID The folder parent id
	 * @param {String} id The id of the new resource.
	 */
	_initForm: function(parentID, id)
	{
		this._form.findField('collection').setValue(parentID);
		this._form.findField('id').setValue(id);
		this._form.findField('filenames').reset();
		this._form.findField('unzip-display').hide();
		this._form.findField('unzip-display').reset();
		this._form.findField('unzip').setValue('false');
		this._form.owner.queryById('update-file-hint').hide();
		
		// Show/Hide btns
		this._box.queryById('upload-file-btn-ok').show();
		this._box.queryById('upload-file-btn-update').hide();
		this._box.queryById('upload-file-btn-add-rename').hide();
	},
	

	/**
	 * @private
	 * Listener when file upload field changed.
	 * Checks if a resource with this name already exists in the given folder, and then, appropriately update the form and buttons.
	 * @param {Ext.ux.form.FileUploadField} field the file field
	 * @param {Object} value The file value returned by the underlying file input field
	 * @param {Object} oldValue the previous value of the field
	 * @param {Object} eOpts The event option object
	 */
	_onFileChange: function(field, value, oldValue, eOpts)
	{
		if (value)
		{
            var filenames = value;
        
            if (!Array.isArray(value))
            {
                filenames = [value];
            }
        
			Ametys.data.ServerComm.callMethod({
				role: 'org.ametys.plugins.explorer.resources.actions.ExplorerResourcesDAO', 
				methodName: 'resourcesExists',
				parameters: [this._form.findField('collection').getValue(), filenames],
				callback: {
					handler: this._checkNameCb,
					scope: this,
					arguments: {
						filenames: value
					}
				},
				waitMessage: true,
				errorMessage: "{{i18n PLUGINS_EXPLORER_FILE_HANDLE_CHECK_NAME_ERROR}}"
			});
		}
		else
		{
			this._refreshDialog(value);
		}
	},
	
	/**
	 * @private
	 * Callback function invoked after checking file name
	 * @param {Boolean} exists true if one of the resource with same name already exists
	 * @param {Object} args the callback arguments
	 * @param {String[]} args.filenames the name of the files
	 */
	_checkNameCb: function (exists, args)
	{
		if (exists)
		{
			this._form.findField('unzip-display').hide();
			this._form.owner.queryById('update-file-hint').show();
			
			this._box.queryById('upload-file-btn-ok').hide();
			this._box.queryById('upload-file-btn-update').show();
			this._box.queryById('upload-file-btn-add-rename').show();
		}
		else
		{
			this._refreshDialog(args.filenames);
		}
	},
	
	/**
	 * @private
	 * Refreshes the dialog's buttons depending on the upload candidate
	 * @param {String[]} filenames the name of the files
	 */
	_refreshDialog: function(filenames)
	{
		this._form.owner.queryById('update-file-hint').hide();
		
		var unzip = this._form.findField('unzip-display');
        if (filenames.length == 1 && Ext.String.endsWith(filenames[0], '.zip'))
        {
            unzip.show();
        }
        else
        {
            unzip.hide();
        }
		
		this._box.queryById('upload-file-btn-ok').show();
		this._box.queryById('upload-file-btn-update').hide();
		this._box.queryById('upload-file-btn-add-rename').hide();
	},
	
	/**
	 * @private
	 * Listener when the 'unzip' checkbox is check or uncheck
	 * Updates the hidden field 'unzip' accordingly.
	 * @param {Ext.form.field.Field} field The field changed
	 * @param {Object} newValue The new value
	 * @param {Object} oldValue The original value
	 * @param {Object} eOpts The event option object
	 */
	_onCheck: function(field, newValue, oldValue, eOpts)
	{
		this._form.findField('unzip').setValue(newValue.toString());
	},
	
	/**
	 * @private
	 * This function is called when the 'Ok' button of the dialog box #_box is pressed
	 * Submits the form to the server in order to add the file.
	 */
	_add: function()
	{
        this._addUpateOrRename("add");
    },
    
	/**
	 * @private
	 * This function is called when the 'Replace' button of the dialog box #_box is pressed
	 * Submits the form to the server in order to update the file.
	 */
	_update: function()
	{
        this._addUpateOrRename("update");
	},
	
	/**
	 * @private
	 * This function is called when the 'Rename' button of the dialog box #_box is pressed
	 * Submits the form to the server in order to add then rename the file.
	 */
	_addAndRename: function()
	{
        this._addUpateOrRename("add-rename");
	},
	
	/**
     * @private
     * This function is called when the one of the import button of the dialog box #_box is pressed
     * Submits the form to the server in order to import the file.
     */
	_addUpateOrRename: function(mode)
    {
        if (this._form.isValid())
        {
            var parameters = [];
            
            var formFieldValues = this._form.getFieldValues();
            parameters.push(formFieldValues["collection"]);
            parameters.push(mode || "add");
            parameters.push(formFieldValues["unzip"] == "true");
            
            var files = this._form.findField('filenames').getFiles();
            parameters.push(files);
            
            try 
            {
                Ametys.data.ServerComm.callMethod({
                    role: 'org.ametys.plugins.explorer.resources.AddOrUpdateResource', 
                    methodName: 'addOrUpdateResources',
                    parameters: parameters,
                    priority: Ametys.data.ServerComm.PRIORITY_LONG_REQUEST,
                    callback: {
                        handler: this._addUpateOrRenameCb,
                        scope: this,
                        arguments: {
                            numberOfFiles: files.length
                        }
                    },
                    errorMessage: "{{i18n plugin.core-ui:PLUGINS_CORE_UI_FILE_HANDLE_ADD_RENAME_UPDATE_ERROR}}",
                    progressMessage: {
                        title: "{{i18n plugin.explorer:PLUGINS_EXPLORER_FILE_HANDLE_ADD_WAIT_TITLE}}",
                        msg: "{{i18n plugin.explorer:PLUGINS_EXPLORER_FILE_HANDLE_ADD_WAIT_MSG}}"
                    }
                });
            }
            catch (e)
            {
                let message;
                
                if (files.length > 1)
                {
                    message = Ext.String.format("{{i18n plugin.core-ui:PLUGINS_CORE_UI_FILE_HANDLE_ADD_MULTIPLE_ERROR}}", files.length) + "<br/>{{i18n plugin.explorer:PLUGINS_EXPLORER_FILE_HANDLE_REJECTED}}";
                }
                else
                {
                    message = "{{i18n plugin.core-ui:PLUGINS_CORE_UI_FILE_HANDLE_ADD_ERROR}}<br/>" + "{{i18n plugin.core-ui:PLUGINS_CORE_UI_FILE_HANDLE_REJECTED}}";
                }
                
                Ametys.log.ErrorDialog.display({
                    title: "{{i18n plugin.explorer:PLUGINS_EXPLORER_FILE_HANDLE_ADD}}",
                    text: message
                });
                console.error(e);
            }
        }
    },
    
    /**
     * @private
     * This function is called after the resources have tried to be imported
     * @param {Object} result The results
     * @param {Object} arguments The arguments. 
     * @param {int} arguments.numberOfFiles The number of files. 
     */
    _addUpateOrRenameCb: function(result, arguments)
    {
        if (result.success)
        {
            if (this._cbFn)
            {
                var reload = arguments.numberOfFiles > 1 ? false : result.resultsForFileSuccess[0].reload;
                var unzip = arguments.numberOfFiles > 1 ? false : result.resultsForFileSuccess[0].unzip;
                Ext.Function.defer (this._cbFn, 0, null, [result.resultsForFileSuccess, result.parentID, reload, unzip]);
            }
            
            this._box.hide();
        }
        
        if (result.message)
        {
            // If we tried to import multiple files
            if (arguments.numberOfFiles > 1)
            {
                this._createDialogForFailureWithMultipleFiles(result, arguments.numberOfFiles, result.resultsForFileSuccess.length);
            }
            // If we tried to import only one file
            else
            {
                var errorMsg = '';
                var error = Object.values(result.message)[0];
                if (error in this._errorMsg)
                {
                    errorMsg += this._errorMsg[error];
                }
                
                Ametys.log.ErrorDialog.display({
                    title: "{{i18n plugin.explorer:PLUGINS_EXPLORER_FILE_HANDLE_ADD}}",
                    text: "{{i18n plugin.core-ui:PLUGINS_CORE_UI_FILE_HANDLE_ADD_ERROR}}<br/>" + errorMsg
                });
            }
        }
    },
    
	/**
	 * @private
	 * This function is called when the submit of the form failed
     * @param {Object} result The results
     * @param {int} numberOfFiles The number of files. 
     * @param {int} nbOfSuccess The number of files that were successefully imported. 
	 */
	_createDialogForFailureWithMultipleFiles: function(result, numberOfFiles, nbOfSuccess)
	{
        var nbOfFilesError = numberOfFiles - nbOfSuccess;
        var multipleError = nbOfFilesError > 1;

        var notInsertedText; 
        if (multipleError)
        {
            // If there are multiple errors, show the number of error
            notInsertedText = Ext.String.format("{{i18n plugin.core-ui:PLUGINS_CORE_UI_FILE_HANDLE_ADD_MULTIPLE_ERROR}}", nbOfFilesError);
        }
        else
        {
            // If there is only one errors, show the error message for one error
            notInsertedText = "{{i18n plugin.core-ui:PLUGINS_CORE_UI_FILE_HANDLE_ADD_MULTIPLE_ONE_ERROR}}";
        }
        
        // Get the error messages for the details
        var errorMsg = this._getErrorsMsg(result, multipleError);
        
        Ametys.log.ErrorDialog.display({
            title: "{{i18n plugin.core-ui:PLUGINS_CORE_UI_FILE_HANDLE_ADD}}",
            text: notInsertedText + (errorMsg ? "<br/>{{i18n plugin.core-ui:PLUGINS_CORE_UI_FILE_HANDLE_ADD_ERROR_SEE_DETAILS}}" : ""),
            details: errorMsg
        });
	},
	
	/**
     * @private
     * Get the error message
     * @param {Object} result The results
     * @param {Boolean} multipleError true if there is multiple files, false otherwise. 
     */
    _getErrorsMsg: function(result, multipleError)
    {
        var errors = result.message || [];
        var unknownErrorMsg = '';
        var msg = '';
        
        for (let [fileName, error] of Object.entries(errors))
        {
            // If the error is known, add it to the messages
            if (error in this._errorMsg)
            {
                msg += "- " + fileName + ": " + this._errorMsg[error] + "\n";
            }
            // If the error is unknwon, add it to the unknown messages, it will be added to the messages if there is not only unknown errors
            else if (multipleError)
            {
                unknownErrorMsg += "- " + fileName + ": {{i18n plugin.core-ui:PLUGINS_CORE_UI_FILE_HANDLE_UNKNOWN_ERROR}}" + "\n";
            }
        }
        
        // If there is at least one other error, show the files with unknown error, else don't show the detail error message
        if (msg != "")
        {
            msg += unknownErrorMsg;
        }
        
        return msg;
    }
});