package convert

import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
)

var (
	ErrUnsupported = errors.New("unsupported")
)

func isBytes(v reflect.Value) bool {
	if v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8 {
		return true
	}
	return false
}

func StringValue(v interface{}) (s string, err error) {
	if v == nil {
		return
	}
	refValue := reflect.Indirect(reflect.ValueOf(v))
	switch refValue.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		s = strconv.FormatInt(refValue.Int(), 10)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		s = strconv.FormatUint(refValue.Uint(), 10)
	case reflect.Float32, reflect.Float64:
		s = strconv.FormatFloat(refValue.Float(), 'f', -1, 64)
	default:
		if isBytes(refValue) {
			s = string(refValue.Bytes())
		} else {
			s = fmt.Sprint(refValue.Interface())
		}
	}
	return
}

func IntegerValue(v interface{}) (n int64, err error) {
	if v == nil {
		return
	}
	refValue := reflect.Indirect(reflect.ValueOf(v))
	switch refValue.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		n = refValue.Int()
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		n = int64(refValue.Uint())
	case reflect.Float32, reflect.Float64:
		n = int64(refValue.Float())
	case reflect.String:
		n, err = strconv.ParseInt(refValue.String(), 10, 64)
	default:
		if isBytes(refValue) {
			n, err = strconv.ParseInt(string(refValue.Bytes()), 10, 64)
		} else {
			err = ErrUnsupported
		}
	}
	return
}

func FloatValue(v interface{}) (n float64, err error) {
	if v == nil {
		return
	}
	refValue := reflect.Indirect(reflect.ValueOf(v))
	switch refValue.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		n = float64(refValue.Int())
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		n = float64(refValue.Uint())
	case reflect.Float32, reflect.Float64:
		n = refValue.Float()
	case reflect.String:
		n, err = strconv.ParseFloat(refValue.String(), 64)
	default:
		if isBytes(refValue) {
			n, err = strconv.ParseFloat(string(refValue.Bytes()), 64)
		} else {
			err = ErrUnsupported
		}
	}
	return
}

func MustString(v interface{}) (s string) {
	s, _ = StringValue(v)
	return
}

func MustInteger(v interface{}) (n int64) {
	n, _ = IntegerValue(v)
	return
}

func MustFloat(v interface{}) (n float64) {
	n, _ = FloatValue(v)
	return
}
