(function($) {
    $.fn.bootstrapValidator.i18n.remote = $.extend($.fn.bootstrapValidator.i18n.remote || {}, {
        'default': 'Please enter a valid value'
    });

    $.fn.bootstrapValidator.validators.remote = {
        html5Attributes: {
            message: 'message',
            name: 'name',
            type: 'type',
            url: 'url',
            delay: 'delay'
        },

        /**
         * Destroy the timer when destroying the bootstrapValidator (using validator.destroy() method)
         */
        destroy: function(validator, $field, options) {
            if ($field.data('bv.remote.timer')) {
                clearTimeout($field.data('bv.remote.timer'));
                $field.removeData('bv.remote.timer');
            }
        },

        /**
         * Request a remote server to check the input value
         *
         * @param {BootstrapValidator} validator Plugin instance
         * @param {jQuery} $field Field element
         * @param {Object} options Can consist of the following keys:
         * - url {String|Function}
         * - type {String} [optional] Can be GET or POST (default)
         * - data {Object|Function} [optional]: By default, it will take the value
         *  {
         *      <fieldName>: <fieldValue>
         *  }
         * - delay
         * - name {String} [optional]: Override the field name for the request.
         * - message: The invalid message
         * - headers: Additional headers
         * @returns {Deferred}
         */
        validate: function(validator, $field, options) {
            var value = $field.val(),
                dfd   = new $.Deferred();
            if (value === '') {
                dfd.resolve($field, 'remote', { valid: true });
                return dfd;
            }

            var name    = $field.attr('data-bv-field'),
                data    = options.data || {},
                url     = options.url,
                type    = options.type || 'GET',
                headers = options.headers || {};

            // Support dynamic data
            if ('function' === typeof data) {
                data = data.call(this, validator);
            }

            // Support dynamic url
            if ('function' === typeof url) {
                url = url.call(this, validator);
            }

            data[options.name || name] = value;
            function runCallback() {
                var xhr = $.ajax({
                    type: type,
                    headers: headers,
                    url: url,
                    dataType: 'json',
                    data: data
                });
                xhr.then(function(response) {
                    response.valid = response.valid === true || response.valid === 'true';
                    dfd.resolve($field, 'remote', response);
                });

                dfd.fail(function() {
                    xhr.abort();
                });

                return dfd;
            }
            
            if (options.delay) {
                // Since the form might have multiple fields with the same name
                // I have to attach the timer to the field element
                if ($field.data('bv.remote.timer')) {
                    clearTimeout($field.data('bv.remote.timer'));
                }

                $field.data('bv.remote.timer', setTimeout(runCallback, options.delay));
                return dfd;
            } else {
                return runCallback();
            }
        }
    };
}(window.jQuery));
