package utils

import (
	"reflect"
	"strconv"
	"time"
)

func ValueType(in interface{}) string {
	if in == nil {
		return "<nil>"
	}
	switch in.(type) {
	case int:
		return "int"
	case int8:
		return "int8"
	case int16:
		return "int16"
	case int32:
		return "int32"
	case int64:
		return "int64"
	case uint:
		return "uint"
	case uint8:
		return "uint8"
	case uint16:
		return "uint16"
	case uint32:
		return "uint32"
	case uint64:
		return "uint64"
	case float32:
		return "float32"
	case float64:
		return "float64"
	case []byte:
		return "[]byte"
	case string:
		return "string"
	case []string:
		return "[]string"
	case *[]byte:
		return "*[]byte"
	case *string:
		return "*string"
	case []*string:
		return "[]*string"
	default:
	}
	rv := reflect.ValueOf(in)
	switch rv.Kind() {
	case reflect.Map:
		return "reflect.Map"
	case reflect.Array:
		return "reflect.Array"
	case reflect.Slice:
		return "reflect.Slice"
	}

	return "[" + rv.Kind().String() + "]" + reflect.TypeOf(in).Name()
}

func ValueToInt(in interface{}, def int) int {
	if in == nil {
		return def
	}
	switch in.(type) {
	case int:
		return in.(int)
	case int8:
		return int(in.(int8))
	case int16:
		return int(in.(int16))
	case int32:
		return int(in.(int32))
	case int64:
		return int(in.(int64))
	case uint:
		return int(in.(uint))
	case uint8:
		return int(in.(uint8))
	case uint16:
		return int(in.(uint16))
	case uint32:
		return int(in.(uint32))
	case uint64:
		return int(in.(uint64))
	case float32:
		return int(in.(float32))
	case float64:
		return int(in.(float64))
	case []byte:
		ii, _ := strconv.ParseFloat(string(in.([]byte)), 64)
		return int(ii)
	case string:
		ii, _ := strconv.ParseFloat(in.(string), 64)
		return int(ii)
	case *[]byte:
		ii, _ := strconv.ParseFloat(string(*(in.(*[]byte))), 64)
		return int(ii)
	case *string:
		ii, _ := strconv.ParseFloat(*(in.(*string)), 64)
		return int(ii)
	default:
	}
	return def
}
func ValueToFloat(in interface{}, def float64) float64 {
	if in == nil {
		return def
	}
	switch in.(type) {
	case int:
		return float64(in.(int))
	case int8:
		return float64(in.(int8))
	case int16:
		return float64(in.(int16))
	case int32:
		return float64(in.(int32))
	case int64:
		return float64(in.(int64))
	case uint:
		return float64(in.(uint))
	case uint8:
		return float64(in.(uint8))
	case uint16:
		return float64(in.(uint16))
	case uint32:
		return float64(in.(uint32))
	case uint64:
		return float64(in.(uint64))
	case float32:
		return float64(in.(float32))
	case float64:
		return in.(float64)
	case []byte:
		ii, _ := strconv.ParseFloat(string(in.([]byte)), 64)
		return ii
	case string:
		ii, _ := strconv.ParseFloat(in.(string), 64)
		return ii
	case *[]byte:
		ii, _ := strconv.ParseFloat(string(*(in.(*[]byte))), 64)
		return ii
	case *string:
		ii, _ := strconv.ParseFloat(*(in.(*string)), 64)
		return ii
	default:
	}
	return def
}

func ValueToString(in interface{}, def string) string {
	return ValueToStringIn(in, def, 0)
}

