package schema

import (
	
	
	
	
)

type encoderFunc func(reflect.Value) string

// Encoder encodes values from a struct into url.Values.
type Encoder struct {
	cache  *cache
	regenc map[reflect.Type]encoderFunc
}

// NewEncoder returns a new Encoder with defaults.
func () *Encoder {
	return &Encoder{cache: newCache(), regenc: make(map[reflect.Type]encoderFunc)}
}

// Encode encodes a struct into map[string][]string.
//
// Intended for use with url.Values.
func ( *Encoder) ( interface{},  map[string][]string) error {
	 := reflect.ValueOf()

	return .encode(, )
}

// RegisterEncoder registers a converter for encoding a custom type.
func ( *Encoder) ( interface{},  func(reflect.Value) string) {
	.regenc[reflect.TypeOf()] = 
}

// SetAliasTag changes the tag used to locate custom field aliases.
// The default tag is "schema".
func ( *Encoder) ( string) {
	.cache.tag = 
}

// isValidStructPointer test if input value is a valid struct pointer.
func isValidStructPointer( reflect.Value) bool {
	return .Type().Kind() == reflect.Ptr && .Elem().IsValid() && .Elem().Type().Kind() == reflect.Struct
}

func isZero( reflect.Value) bool {
	switch .Kind() {
	case reflect.Func:
	case reflect.Map, reflect.Slice:
		return .IsNil() || .Len() == 0
	case reflect.Array:
		 := true
		for  := 0;  < .Len(); ++ {
			 =  && (.Index())
		}
		return 
	case reflect.Struct:
		type  interface {
			() bool
		}
		if .Type().Implements(reflect.TypeOf((*)(nil)).Elem()) {
			 := .MethodByName("IsZero").Call([]reflect.Value{})[0]
			return .Interface().(bool)
		}
		 := true
		for  := 0;  < .NumField(); ++ {
			 =  && (.Field())
		}
		return 
	}
	// Compare other types directly:
	 := reflect.Zero(.Type())
	return .Interface() == .Interface()
}

func ( *Encoder) ( reflect.Value,  map[string][]string) error {
	if .Kind() == reflect.Ptr {
		 = .Elem()
	}
	if .Kind() != reflect.Struct {
		return errors.New("schema: interface must be a struct")
	}
	 := .Type()

	 := MultiError{}

	for  := 0;  < .NumField(); ++ {
		,  := fieldAlias(.Field(), .cache.tag)
		if  == "-" {
			continue
		}

		// Encode struct pointer types if the field is a valid pointer and a struct.
		if isValidStructPointer(.Field()) {
			_ = .(.Field().Elem(), )
			continue
		}

		 := typeEncoder(.Field().Type(), .regenc)

		// Encode non-slice types and custom implementations immediately.
		if  != nil {
			 := (.Field())
			if .Contains("omitempty") && isZero(.Field()) {
				continue
			}

			[] = append([], )
			continue
		}

		if .Field().Type().Kind() == reflect.Struct {
			_ = .(.Field(), )
			continue
		}

		if .Field().Type().Kind() == reflect.Slice {
			 = typeEncoder(.Field().Type().Elem(), .regenc)
		}

		if  == nil {
			[.Field().Type().String()] = fmt.Errorf("schema: encoder not found for %v", .Field())
			continue
		}

		// Encode a slice.
		if .Field().Len() == 0 && .Contains("omitempty") {
			continue
		}

		[] = []string{}
		for  := 0;  < .Field().Len(); ++ {
			[] = append([], (.Field().Index()))
		}
	}

	if len() > 0 {
		return 
	}
	return nil
}

func typeEncoder( reflect.Type,  map[reflect.Type]encoderFunc) encoderFunc {
	if ,  := [];  {
		return 
	}

	switch .Kind() {
	case reflect.Bool:
		return encodeBool
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return encodeInt
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return encodeUint
	case reflect.Float32:
		return encodeFloat32
	case reflect.Float64:
		return encodeFloat64
	case reflect.Ptr:
		 := (.Elem(), )
		return func( reflect.Value) string {
			if .IsNil() {
				return "null"
			}
			return (.Elem())
		}
	case reflect.String:
		return encodeString
	default:
		return nil
	}
}

func encodeBool( reflect.Value) string {
	return strconv.FormatBool(.Bool())
}

func encodeInt( reflect.Value) string {
	return strconv.FormatInt(int64(.Int()), 10)
}

func encodeUint( reflect.Value) string {
	return strconv.FormatUint(uint64(.Uint()), 10)
}

func encodeFloat( reflect.Value,  int) string {
	return strconv.FormatFloat(.Float(), 'f', 6, )
}

func encodeFloat32( reflect.Value) string {
	return encodeFloat(, 32)
}

func encodeFloat64( reflect.Value) string {
	return encodeFloat(, 64)
}

func encodeString( reflect.Value) string {
	return .String()
}