package validator

import (
	"fmt"
	"net"
	"net/url"
	"reflect"
	"strings"
	"time"
	"unicode/utf8"
)

// BakedInAliasValidators is a default mapping of a single validationstag that
// defines a common or complex set of validation(s) to simplify
// adding validation to structs. i.e. set key "_ageok" and the tags
// are "gt=0,lte=130" or key "_preferredname" and tags "omitempty,gt=0,lte=60"
//read note 别名标签，通过别名定义规则，默认是iscolor
// 它的作用比较神奇，就是说通过iscolor这个标签就可以判断后面一组的规则
// 比如说这边 iscolor对应的是 hexcolor|rgb|rgba|hsl|hsla
// 如果我在tag中使用iscolor标签，则validator会去校验 是否满足 hexcolor | 是否满足 rgb | 是否满足 rgba | 是否满足 hsl | 是否满足 hsla （|：或者）
var bakedInAliasValidators = map[string]string{
	"iscolor": "hexcolor|rgb|rgba|hsl|hsla",
}

// BakedInValidators is the default map of ValidationFunc
// you can add, remove or even replace items to suite your needs,
// or even disregard and use your own map if so desired.
//read note 默认的校验映射map. key是tag标签，value是对应tag标签的处理类
// 具体校验实现可参考：https://blog.csdn.net/weixin_30896511/article/details/98428064
var bakedInValidators = map[string]Func{
	"required": HasValue,    //read note 参数需要有值
	"len":      HasLengthOf, //read note 数值类型判断的是值相等， Map Slice Array判断的是长度相等，string判断的是Rune数组长度相等
	"min":      HasMinOf,    //read note 数值类型判断的是值【大于等于】min， Map Slice Array判断的是长度【大于等于】min，string判断的是Rune数组长度【大于等于】min
	"max":      HasMaxOf,    //read note 数值类型判断的是值【小于等于】max， Map Slice Array判断的是长度【小于等于】max，string判断的是Rune数组长度【小于等于】max
	"eq":       IsEq,        //read note 字符串或者数值类型，判断的是值【相等】,Map Slice Array判断长度是否【相等】
	"ne":       IsNe,        //read note 字符串或者数值类型，判断的是值【不相等】,Map Slice Array判断长度是否【不相等】
	"lt":       IsLt,        //read note 数值类型判断的是值【小于】lt， Map Slice Array判断的是长度【小于】lt，string判断的是Rune数组长度【小于】lt
	"lte":      IsLte,       //类似max的操作，但是这边多了一步处理就是判断如果是【time.Time】，比较时间在当前时间之前或者等于当前时间
	"gt":       IsGt,        //read note 数值类型判断的是值【大于】gt， Map Slice Array判断的是长度【大于】gt，string判断的是Rune数组长度【大于】gt
	"gte":      IsGte,       //类似min的操作，但是这边多了一步处理就是判断如果是【time.Time】，比较时间在当前时间之后或者等于当前时间

	//read note 判断关联字段(标签标识关联字段的名字)和当前字段的值的关系，数值是判断值相等，Map Slice Array判断长度,如果有time.Time判断时间和当前时间前后关系，否则判断reflect.String
	"eqfield":    IsEqField,
	"eqcsfield":  IsEqCrossStructField,
	"necsfield":  IsNeCrossStructField,
	"gtcsfield":  IsGtCrossStructField,
	"gtecsfield": IsGteCrossStructField,
	"ltcsfield":  IsLtCrossStructField,
	"ltecsfield": IsLteCrossStructField,
	"nefield":    IsNeField,
	"gtefield":   IsGteField,
	"gtfield":    IsGtField,
	"ltefield":   IsLteField,
	"ltfield":    IsLtField,

	"alpha":    IsAlpha,    //这将验证字符串值是否仅包含ASCII字母字符
	"alphanum": IsAlphanum, //这将验证字符串值是否仅包含ASCII字母数字字符

	"numeric":     IsNumeric,     //这将验证字符串值是否包含基本数值。基本排除指数等...对于整数或浮点数，它返回true。
	"number":      IsNumber,      //判断是否Number
	"hexadecimal": IsHexadecimal, //这将验证字符串值是否包含有效的十六进制

	"hexcolor": IsHEXColor, //这验证字符串值包含有效的十六进制颜色，包括＃标签（＃）
	"rgb":      IsRGB,      //这将验证字符串值是否包含有效的rgb颜色
	"rgba":     IsRGBA,     //这将验证字符串值是否包含有效的rgba颜色
	"hsl":      IsHSL,      //这将验证字符串值是否包含有效的hsl颜色
	"hsla":     IsHSLA,     //这将验证字符串值是否包含有效的hsla颜色

	"email":  IsEmail,  //这验证字符串值包含有效的电子邮件这可能不符合任何rfc标准的所有可能性，但任何电子邮件提供商都不接受所有可能性
	"url":    IsURL,    //这会验证字符串值是否包含有效的url这将接受golang请求uri接受的任何url，但必须包含一个模式，例如http：//或rtmp：//
	"uri":    IsURI,    //这验证了字符串值包含有效的uri。这将接受uri接受的golang请求的任何uri
	"base64": IsBase64, //这将验证字符串值是否包含有效的base64值。虽然空字符串是有效的base64，但这会将空字符串报告为错误，如果您希望接受空字符串作为有效字符，则可以将此字符串与omitempty标记一起使用。

	//read note 验证字符串是否【包含】/【不包含】对应的子字符串(这个方法看源码是处理Field.String()是否包含对应的子串,而如果不是字符串的话返回的是<invalid string>，不知道这个会不会被对比)
	"contains":     Contains,
	"containsany":  ContainsAny,
	"containsrune": ContainsRune,
	"excludes":     Excludes,
	"excludesall":  ExcludesAll,
	"excludesrune": ExcludesRune,

	//read note 校验字符串是否有效的isbn值
	"isbn":   IsISBN,
	"isbn10": IsISBN10,
	"isbn13": IsISBN13,

	//read note 校验字符串是否有效的uuid值
	"uuid":  IsUUID,
	"uuid3": IsUUID3,
	"uuid4": IsUUID4,
	"uuid5": IsUUID5,

	"ascii":      IsASCII,               //这将验证字符串值是否仅包含ASCII字符。注意：如果字符串为空，则验证为true
	"printascii": IsPrintableASCII,      //这将验证字符串值是否仅包含可打印的ASCII字符。注意：如果字符串为空，则验证为true。
	"multibyte":  HasMultiByteCharacter, //这将验证字符串值是否包含一个或多个多字节字符。注意：如果字符串为空，则验证为true
	"datauri":    IsDataURI,             //这将验证字符串值是否包含有效的DataURI。注意：这也将验证数据部分是否有效base64
	"latitude":   IsLatitude,            //这将验证字符串值是否包含有效的纬度。
	"longitude":  IsLongitude,           //这将验证字符串值是否包含有效经度。
	"ssn":        IsSSN,                 //这将验证字符串值是否包含有效的美国社会安全号码。
	"ipv4":       IsIPv4,                //这将验证字符串值是否包含有效的v4 IP地址
	"ipv6":       IsIPv6,                //这将验证字符串值是否包含有效的v6 IP地址
	"ip":         IsIP,                  //这将验证字符串值是否包含有效的IP地址
	"cidrv4":     IsCIDRv4,              //这将验证字符串值是否包含有效的v4 CIDR地址
	"cidrv6":     IsCIDRv6,              //这将验证字符串值是否包含有效的v6 CIDR地址
	"cidr":       IsCIDR,                //这将验证字符串值是否包含有效的CIDR地址
	"tcp4_addr":  IsTCP4AddrResolvable,  //这将验证字符串值是否包含有效的可解析v4 TCP地址
	"tcp6_addr":  IsTCP6AddrResolvable,  //这将验证字符串值是否包含有效的可解析v6 TCP地址
	"tcp_addr":   IsTCPAddrResolvable,   //这将验证字符串值是否包含有效的可解析TCP地址
	"udp4_addr":  IsUDP4AddrResolvable,  //这将验证字符串值是否包含有效的可解析v4 UDP地址
	"udp6_addr":  IsUDP6AddrResolvable,  //这将验证字符串值是否包含有效的可解析v6 UDP地址
	"udp_addr":   IsUDPAddrResolvable,   //这将验证字符串值是否包含有效的可解析UDP地址
	"ip4_addr":   IsIP4AddrResolvable,   //这将验证字符串值是否包含有效的可解析v4 IP地址
	"ip6_addr":   IsIP6AddrResolvable,   //这将验证字符串值是否包含有效的可解析v6 IP地址
	"ip_addr":    IsIPAddrResolvable,    //这将验证字符串值是否包含有效的可解析IP地址
	"unix_addr":  IsUnixAddrResolvable,  //这将验证字符串值是否包含有效的Unix地址
	"mac":        IsMAC,                 //这将验证字符串值是否包含有效的MAC地址
}

