package hscan

import (
	
	
	
	
)

// decoderFunc represents decoding functions for default built-in types.
type decoderFunc func(reflect.Value, string) error

// Scanner is the interface implemented by themselves,
// which will override the decoding behavior of decoderFunc.
type Scanner interface {
	ScanRedis(s string) error
}

var (
	// List of built-in decoders indexed by their numeric constant values (eg: reflect.Bool = 1).
	decoders = []decoderFunc{
		reflect.Bool:          decodeBool,
		reflect.Int:           decodeInt,
		reflect.Int8:          decodeInt8,
		reflect.Int16:         decodeInt16,
		reflect.Int32:         decodeInt32,
		reflect.Int64:         decodeInt64,
		reflect.Uint:          decodeUint,
		reflect.Uint8:         decodeUint8,
		reflect.Uint16:        decodeUint16,
		reflect.Uint32:        decodeUint32,
		reflect.Uint64:        decodeUint64,
		reflect.Float32:       decodeFloat32,
		reflect.Float64:       decodeFloat64,
		reflect.Complex64:     decodeUnsupported,
		reflect.Complex128:    decodeUnsupported,
		reflect.Array:         decodeUnsupported,
		reflect.Chan:          decodeUnsupported,
		reflect.Func:          decodeUnsupported,
		reflect.Interface:     decodeUnsupported,
		reflect.Map:           decodeUnsupported,
		reflect.Ptr:           decodeUnsupported,
		reflect.Slice:         decodeSlice,
		reflect.String:        decodeString,
		reflect.Struct:        decodeUnsupported,
		reflect.UnsafePointer: decodeUnsupported,
	}

	// Global map of struct field specs that is populated once for every new
	// struct type that is scanned. This caches the field types and the corresponding
	// decoder functions to avoid iterating through struct fields on subsequent scans.
	globalStructMap = newStructMap()
)

func ( interface{}) (StructValue, error) {
	 := reflect.ValueOf()

	// The destination to scan into should be a struct pointer.
	if .Kind() != reflect.Ptr || .IsNil() {
		return StructValue{}, fmt.Errorf("redis.Scan(non-pointer %T)", )
	}

	 = .Elem()
	if .Kind() != reflect.Struct {
		return StructValue{}, fmt.Errorf("redis.Scan(non-struct %T)", )
	}

	return StructValue{
		spec:  globalStructMap.get(.Type()),
		value: ,
	}, nil
}

// Scan scans the results from a key-value Redis map result set to a destination struct.
// The Redis keys are matched to the struct's field with the `redis` tag.
func ( interface{},  []interface{},  []interface{}) error {
	if len() != len() {
		return errors.New("args should have the same number of keys and vals")
	}

	,  := Struct()
	if  != nil {
		return 
	}

	// Iterate through the (key, value) sequence.
	for  := 0;  < len(); ++ {
		,  := [].(string)
		if ! {
			continue
		}

		,  := [].(string)
		if ! {
			continue
		}

		if  := .Scan(, );  != nil {
			return 
		}
	}

	return nil
}

func decodeBool( reflect.Value,  string) error {
	,  := strconv.ParseBool()
	if  != nil {
		return 
	}
	.SetBool()
	return nil
}

func decodeInt8( reflect.Value,  string) error {
	return decodeNumber(, , 8)
}

func decodeInt16( reflect.Value,  string) error {
	return decodeNumber(, , 16)
}

func decodeInt32( reflect.Value,  string) error {
	return decodeNumber(, , 32)
}

func decodeInt64( reflect.Value,  string) error {
	return decodeNumber(, , 64)
}

func decodeInt( reflect.Value,  string) error {
	return decodeNumber(, , 0)
}

func decodeNumber( reflect.Value,  string,  int) error {
	,  := strconv.ParseInt(, 10, )
	if  != nil {
		return 
	}
	.SetInt()
	return nil
}

func decodeUint8( reflect.Value,  string) error {
	return decodeUnsignedNumber(, , 8)
}

func decodeUint16( reflect.Value,  string) error {
	return decodeUnsignedNumber(, , 16)
}

func decodeUint32( reflect.Value,  string) error {
	return decodeUnsignedNumber(, , 32)
}

func decodeUint64( reflect.Value,  string) error {
	return decodeUnsignedNumber(, , 64)
}

func decodeUint( reflect.Value,  string) error {
	return decodeUnsignedNumber(, , 0)
}

func decodeUnsignedNumber( reflect.Value,  string,  int) error {
	,  := strconv.ParseUint(, 10, )
	if  != nil {
		return 
	}
	.SetUint()
	return nil
}

func decodeFloat32( reflect.Value,  string) error {
	,  := strconv.ParseFloat(, 32)
	if  != nil {
		return 
	}
	.SetFloat()
	return nil
}

// although the default is float64, but we better define it.
func decodeFloat64( reflect.Value,  string) error {
	,  := strconv.ParseFloat(, 64)
	if  != nil {
		return 
	}
	.SetFloat()
	return nil
}

func decodeString( reflect.Value,  string) error {
	.SetString()
	return nil
}

func decodeSlice( reflect.Value,  string) error {
	// []byte slice ([]uint8).
	if .Type().Elem().Kind() == reflect.Uint8 {
		.SetBytes([]byte())
	}
	return nil
}

func decodeUnsupported( reflect.Value,  string) error {
	return fmt.Errorf("redis.Scan(unsupported %s)", .Type())
}