$.fn.extend({
    disableForm: function(){
        return this.each(function(){
            $(this).addClass('disabled').find(':input, button').attr('disabled','disabled');
        });
    },
    
    enableForm: function(){
        return this.each(function(){
            $(this).removeClass('disabled').find(':input, button').removeAttr('disabled');
        });
    },

    showFormSuccess: function(message) {
        return this.each(function(){
            var $form = $(this);
            var $content = $('.async-success', $form);

            $('.message', $content).text(message);
            $content.show();
        });
    },

    hideFormSuccess: function() {
        return this.each(function(){
            var $form = $(this);
            var $content = $('.async-success', $form);

            $('.message', $content).text('');
            $content.hide();
        });
    },

    showFormError: function(xhr) {
        return this.each(function(){
            var $form = $(this);
            var $content = $('.async-error', $form);

            var $message = errorMessage(xhr);
            $('.message', $content).text($message != undefined ? $message : '');

            var $reason = errorReason(xhr);
            $('.reason', $content).text($reason != undefined ? $reason : '');

            var $recovery = errorRecovery(xhr);
            $('.recovery', $content).text($recovery != undefined ? $recovery : '');

            $('.more', $content).text('');

            var domain = errorDomain(xhr);
            var code = errorCode(xhr);
            if (domain == 'terms' && code == 1) {
                var url = $('meta[name=application-config]').attr('data-terms');
                var anchor = $('meta[name=application-config]').attr('data-terms-anchor');
                if (!_.isUndefined(url) && !_.isUndefined(anchor)) {
                    $('.more', $content).html('<a href="' + url +'" class="btn btn-secondary">' + anchor +'</a>');
                }
            }

            $content.show();
        });
    },

    hideFormError: function() {
        return this.each(function(){
            var $form = $(this);
            var $alert = $('.async-error', $form);

            $('.message', $alert).text('');
            $('.reason', $alert).text('');
            $('.recovery', $alert).text('');
            $('.more', $alert).text('');
            $alert.hide();
        });
    },

    showFormSubmitLoading: function() {
        return this.each(function(){
            $('button[type=submit]', $(this)).prepend('<span class="spinner-border spinner-border-sm loading" role="status" aria-hidden="true"></span>');
        });
    },

    hideFormSubmitLoading: function() {
        return this.each(function(){
            $('button[type=submit] .loading', $(this)).remove();
        });
    },

    showFormLoading: function() {
        return this.each(function(){
            $('.async-loading', $(this)).show();
        });
    },

    hideFormLoading: function() {
        return this.each(function(){
            $('.async-loading', $(this)).hide();
        });
    },

    showFormContent: function() {
        return this.each(function(){
            var $form = $(this);
            $('.async-content', $form).show();
        });
    },

    hideFormContent: function() {
        return this.each(function(){
            var $form = $(this);
            $('.async-content', $form).hide();
        });
    },

    showFormItemsOnComplete: function() {
        return this.each(function(){
            var $form = $(this);
            $('*[data-async-oncomplete=false]', $form).hide();
            $('*[data-async-oncomplete=true]', $form).show();
        });
    },

    hideFormItemsOnComplete: function() {
        return this.each(function(){
            var $form = $(this);
            $('*[data-async-oncomplete=false]', $form).show();
            $('*[data-async-oncomplete=true]', $form).hide();
        });
    },

    initalizeValidationMaxChars: function() {
        return this.each(function(){
            var $input = $(this);
            var $maxChars = parseInt($input.attr('data-validation-max-chars'), 10);

            if (_.isNaN($maxChars) || _.isUndefined($maxChars)) {
                return;
            }

            var $feedback = $('<small class="feedback"></small>').insertAfter($input);
            var $form = $input.parents('form');
            var $submit = $('*[type=submit]', $form);

            $input.on('change paste keyup', function() {
                var count = $input.val().length;
                $feedback.text(count+' / '+$maxChars);
                if (count <= $maxChars) {
                    $feedback.addClass('text-success').removeClass('text-danger').attr('data-validation-ok', true);
                } else {
                    $feedback.addClass('text-danger').removeClass('text-success').attr('data-validation-ok', false);
                }

                if ($('*[data-validation-ok=false]', $form).length > 0) {
                    $submit.attr('disabled','disabled');
                } else {
                    $submit.removeAttr('disabled');
                }
            });
        }).trigger('change');
    },

    initializeInputFile: function(callback) {
        return this.each(function(){
            var $input = $(this);
            var $parent = $($input.attr('data-file-parent-loading'));

            $input.on('change', function(e) {
                var file = $input[0].files[0]
                if (_.isUndefined(file)) {
                    $input.val('');
                    return;
                }
        
                var filetype = $input.attr('data-file-type');
                if (!file.type.startsWith(filetype)) {
                    $input.val('');
                    notifyError($input.attr('data-file-type-error'));
                    return;
                }
        
                var filesize = $input.attr('data-file-size');
                if (file.size > filesize) {
                    $input.val('');
                    notifyError($input.attr('data-file-size-error'));
                    return;
                }
        
                $parent.startLoading();
        
                var formData = new FormData();
                formData.append('file', file);

                var method = $input.attr('data-file-method');
                if (_.isUndefined(method)) {
                    method = 'put'
                }
        
                $.ajax({
                    url: $input.attr('data-file-save-url'),
                    type: method,
                    data: formData,
                    cache: false,
                    contentType: false,
                    processData: false,
                    success: function(json){
                        notifyOk(json.status, '');
                        if (!_.isUndefined(callback)) {
                            callback(json);
                        }
                    },
                    complete: function() {
                        $input.val('');
                        $parent.stopLoading();
                    }
                });
            });
        });
    },

    initializeFormModal: function(){
        return this.each(function(){
            var $form = $(this);
            var $modal = $form;
            if (!$form.is('.modal')) {
                $modal = $form.closest('.modal');
            }

            var $errorContent = $('.async-error', $form);
            if ($errorContent.length == 0) {
                $errorContent = $(
                    '<div class="async-error alert alert-danger" role="alert">'+
                    '<h5 class="message alert-heading"></h5>'+
                    '<p class="reason"></p>'+
                    '<p class="recovery"></p>'+
                    '<div class="more"></div>'+
                    '</div>'
                );

                if ($form.is('.modal')) {
                    $('.modal-body', $form).append($errorContent);
                } else {
                    $form.append($errorContent);
                }
            }
            $errorContent.hide();

            $modal.on('show.bs.modal', function(e) {
                $form.enableForm();
                $form.hideFormError();
                $form.hideFormSuccess();
                $form.hideFormLoading();
                $form.showFormContent();
                $form.hideFormItemsOnComplete();
                $('input[type=text]:not([data-keep]), input[type=hidden]:not([data-keep]), textarea:not([data-keep])', $form).val('').trigger('change');
            });
        });
    },

    initializeFormAsync: function() {
        return this.each(function(){
            var $form = $(this);

            var $loadingContent = $('.async-loading', $form);
            var $successContent = $('.async-success', $form);
            var $errorContent = $('.async-error', $form);

            $form.hideFormSuccess();
            $form.hideFormError();

            $form.on('submit', function(e) {
                e.preventDefault();

                var data = {};
                $form.serializeArray().forEach(function(v) {
                    var name = v.name;
                    var value = v.value;

                    var $item = $(':input[name=' + name + ']', $form);
                    var path = $item.attr('data-async-path');

                    var dataType = $item.attr('data-type');
                    if (dataType == 'int') {
                        value = parseInt(value, 10);
                    } else if (dataType == 'float') {
                        value = parseFloat(value, 10);
                    }

                    if ($item.is(':input[type=checkbox]') && value.length == 0) {
                        value = $item.is(':checked') ? true : false;
                    }

                    if (_.isUndefined(path) || path.length == 0) {
                        if (_.has(data, name)) {
                            var currentValue = data[name];
                            if (_.isArray(currentValue)) {
                                data[name].push(value);
                            } else {
                                data[name] = [currentValue, value];
                            }
                        } else {
                            data[name] = value;
                        }
                    } else {
                        var p = data;
                        var parts = path.split('.');
                        _.each(parts, function(element, index, list) {
                            if (!_.has(p, element)) {
                                p[element] = {};
                            }
                            p = p[element];

                            if (index == list.length-1) {
                                if (_.has(p, name)) {
                                    var currentValue = p[name];
                                    if (_.isArray(currentValue)) {
                                        p[name].push(value);
                                    } else {
                                        p[name] = [currentValue, value];
                                    }
                                } else {
                                    p[name] = value;
                                }
                            }
                        });
                    }
                });

                var mustBeDismissed = $form.attr('data-async-dismiss') == 'true';

                if ($loadingContent.length > 0) {
                    $form.showFormLoading();
                    $form.hideFormContent();
                }

                $form.showFormSubmitLoading();
                $form.disableForm();
                $(':input', $form).removeClass('is-valid is-invalid');

                $.ajax({
                    url: $form.attr('action'),
                    type: $form.attr('method'),
                    data: JSON.stringify(data),
                    global: false,
                    success: function(data) {
                        if ($successContent.length > 0) {
                            $form.showFormSuccess(data.status);
                            $form.hideFormContent();
                        } else {
                            notifyOk(data.status, '');
                        }

                        $form.showFormItemsOnComplete();

                        var callback = $form.attr('data-callback');
                        if (!_.isUndefined(callback)) {
                            $form.attr('data-callback-response', JSON.stringify(data));
                            eval(callback);
                        }

                        var reloadTarget = $form.attr('data-async-reload');
                        if (!_.isUndefined(reloadTarget)) {
                            var reloadArgs = {};
                            var page = $form.attr('data-async-reload-page');
                            if (!_.isUndefined(page)) {
                                reloadArgs.page = parseInt(page, 10);
                            }
                            $(reloadTarget).reloadData(reloadArgs);
                        }
                    },
                    error: function(xhr) {
                        if (handleJSONError(xhr)) {
                            return;
                        }

                        var fieldName = errorField(xhr);
                        var $invalidField = $(':input[name=' + fieldName + ']', $form);
                        if (_.isUndefined(fieldName) || $invalidField.length == 0) {
                            if ($errorContent.length > 0) {
                                $form.showFormItemsOnComplete();
                                $form.showFormError(xhr);
                            } else {
                                $form.showFormContent();
                                notifyError(xhr);
                            }
                            return;
                        }

                        $form.showFormContent();

                        $invalidField.addClass('is-invalid');
                        $feedback = $invalidField.next('.invalid-feedback');
                        if ($feedback.length == 0) {
                            $feedback = $('<div></div>').addClass('invalid-feedback').insertAfter($invalidField);
                        }
                        $feedback.text(errorReason(xhr)+' '+errorRecovery(xhr));
                    },
                    complete: function() {
                        $form.enableForm();
                        $form.hideFormSubmitLoading();
                        if (mustBeDismissed) {
                            $form.modal('hide');
                        } else {
                            $form.hideFormLoading();
                        }
                    }
                });
            });
        });
    }
});

$(function(){
    $('form.modal, .modal form').initializeFormModal();
    $('form[data-async=true]').initializeFormAsync();
    $(':input[data-validation-max-chars]').initalizeValidationMaxChars();
});