// IsMAC is the validation function for validating if the field's value is a valid MAC address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsMAC(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	_, err := net.ParseMAC(field.String())
	return err == nil
}

// IsCIDRv4 is the validation function for validating if the field's value is a valid v4 CIDR address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsCIDRv4(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	ip, _, err := net.ParseCIDR(field.String())

	return err == nil && ip.To4() != nil
}

// IsCIDRv6 is the validation function for validating if the field's value is a valid v6 CIDR address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsCIDRv6(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	ip, _, err := net.ParseCIDR(field.String())

	return err == nil && ip.To4() == nil
}

// IsCIDR is the validation function for validating if the field's value is a valid v4 or v6 CIDR address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsCIDR(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	_, _, err := net.ParseCIDR(field.String())

	return err == nil
}

// IsIPv4 is the validation function for validating if a value is a valid v4 IP address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsIPv4(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	ip := net.ParseIP(field.String())

	return ip != nil && ip.To4() != nil
}

// IsIPv6 is the validation function for validating if the field's value is a valid v6 IP address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsIPv6(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	ip := net.ParseIP(field.String())

	return ip != nil && ip.To4() == nil
}

// IsIP is the validation function for validating if the field's value is a valid v4 or v6 IP address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsIP(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	ip := net.ParseIP(field.String())

	return ip != nil
}