func ValueToStringIn(in interface{}, def string, fixed int) string {
	if in == nil {
		return def
	}
	switch in.(type) {
	case int:
		return strconv.FormatInt(int64(in.(int)), 10)
	case int8:
		return strconv.FormatInt(int64(in.(int8)), 10)
	case int16:
		return strconv.FormatInt(int64(in.(int16)), 10)
	case int32:
		return strconv.FormatInt(int64(in.(int32)), 10)
	case int64:
		return strconv.FormatInt(in.(int64), 10)
	case uint:
		return strconv.FormatUint(uint64(in.(uint)), 10)
	case uint8:
		return strconv.FormatUint(uint64(in.(uint8)), 10)
	case uint16:
		return strconv.FormatUint(uint64(in.(uint16)), 10)
	case uint32:
		return strconv.FormatUint(uint64(in.(uint32)), 10)
	case uint64:
		return strconv.FormatUint(in.(uint64), 10)
	case float32:
		if fixed > 0 {
			return strconv.FormatFloat(float64(in.(float32)), 'f', fixed, 32)
		}
		return strconv.FormatFloat(float64(in.(float32)), 'f', 10, 32)
	case float64:
		if fixed > 0 {
			return strconv.FormatFloat(in.(float64), 'f', fixed, 64)
		}
		return strconv.FormatFloat(in.(float64), 'f', 10, 64)
	case []byte:
		return string(in.([]byte))
	case string:
		return in.(string)
	case *[]byte:
		return string(*(in.(*[]byte)))
	case *string:
		return *(in.(*string))
	default:
	}
	return def
}

func ValueToTime(in interface{}, timeFormat string, def string) string {
	if in == nil {
		return def
	}
	switch in.(type) {
	case time.Time:
		return TimeFormat(in.(time.Time), timeFormat)
	case *time.Time:
		return TimeFormat(*(in.(*time.Time)), timeFormat)
	case []byte:
		return string(in.([]byte))
	case string:
		return in.(string)
	case *[]byte:
		return string(*(in.(*[]byte)))
	case *string:
		return *(in.(*string))
	default:
		res := ValueToInt(in, 0)
		return TimeFormat(time.Unix(int64(res), 0), timeFormat)
	}
	return def
}

func ValueIsEmpty(in interface{}) bool {
	if in == nil {
		return true
	}

	rv := reflect.ValueOf(in)
	switch rv.Kind() {
	case reflect.Map, reflect.Array, reflect.Slice:
		return rv.Len() == 0
	}

	switch in.(type) {
	case bool:
		return !in.(bool)
	case *bool:
		return !(*(in.(*bool)))
	case int:
		return in.(int) == 0
	case int8:
		return in.(int8) == 0
	case int16:
		return in.(int16) == 0
	case int32:
		return in.(int32) == 0
	case int64:
		return in.(int64) == 0
	case uint:
		return in.(uint) == 0
	case uint8:
		return in.(uint8) == 0
	case uint16:
		return in.(uint16) == 0
	case uint32:
		return in.(uint32) == 0
	case uint64:
		return in.(uint64) == 0
	case float32:
		return in.(float32) == 0.0
	case float64:
		return in.(float64) == 0.0
	case string:
		return len(in.(string)) == 0
	case *string:
		return len(*(in.(*string))) == 0
	default:
	}
	return rv.IsNil()
}

func ValueToStringArray(in interface{}) []string {
	arr, ok := in.([]interface{})
	if !ok {
		return nil
	}
	res := make([]string, 0)
	for _, v := range arr {
		res = append(res, ValueToString(v, ""))
	}
	return res
}

func ValueToIntArray(in interface{}) []int {
	arr, ok := in.([]interface{})
	if !ok {
		return nil
	}
	res := make([]int, 0)
	for _, v := range arr {
		res = append(res, ValueToInt(v, 0))
	}
	return res
}

func ValueToFloatArray(in interface{}) []float64 {
	arr, ok := in.([]interface{})
	if !ok {
		return nil
	}
	res := make([]float64, 0)
	for _, v := range arr {
		res = append(res, ValueToFloat(v, 0.0))
	}
	return res
}

func ValueFromStringArray(in []string) []interface{} {
	out := make([]interface{}, len(in))
	for k, v := range in {
		out[k] = v
	}
	return out
}

func ValueFromIntArray(in []int) []interface{} {
	out := make([]interface{}, len(in))
	for k, v := range in {
		out[k] = v
	}
	return out
}

func ValueFromFloatArray(in []float64) []interface{} {
	out := make([]interface{}, len(in))
	for k, v := range in {
		out[k] = v
	}
	return out
}

func IDsToString(ids []int) string {
	ll := len(ids)
	str := ""
	for i := 0; i < ll; i++ {
		if i > 0 {
			str = str + "','" + ValueToString(ids[i], "")
		} else {
			str = "('" + ValueToString(ids[i], "")
		}
	}
	if len(str) > 0 {
		str = str + "')"
	}
	return str
}
