// (C) Copyright 2020 Hewlett-Packard Enterprise Company, L.P.
/**
 * @type {Validator}
 */
define(['hp/core/Localizer', 'jquery', 'lib/jquery.validate'], function (localizer) {
"use strict";

    var Validator = (function () {

        /** delay after typing characters before validating a field (to avoid bouncing between valid and invalid as they type.) */
        var VALIDATION_DELAY = 500; // ms

        /**
         * @constructor
         * @type {Validator}
         */
        function Validator() {

            function defaultOnkeyup(element) {
                if ( element.name in this.submitted || element == this.lastElement ) {
                    this.element(element);
                }
            }

            function delayedOnkeyup (element) {
                var validator = this;
                if (element.validTimer) {
                    clearTimeout(element.validTimer);
                }
                if (validator.invalid[element.name]) {
                    defaultOnkeyup.call(validator, element);
                }
                else {
                    element.validTimer = setTimeout(function () {
                        element.validTimer = null;
                        defaultOnkeyup.call(validator, element);
                    }, VALIDATION_DELAY);
                }
            }
            
            // http://stackoverflow.com/a/12254194/1365886
            
            function preTrim(handler) {
                return function (element, event) {
                    if (element.tagName === "TEXTAREA" ||
                        (element.tagName === "INPUT" &&
                        element.type !== "password" &&
                        //exclude "file" type element to avoid browser security exception
                        element.type !== "file" )) {
                    	//exclude textarea and input with "hp-no-trim" class
                        if (!$(element).hasClass("hp-no-trim")) {
                            element.value = $.trim(element.value);
                        }
                    }
                    return handler.call(this, element, event);
                };
            }


            /**
             * Validate a host address.
             * 
             * @param {string} value The value to validate.
             * @param {Element} element The field element that the value came from.
             * @param {Array<string>} param The types of addresses that are allowed:
             *     hostname, fqdn, ipv4, ipv6, maskv4, and/or
             *     maskv6.  (The last two allow CIDR bit counts or subnet masks.)
             * @returns {Boolean} Whether the address is valid.
             */
            function validateHostAddress(value, element, param) {
                // If we don't have a value at all, it's considered valid.
                // They can specify 'required' separately if needed.
                if (!value) {
                    return true;
                }
                
                // If they didn't specify what types of host addresses are valid,
                // accept these.
                if (typeof param == 'string') {
                    param = [param];
                } else if (!(param instanceof Array)) {
                    param = ['hostname', 'fqdn', 'ipv4', 'ipv6'];
                }
                
                // Try each requested type to see whether there's a match.
                var valid = false;
                $.each(param, function (unused, type) {
                    switch (type) {
                    case 'hostname':
                        valid = /^[a-zA-Z0-9][a-zA-Z0-9_\-]*$/.test(value);
                        break;
                    case 'fqdn':
                        valid = /^[a-zA-Z0-9][a-zA-Z0-9_\-]*(?:\.[a-zA-Z0-9_\-][a-zA-Z0-9_\-]*)+$/.test(value);
                        break;
                    case 'maskv4':
                        if (/^\d+$/.test(value) && value >= 0 && value <= 32) {
                            valid = true;
                            break;
                        }
                        // FALL-THROUGH
                    case 'ipv4':
                        valid = /^(([01]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])\.){3}([01]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])$/.test(value);
                        break;
                    case 'maskv6':
                        if (/^\d+$/.test(value) && value >= 0 && value <= 128) {
                            valid = true;
                            break;
                        }
                        // FALL-THROUGH
                    case 'ipv6':
                        valid = /^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?$/.test(value);
                        break;
                    default:
                        throw new Error("unknown address type " + type);
                    }
                    return !valid;
                });
                return valid;
            }
            
            this.init = function () {
                var validatorMessages;

                validatorMessages = localizer.getString('core.validator.messages');
                if (validatorMessages) {
                    $.each(validatorMessages, function (key, msg) {
                        $.validator.messages[key] =
                            /\{[0-9+]\}/.test(msg) ? $.validator.format(msg) : msg;
                    });
                }
                $.validator.setDefaults({
                    focusInvalid: false,
                    invalidHandler: function(form, validator) {
                        validator.errorList[0].element.focus();
                    },
                    onkeyup: delayedOnkeyup,
                    errorClass: 'hp-error',
                    validClass: 'hp-valid',
                    errorPlacement: function (error, element) {
                        if ($(element).hasClass('hp-search-combo-select') ||
                            $(element).hasClass('hp-search-combo-input') ||
                            $(element).hasClass('hp-select-select')) {
                            element = $(element).parent();
                        }
                        if ($(element).siblings('.hp-message-container').length > 0) {
                            $(element).siblings('.hp-message-container').
                              prepend(error);
                        } else {
                            element.after(error);
                        }
                    },
                    ignore : '',
                    highlight: function (element, errorClass, validClass) {
                        $(element).addClass(errorClass);
                        if ($(element).hasClass('hp-search-combo-select') ||
                            $(element).hasClass('hp-search-combo-input')) {
                            $(element).parent().addClass(errorClass); 
                        }
                        if ($(element).hasClass('hp-select-select')) {
                            $(element).siblings('.hp-select').
                                addClass(errorClass);
                        }
                        $(element).parents('.hp-form-content').
                            addClass('hp-has-error');
                        //to overwrite jquery 1.8 display behavior for show/hide methods.
                        $(element).parents('.hp-form-content:not(.hp-wide)').
                            children("label.hp-error[for='" +
                                $(element).attr("id") + "']").
                            css('display', 'inline-block');
                    },
                    unhighlight: function (element, errorClass, validClass) {
                        $(element).removeClass(errorClass);
                        if ($(element).hasClass('hp-search-combo-select') ||
                            $(element).hasClass('hp-search-combo-input') ) {
                            $(element).parent().removeClass(errorClass);
                        }
                        if ($(element).hasClass('hp-select-select')) {
                            $(element).siblings('.hp-select').
                                removeClass(errorClass);
                        }
                        $(element).parents('.hp-form-content').
                            removeClass('hp-has-error');
                    },
                    onfocusout: preTrim($.validator.defaults.onfocusout)
                });
                $.validator.addMethod('hostAddress', validateHostAddress,
                    "Please specify a valid hostname or IP address.");
                $.validator.addClassRules('hostAddress', {hostAddress: true});
                $.fn.resetForm = function () {
                    $('label', this).remove('.hp-error');
                    $('.hp-form-content', this).removeClass('hp-has-error');
                };
            };
        }

        return new Validator();
    }());

    return Validator;
});