// IsSSN is the validation function for validating if the field's value is a valid SSN.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsSSN(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	if field.Len() != 11 {
		return false
	}

	return sSNRegex.MatchString(field.String())
}

// IsLongitude is the validation function for validating if the field's value is a valid longitude coordinate.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsLongitude(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return longitudeRegex.MatchString(field.String())
}

// IsLatitude is the validation function for validating if the field's value is a valid latitude coordinate.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsLatitude(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return latitudeRegex.MatchString(field.String())
}

// IsDataURI is the validation function for validating if the field's value is a valid data URI.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsDataURI(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	uri := strings.SplitN(field.String(), ",", 2)

	if len(uri) != 2 {
		return false
	}

	if !dataURIRegex.MatchString(uri[0]) {
		return false
	}

	fld := reflect.ValueOf(uri[1])

	return IsBase64(v, topStruct, currentStructOrField, fld, fld.Type(), fld.Kind(), param)
}

// HasMultiByteCharacter is the validation function for validating if the field's value has a multi byte character.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func HasMultiByteCharacter(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	if field.Len() == 0 {
		return true
	}

	return multibyteRegex.MatchString(field.String())
}

// IsPrintableASCII is the validation function for validating if the field's value is a valid printable ASCII character.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsPrintableASCII(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return printableASCIIRegex.MatchString(field.String())
}

// IsASCII is the validation function for validating if the field's value is a valid ASCII character.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsASCII(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return aSCIIRegex.MatchString(field.String())
}

// IsUUID5 is the validation function for validating if the field's value is a valid v5 UUID.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsUUID5(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return uUID5Regex.MatchString(field.String())
}

// IsUUID4 is the validation function for validating if the field's value is a valid v4 UUID.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsUUID4(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return uUID4Regex.MatchString(field.String())
}

// IsUUID3 is the validation function for validating if the field's value is a valid v3 UUID.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsUUID3(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return uUID3Regex.MatchString(field.String())
}

// IsUUID is the validation function for validating if the field's value is a valid UUID of any version.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsUUID(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return uUIDRegex.MatchString(field.String())
}

// IsISBN is the validation function for validating if the field's value is a valid v10 or v13 ISBN.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsISBN(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return IsISBN10(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) || IsISBN13(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param)
}

// IsISBN13 is the validation function for validating if the field's value is a valid v13 ISBN.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsISBN13(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	s := strings.Replace(strings.Replace(field.String(), "-", "", 4), " ", "", 4)

	if !iSBN13Regex.MatchString(s) {
		return false
	}

	var checksum int32
	var i int32

	factor := []int32{1, 3}

	for i = 0; i < 12; i++ {
		checksum += factor[i%2] * int32(s[i]-'0')
	}

	return (int32(s[12]-'0'))-((10-(checksum%10))%10) == 0
}

// IsISBN10 is the validation function for validating if the field's value is a valid v10 ISBN.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsISBN10(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	s := strings.Replace(strings.Replace(field.String(), "-", "", 3), " ", "", 3)

	if !iSBN10Regex.MatchString(s) {
		return false
	}

	var checksum int32
	var i int32

	for i = 0; i < 9; i++ {
		checksum += (i + 1) * int32(s[i]-'0')
	}

	if s[9] == 'X' {
		checksum += 10 * 10
	} else {
		checksum += 10 * int32(s[9]-'0')
	}

	return checksum%11 == 0
}

