package util

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"math"
	"reflect"
	"strconv"
	"strings"
)

// ToString returns the string result converted by src.
func ToString(src interface{}) string {
	switch v := src.(type) {
	case int, int8, int16, int32, int64:
		return strconv.FormatInt(ToInt64(v), 10)
	case uint, uint8, uint16, uint32, uint64, uintptr:
		return strconv.FormatUint(ToUint64(v), 10)
	case float32, float64, complex64, complex128:
		return strconv.FormatFloat(ToFloat64(v), 'f', -1, 64)
	case string:
		return v
	case []byte:
		return string(v)
	case []rune:
		return string(v)
	case bool:
		return strconv.FormatBool(v)
	default:
		return fmt.Sprint(v)
	}
}

// ToBool returns the bool result converted by src.
func ToBool(src interface{}) bool {
	switch v := src.(type) {
	case int, int8, int16, int32, int64:
		return ToInt64(v) > 0
	case uint, uint8, uint16, uint32, uint64, uintptr:
		return ToUint64(v) > 0
	case float32, float64, complex64, complex128:
		return ToFloat64(v) > 0
	case bool:
		return v
	case string, []byte, []rune:
		result, _ := strconv.ParseBool(ToString(v))
		return result
	default:
		return false
	}
}

// ToInt returns the int result converted by src.
func ToInt(src interface{}) int {
	return int(ToInt64(src))
}

// ToInt32 returns the int32 result converted by src.
func ToInt32(src interface{}) int32 {
	return int32(ToInt64(src))
}

// ToInt64 returns the int64 result converted by src.
func ToInt64(src interface{}) int64 {
	switch v := src.(type) {
	case int:
		return int64(v)
	case int8:
		return int64(v)
	case int16:
		return int64(v)
	case int32:
		return int64(v)
	case int64:
		return v
	case uint:
		return int64(v)
	case uint8:
		return int64(v)
	case uint16:
		return int64(v)
	case uint32:
		return int64(v)
	case uint64:
		return int64(v)
	case uintptr:
		return int64(v)
	case float32:
		return int64(v)
	case float64:
		return int64(v)
	case complex64:
		return int64(real(v))
	case complex128:
		return int64(real(v))
	case bool:
		if v {
			return 1
		}
		return 0
	case string:
		v = strings.TrimSpace(v)
		index := strings.Index(v, ".")
		if index != -1 {
			v = v[:index]
		}
		result, _ := strconv.ParseInt(v, 10, 64)
		return result
	case []byte:
		return BytesToInt64(v)
	default:
		return 0
	}
}

// ToUint returns the uint result converted by src.
func ToUint(src interface{}) uint {
	return uint(ToUint64(src))
}

// ToUint32 returns the uint32 result converted by src.
func ToUint32(src interface{}) uint32 {
	return uint32(ToUint64(src))
}

// ToUint64 returns the uint64 result converted by src.
func ToUint64(src interface{}) uint64 {
	switch v := src.(type) {
	case int:
		return uint64(v)
	case int8:
		return uint64(v)
	case int16:
		return uint64(v)
	case int32:
		return uint64(v)
	case int64:
		return uint64(v)
	case uint:
		return uint64(v)
	case uint8:
		return uint64(v)
	case uint16:
		return uint64(v)
	case uint32:
		return uint64(v)
	case uint64:
		return v
	case uintptr:
		return uint64(v)
	case float32:
		return uint64(v)
	case float64:
		return uint64(v)
	case complex64:
		return uint64(real(v))
	case complex128:
		return uint64(real(v))
	case bool:
		if v {
			return 1
		}
		return 0
	case string:
		v = strings.TrimSpace(v)
		index := strings.Index(v, ".")
		if index != -1 {
			v = v[:index]
		}
		result, _ := strconv.ParseUint(v, 10, 64)
		return result
	case []byte:
		return BytesToUint64(v)
	default:
		return 0
	}
}

// ToFloat returns the float64 result converted by src.
func ToFloat(src interface{}) float64 {
	return ToFloat64(src)
}

// ToFloat32 returns the float32 result converted by src.
func ToFloat32(src interface{}) float32 {
	return float32(ToFloat64(src))
}

// ToFloat64 returns the float64 result converted by src.
func ToFloat64(src interface{}) float64 {
	switch v := src.(type) {
	case int, int8, int16, int32, int64:
		return float64(ToInt64(v))
	case uint, uint8, uint16, uint32, uint64, uintptr:
		return float64(ToUint64(v))
	case float32:
		return float64(v)
	case float64:
		return v
	case complex64:
		return float64(real(v))
	case complex128:
		return real(v)
	case bool:
		if v {
			return 1
		}
		return 0
	case string:
		v = strings.TrimSpace(v)
		result, _ := strconv.ParseFloat(v, 64)
		return result
	case []byte:
		return BytesToFloat64(v)
	default:
		return 0
	}
}