// ExcludesRune is the validation function for validating that the field's value does not contain the rune specified within the param.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func ExcludesRune(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return !ContainsRune(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param)
}

// ExcludesAll is the validation function for validating that the field's value does not contain any of the characters specified within the param.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func ExcludesAll(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return !ContainsAny(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param)
}

// Excludes is the validation function for validating that the field's value does not contain the text specified within the param.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func Excludes(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return !Contains(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param)
}

// ContainsRune is the validation function for validating that the field's value contains the rune specified within the param.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func ContainsRune(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	r, _ := utf8.DecodeRuneInString(param)

	return strings.ContainsRune(field.String(), r)
}

// ContainsAny is the validation function for validating that the field's value contains any of the characters specified within the param.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func ContainsAny(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return strings.ContainsAny(field.String(), param)
}

// Contains is the validation function for validating that the field's value contains the text specified within the param.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func Contains(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return strings.Contains(field.String(), param)
}

// IsNeField is the validation function for validating if the current field's value is not equal to the field specified by the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsNeField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param)

	if !ok || currentKind != fieldKind {
		return true
	}

	switch fieldKind {

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return field.Int() != currentField.Int()

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return field.Uint() != currentField.Uint()

	case reflect.Float32, reflect.Float64:
		return field.Float() != currentField.Float()

	case reflect.Slice, reflect.Map, reflect.Array:
		return int64(field.Len()) != int64(currentField.Len())

	case reflect.Struct:

		// Not Same underlying type i.e. struct and time
		if fieldType != currentField.Type() {
			return true
		}

		if fieldType == timeType {

			t := currentField.Interface().(time.Time)
			fieldTime := field.Interface().(time.Time)

			return !fieldTime.Equal(t)
		}

	}

	// default reflect.String:
	return field.String() != currentField.String()
}

// IsNe is the validation function for validating that the field's value does not equal the provided param value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsNe(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return !IsEq(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param)
}

// IsLteCrossStructField is the validation function for validating if the current field's value is less than or equal to the field, within a separate struct, specified by the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsLteCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	topField, topKind, ok := v.GetStructFieldOK(topStruct, param)
	if !ok || topKind != fieldKind {
		return false
	}

	switch fieldKind {

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return field.Int() <= topField.Int()

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return field.Uint() <= topField.Uint()

	case reflect.Float32, reflect.Float64:
		return field.Float() <= topField.Float()

	case reflect.Slice, reflect.Map, reflect.Array:
		return int64(field.Len()) <= int64(topField.Len())

	case reflect.Struct:

		// Not Same underlying type i.e. struct and time
		if fieldType != topField.Type() {
			return false
		}

		if fieldType == timeType {

			fieldTime := field.Interface().(time.Time)
			topTime := topField.Interface().(time.Time)

			return fieldTime.Before(topTime) || fieldTime.Equal(topTime)
		}
	}

	// default reflect.String:
	return field.String() <= topField.String()
}

// IsLtCrossStructField is the validation function for validating if the current field's value is less than the field, within a separate struct, specified by the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsLtCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	topField, topKind, ok := v.GetStructFieldOK(topStruct, param)
	if !ok || topKind != fieldKind {
		return false
	}

	switch fieldKind {

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return field.Int() < topField.Int()

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return field.Uint() < topField.Uint()

	case reflect.Float32, reflect.Float64:
		return field.Float() < topField.Float()

	case reflect.Slice, reflect.Map, reflect.Array:
		return int64(field.Len()) < int64(topField.Len())

	case reflect.Struct:

		// Not Same underlying type i.e. struct and time
		if fieldType != topField.Type() {
			return false
		}

		if fieldType == timeType {

			fieldTime := field.Interface().(time.Time)
			topTime := topField.Interface().(time.Time)

			return fieldTime.Before(topTime)
		}
	}

	// default reflect.String:
	return field.String() < topField.String()
}

// IsGteCrossStructField is the validation function for validating if the current field's value is greater than or equal to the field, within a separate struct, specified by the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsGteCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	topField, topKind, ok := v.GetStructFieldOK(topStruct, param)
	if !ok || topKind != fieldKind {
		return false
	}

	switch fieldKind {

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return field.Int() >= topField.Int()

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return field.Uint() >= topField.Uint()

	case reflect.Float32, reflect.Float64:
		return field.Float() >= topField.Float()

	case reflect.Slice, reflect.Map, reflect.Array:
		return int64(field.Len()) >= int64(topField.Len())

	case reflect.Struct:

		// Not Same underlying type i.e. struct and time
		if fieldType != topField.Type() {
			return false
		}

		if fieldType == timeType {

			fieldTime := field.Interface().(time.Time)
			topTime := topField.Interface().(time.Time)

			return fieldTime.After(topTime) || fieldTime.Equal(topTime)
		}
	}

	// default reflect.String:
	return field.String() >= topField.String()
}

// IsGtCrossStructField is the validation function for validating if the current field's value is greater than the field, within a separate struct, specified by the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsGtCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	topField, topKind, ok := v.GetStructFieldOK(topStruct, param)
	if !ok || topKind != fieldKind {
		return false
	}

	switch fieldKind {

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return field.Int() > topField.Int()

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return field.Uint() > topField.Uint()

	case reflect.Float32, reflect.Float64:
		return field.Float() > topField.Float()

	case reflect.Slice, reflect.Map, reflect.Array:
		return int64(field.Len()) > int64(topField.Len())

	case reflect.Struct:

		// Not Same underlying type i.e. struct and time
		if fieldType != topField.Type() {
			return false
		}

		if fieldType == timeType {

			fieldTime := field.Interface().(time.Time)
			topTime := topField.Interface().(time.Time)

			return fieldTime.After(topTime)
		}
	}

	// default reflect.String:
	return field.String() > topField.String()
}

// IsNeCrossStructField is the validation function for validating that the current field's value is not equal to the field, within a separate struct, specified by the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsNeCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	topField, currentKind, ok := v.GetStructFieldOK(topStruct, param)
	if !ok || currentKind != fieldKind {
		return true
	}

	switch fieldKind {

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return topField.Int() != field.Int()

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return topField.Uint() != field.Uint()

	case reflect.Float32, reflect.Float64:
		return topField.Float() != field.Float()

	case reflect.Slice, reflect.Map, reflect.Array:
		return int64(topField.Len()) != int64(field.Len())

	case reflect.Struct:

		// Not Same underlying type i.e. struct and time
		if fieldType != topField.Type() {
			return true
		}

		if fieldType == timeType {

			t := field.Interface().(time.Time)
			fieldTime := topField.Interface().(time.Time)

			return !fieldTime.Equal(t)
		}
	}

	// default reflect.String:
	return topField.String() != field.String()
}

// IsEqCrossStructField is the validation function for validating that the current field's value is equal to the field, within a separate struct, specified by the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsEqCrossStructField(v *Validate, topStruct reflect.Value, current reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	topField, topKind, ok := v.GetStructFieldOK(topStruct, param)
	if !ok || topKind != fieldKind {
		return false
	}

	switch fieldKind {

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return topField.Int() == field.Int()

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return topField.Uint() == field.Uint()

	case reflect.Float32, reflect.Float64:
		return topField.Float() == field.Float()

	case reflect.Slice, reflect.Map, reflect.Array:
		return int64(topField.Len()) == int64(field.Len())

	case reflect.Struct:

		// Not Same underlying type i.e. struct and time
		if fieldType != topField.Type() {
			return false
		}

		if fieldType == timeType {

			t := field.Interface().(time.Time)
			fieldTime := topField.Interface().(time.Time)

			return fieldTime.Equal(t)
		}
	}

	// default reflect.String:
	return topField.String() == field.String()
}

// IsEqField is the validation function for validating if the current field's value is equal to the field specified by the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsEqField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param)
	if !ok || currentKind != fieldKind {
		return false
	}

	switch fieldKind {

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return field.Int() == currentField.Int()

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return field.Uint() == currentField.Uint()

	case reflect.Float32, reflect.Float64:
		return field.Float() == currentField.Float()

	case reflect.Slice, reflect.Map, reflect.Array:
		return int64(field.Len()) == int64(currentField.Len())

	case reflect.Struct:

		// Not Same underlying type i.e. struct and time
		if fieldType != currentField.Type() {
			return false
		}

		if fieldType == timeType {

			t := currentField.Interface().(time.Time)
			fieldTime := field.Interface().(time.Time)

			return fieldTime.Equal(t)
		}

	}

	// default reflect.String:
	return field.String() == currentField.String()
}

// IsEq is the validation function for validating if the current field's value is equal to the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsEq(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	switch fieldKind {

	case reflect.String:
		return field.String() == param

	case reflect.Slice, reflect.Map, reflect.Array:
		p := asInt(param)

		return int64(field.Len()) == p

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		p := asInt(param)

		return field.Int() == p

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		p := asUint(param)

		return field.Uint() == p

	case reflect.Float32, reflect.Float64:
		p := asFloat(param)

		return field.Float() == p
	}

	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}