// BytesToInt64 returns the int64 result converted by byte slice bytes.
func BytesToInt64(bytes []byte) int64 {
	return int64(BytesToUint64(bytes))
}

// Int64ToBytes returns the byte slice result converted by int64 i.
func Int64ToBytes(i int64) []byte {
	return Uint64ToBytes(uint64(i))
}

// BytesToUint64 returns the uint64 result converted by byte slice bytes.
func BytesToUint64(bytes []byte) uint64 {
	return binary.BigEndian.Uint64(bytes)
}

// Uint64ToBytes returns the byte slice result converted by uint64 i.
func Uint64ToBytes(i uint64) []byte {
	bytes := make([]byte, 8)
	binary.BigEndian.PutUint64(bytes, i)
	return bytes
}

// BytesToFloat64 returns the float64 result converted by byte slice bytes.
func BytesToFloat64(bytes []byte) float64 {
	bits := binary.BigEndian.Uint64(bytes)
	return math.Float64frombits(bits)
}

// Float64ToBytes returns the byte slice result converted by float64 f.
func Float64ToBytes(f float64) []byte {
	bits := math.Float64bits(f)
	bytes := make([]byte, 8)
	binary.BigEndian.PutUint64(bytes, bits)
	return bytes
}

// isStruct reports whether i is struct.
func isStruct(i interface{}) (reflect.Value, bool) {
	v := reflect.ValueOf(i)
	if v.Kind() == reflect.Ptr {
		if v.IsNil() {
			return reflect.Value{}, false
		}
		v = v.Elem()
	}
	if v.Kind() != reflect.Struct {
		return reflect.Value{}, false
	}
	return v, true
}

// getStructFieldName returns the struct field name.
func getStructFieldName(sf reflect.StructField) string {
	name := strings.SplitN(sf.Tag.Get("json"), ",", 2)[0]
	if name == "-" {
		return ""
	} else if name == "" {
		return sf.Name
	}
	return name
}

// StructToInterfaceMap returns the map[string]interface{} result converted by struct s.
func StructToInterfaceMap(s interface{}, ignoreZeroValue ...bool) map[string]interface{} {
	m := make(map[string]interface{})
	v, flag := isStruct(s)
	if !flag {
		return m
	}

	ignore := false
	if len(ignoreZeroValue) != 0 {
		ignore = ignoreZeroValue[0]
	}

	t := v.Type()
	for i := 0; i < t.NumField(); i++ {
		f := v.Field(i)
		name := getStructFieldName(t.Field(i))
		if name == "" || (ignore && f.IsZero()) {
			continue
		}
		if f.Kind() == reflect.Ptr && !f.IsNil() {
			f = f.Elem()
		}
		m[name] = f.Interface()
	}

	return m
}

// StructToStringMap returns the map[string]string result converted by struct s.
func StructToStringMap(s interface{}, ignoreZeroValue ...bool) map[string]string {
	m := make(map[string]string)
	v, flag := isStruct(s)
	if !flag {
		return m
	}

	ignore := false
	if len(ignoreZeroValue) != 0 {
		ignore = ignoreZeroValue[0]
	}

	t := v.Type()
	for i := 0; i < t.NumField(); i++ {
		f := v.Field(i)
		name := getStructFieldName(t.Field(i))
		if name == "" || (ignore && f.IsZero()) {
			continue
		}
		if f.Kind() == reflect.Ptr && !f.IsNil() {
			f = f.Elem()
		}
		m[name] = ToString(f.Interface())
	}

	return m
}

// ObjToMapString 将struct结构体转换成map
func ObjToMapString(obj interface{}) map[string]string {
	m := make(map[string]string)
	elem := reflect.ValueOf(obj).Elem()
	relType := elem.Type()

	for i := 0; i < relType.NumField(); i++ {
		m[relType.Field(i).Name] = ToString(elem.Field(i).Interface())
	}
	return m
}

// JsonToMapString 将json结构体转换成map
func JsonToMapString(in interface{}) map[string]string {
	m := make(map[string]interface{})
	j, _ := json.Marshal(in)
	var s string
	decoder := json.NewDecoder(strings.NewReader(string(j)))
	decoder.UseNumber()
	decoder.Decode(&m)

	//json.Unmarshal(j, &m)
	ret := make(map[string]string)
	for k, v := range m {
		s = fmt.Sprintf("%v", v)
		if s == "" {
			continue
		}
		ret[k] = s
	}
	return ret
}