// IsBase64 is the validation function for validating if the current field's value is a valid base 64.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsBase64(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return base64Regex.MatchString(field.String())
}

// IsURI is the validation function for validating if the current field's value is a valid URI.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsURI(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	switch fieldKind {

	case reflect.String:

		s := field.String()

		// checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195
		// emulate browser and strip the '#' suffix prior to validation. see issue-#237
		if i := strings.Index(s, "#"); i > -1 {
			s = s[:i]
		}

		if s == blank {
			return false
		}

		_, err := url.ParseRequestURI(s)

		return err == nil
	}

	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}

// IsURL is the validation function for validating if the current field's value is a valid URL.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsURL(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	switch fieldKind {

	case reflect.String:

		var i int
		s := field.String()

		// checks needed as of Go 1.6 because of change https://github.com/golang/go/commit/617c93ce740c3c3cc28cdd1a0d712be183d0b328#diff-6c2d018290e298803c0c9419d8739885L195
		// emulate browser and strip the '#' suffix prior to validation. see issue-#237
		if i = strings.Index(s, "#"); i > -1 {
			s = s[:i]
		}

		if s == blank {
			return false
		}

		url, err := url.ParseRequestURI(s)

		if err != nil || url.Scheme == blank {
			return false
		}

		return err == nil
	}

	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}

// IsEmail is the validation function for validating if the current field's value is a valid email address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsEmail(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return emailRegex.MatchString(field.String())
}

// IsHSLA is the validation function for validating if the current field's value is a valid HSLA color.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsHSLA(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return hslaRegex.MatchString(field.String())
}

// IsHSL is the validation function for validating if the current field's value is a valid HSL color.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsHSL(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return hslRegex.MatchString(field.String())
}

// IsRGBA is the validation function for validating if the current field's value is a valid RGBA color.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsRGBA(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return rgbaRegex.MatchString(field.String())
}

// IsRGB is the validation function for validating if the current field's value is a valid RGB color.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsRGB(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return rgbRegex.MatchString(field.String())
}

// IsHEXColor is the validation function for validating if the current field's value is a valid HEX color.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsHEXColor(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return hexcolorRegex.MatchString(field.String())
}

// IsHexadecimal is the validation function for validating if the current field's value is a valid hexadecimal.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsHexadecimal(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return hexadecimalRegex.MatchString(field.String())
}

// IsNumber is the validation function for validating if the current field's value is a valid number.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsNumber(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return numberRegex.MatchString(field.String())
}

// IsNumeric is the validation function for validating if the current field's value is a valid numeric value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsNumeric(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return numericRegex.MatchString(field.String())
}

// IsAlphanum is the validation function for validating if the current field's value is a valid alphanumeric value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsAlphanum(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return alphaNumericRegex.MatchString(field.String())
}

// IsAlpha is the validation function for validating if the current field's value is a valid alpha value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsAlpha(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return alphaRegex.MatchString(field.String())
}

// HasValue is the validation function for validating if the current field's value is not the default static value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func HasValue(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	switch fieldKind {
	case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
		return !field.IsNil()
	default:
		return field.IsValid() && field.Interface() != reflect.Zero(fieldType).Interface()
	}
}

// IsGteField is the validation function for validating if the current field's value is greater than or equal to the field specified by the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsGteField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param)
	if !ok || currentKind != fieldKind {
		return false
	}

	switch fieldKind {

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:

		return field.Int() >= currentField.Int()

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:

		return field.Uint() >= currentField.Uint()

	case reflect.Float32, reflect.Float64:

		return field.Float() >= currentField.Float()

	case reflect.Struct:

		// Not Same underlying type i.e. struct and time
		if fieldType != currentField.Type() {
			return false
		}

		if fieldType == timeType {

			t := currentField.Interface().(time.Time)
			fieldTime := field.Interface().(time.Time)

			return fieldTime.After(t) || fieldTime.Equal(t)
		}
	}

	// default reflect.String
	return len(field.String()) >= len(currentField.String())
}

// IsGtField is the validation function for validating if the current field's value is greater than the field specified by the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsGtField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param)
	if !ok || currentKind != fieldKind {
		return false
	}

	switch fieldKind {

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:

		return field.Int() > currentField.Int()

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:

		return field.Uint() > currentField.Uint()

	case reflect.Float32, reflect.Float64:

		return field.Float() > currentField.Float()

	case reflect.Struct:

		// Not Same underlying type i.e. struct and time
		if fieldType != currentField.Type() {
			return false
		}

		if fieldType == timeType {

			t := currentField.Interface().(time.Time)
			fieldTime := field.Interface().(time.Time)

			return fieldTime.After(t)
		}
	}

	// default reflect.String
	return len(field.String()) > len(currentField.String())
}

// IsGte is the validation function for validating if the current field's value is greater than or equal to the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsGte(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	switch fieldKind {

	case reflect.String:
		p := asInt(param)

		return int64(utf8.RuneCountInString(field.String())) >= p

	case reflect.Slice, reflect.Map, reflect.Array:
		p := asInt(param)

		return int64(field.Len()) >= p

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		p := asInt(param)

		return field.Int() >= p

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		p := asUint(param)

		return field.Uint() >= p

	case reflect.Float32, reflect.Float64:
		p := asFloat(param)

		return field.Float() >= p

	case reflect.Struct:

		if fieldType == timeType || fieldType == timePtrType {

			now := time.Now().UTC()
			t := field.Interface().(time.Time)

			return t.After(now) || t.Equal(now)
		}
	}

	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}

// IsGt is the validation function for validating if the current field's value is greater than the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsGt(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	switch fieldKind {

	case reflect.String:
		p := asInt(param)

		return int64(utf8.RuneCountInString(field.String())) > p

	case reflect.Slice, reflect.Map, reflect.Array:
		p := asInt(param)

		return int64(field.Len()) > p

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		p := asInt(param)

		return field.Int() > p

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		p := asUint(param)

		return field.Uint() > p

	case reflect.Float32, reflect.Float64:
		p := asFloat(param)

		return field.Float() > p
	case reflect.Struct:

		if fieldType == timeType || fieldType == timePtrType {

			return field.Interface().(time.Time).After(time.Now().UTC())
		}
	}

	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}

// HasLengthOf is the validation function for validating if the current field's value is equal to the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func HasLengthOf(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	switch fieldKind {

	case reflect.String:
		p := asInt(param)

		return int64(utf8.RuneCountInString(field.String())) == p

	case reflect.Slice, reflect.Map, reflect.Array:
		p := asInt(param)

		return int64(field.Len()) == p

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		p := asInt(param)

		return field.Int() == p

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		p := asUint(param)

		return field.Uint() == p

	case reflect.Float32, reflect.Float64:
		p := asFloat(param)

		return field.Float() == p
	}

	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}

// HasMinOf is the validation function for validating if the current field's value is greater than or equal to the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func HasMinOf(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	return IsGte(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param)
}

// IsLteField is the validation function for validating if the current field's value is less than or equal to the field specified by the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsLteField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param)
	if !ok || currentKind != fieldKind {
		return false
	}

	switch fieldKind {

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:

		return field.Int() <= currentField.Int()

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:

		return field.Uint() <= currentField.Uint()

	case reflect.Float32, reflect.Float64:

		return field.Float() <= currentField.Float()

	case reflect.Struct:

		// Not Same underlying type i.e. struct and time
		if fieldType != currentField.Type() {
			return false
		}

		if fieldType == timeType {

			t := currentField.Interface().(time.Time)
			fieldTime := field.Interface().(time.Time)

			return fieldTime.Before(t) || fieldTime.Equal(t)
		}
	}

	// default reflect.String
	return len(field.String()) <= len(currentField.String())
}

// IsLtField is the validation function for validating if the current field's value is less than the field specified by the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsLtField(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param)
	if !ok || currentKind != fieldKind {
		return false
	}

	switch fieldKind {

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:

		return field.Int() < currentField.Int()

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:

		return field.Uint() < currentField.Uint()

	case reflect.Float32, reflect.Float64:

		return field.Float() < currentField.Float()

	case reflect.Struct:

		// Not Same underlying type i.e. struct and time
		if fieldType != currentField.Type() {
			return false
		}

		if fieldType == timeType {

			t := currentField.Interface().(time.Time)
			fieldTime := field.Interface().(time.Time)

			return fieldTime.Before(t)
		}
	}

	// default reflect.String
	return len(field.String()) < len(currentField.String())
}

// IsLte is the validation function for validating if the current field's value is less than or equal to the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsLte(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	switch fieldKind {

	case reflect.String:
		p := asInt(param)

		return int64(utf8.RuneCountInString(field.String())) <= p

	case reflect.Slice, reflect.Map, reflect.Array:
		p := asInt(param)

		return int64(field.Len()) <= p

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		p := asInt(param)

		return field.Int() <= p

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		p := asUint(param)

		return field.Uint() <= p

	case reflect.Float32, reflect.Float64:
		p := asFloat(param)

		return field.Float() <= p

	case reflect.Struct:

		if fieldType == timeType || fieldType == timePtrType {

			now := time.Now().UTC()
			t := field.Interface().(time.Time)

			return t.Before(now) || t.Equal(now)
		}
	}

	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}

// IsLt is the validation function for validating if the current field's value is less than the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsLt(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	switch fieldKind {

	case reflect.String:
		p := asInt(param)

		return int64(utf8.RuneCountInString(field.String())) < p

	case reflect.Slice, reflect.Map, reflect.Array:
		p := asInt(param)

		return int64(field.Len()) < p

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		p := asInt(param)

		return field.Int() < p

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		p := asUint(param)

		return field.Uint() < p

	case reflect.Float32, reflect.Float64:
		p := asFloat(param)

		return field.Float() < p

	case reflect.Struct:

		if fieldType == timeType || fieldType == timePtrType {

			return field.Interface().(time.Time).Before(time.Now().UTC())
		}
	}

	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}

// HasMaxOf is the validation function for validating if the current field's value is less than or equal to the param's value.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func HasMaxOf(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	return IsLte(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param)
}

// IsTCP4AddrResolvable is the validation function for validating if the field's value is a resolvable tcp4 address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsTCP4AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	if !isIP4Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) {
		return false
	}

	_, err := net.ResolveTCPAddr("tcp4", field.String())
	return err == nil
}

// IsTCP6AddrResolvable is the validation function for validating if the field's value is a resolvable tcp6 address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsTCP6AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	if !isIP6Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) {
		return false
	}

	_, err := net.ResolveTCPAddr("tcp6", field.String())
	return err == nil
}

// IsTCPAddrResolvable is the validation function for validating if the field's value is a resolvable tcp address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsTCPAddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	if !isIP4Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) &&
		!isIP6Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) {
		return false
	}

	_, err := net.ResolveTCPAddr("tcp", field.String())
	return err == nil
}

// IsUDP4AddrResolvable is the validation function for validating if the field's value is a resolvable udp4 address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsUDP4AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	if !isIP4Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) {
		return false
	}

	_, err := net.ResolveUDPAddr("udp4", field.String())
	return err == nil
}

// IsUDP6AddrResolvable is the validation function for validating if the field's value is a resolvable udp6 address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsUDP6AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	if !isIP6Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) {
		return false
	}

	_, err := net.ResolveUDPAddr("udp6", field.String())
	return err == nil
}

// IsUDPAddrResolvable is the validation function for validating if the field's value is a resolvable udp address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsUDPAddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	if !isIP4Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) &&
		!isIP6Addr(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) {
		return false
	}

	_, err := net.ResolveUDPAddr("udp", field.String())
	return err == nil
}

// IsIP4AddrResolvable is the validation function for validating if the field's value is a resolvable ip4 address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsIP4AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	if !IsIPv4(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) {
		return false
	}

	_, err := net.ResolveIPAddr("ip4", field.String())
	return err == nil
}

// IsIP6AddrResolvable is the validation function for validating if the field's value is a resolvable ip6 address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsIP6AddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	if !IsIPv6(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) {
		return false
	}

	_, err := net.ResolveIPAddr("ip6", field.String())
	return err == nil
}

// IsIPAddrResolvable is the validation function for validating if the field's value is a resolvable ip address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsIPAddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {

	if !IsIP(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) {
		return false
	}

	_, err := net.ResolveIPAddr("ip", field.String())
	return err == nil
}

// IsUnixAddrResolvable is the validation function for validating if the field's value is a resolvable unix address.
// NOTE: This is exposed for use within your own custom functions and not intended to be called directly.
func IsUnixAddrResolvable(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	_, err := net.ResolveUnixAddr("unix", field.String())
	return err == nil
}

func isIP4Addr(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	val := field.String()

	if idx := strings.LastIndex(val, ":"); idx != -1 {
		val = val[0:idx]
	}

	if !IsIPv4(v, topStruct, currentStructOrField, reflect.ValueOf(val), fieldType, fieldKind, param) {
		return false
	}

	return true
}

func isIP6Addr(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
	val := field.String()

	if idx := strings.LastIndex(val, ":"); idx != -1 {
		if idx != 0 && val[idx-1:idx] == "]" {
			val = val[1 : idx-1]
		}
	}

	if !IsIPv6(v, topStruct, currentStructOrField, reflect.ValueOf(val), fieldType, fieldKind, param) {
		return false
	}

	return true
}
