// Copyright 2021 The Xorm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package convert

import (
	"database/sql"
	"fmt"
	"math/big"
	"reflect"
	"strconv"
)

// AsFloat64 convets interface as float64
func AsFloat64(src interface{}) (float64, error) {
	switch v := src.(type) {
	case int:
		return float64(v), nil
	case int16:
		return float64(v), nil
	case int32:
		return float64(v), nil
	case int8:
		return float64(v), nil
	case int64:
		return float64(v), nil
	case uint:
		return float64(v), nil
	case uint8:
		return float64(v), nil
	case uint16:
		return float64(v), nil
	case uint32:
		return float64(v), nil
	case uint64:
		return float64(v), nil
	case []byte:
		return strconv.ParseFloat(string(v), 64)
	case string:
		return strconv.ParseFloat(v, 64)
	case *sql.NullString:
		return strconv.ParseFloat(v.String, 64)
	case *sql.NullInt32:
		return float64(v.Int32), nil
	case *sql.NullInt64:
		return float64(v.Int64), nil
	case *sql.NullFloat64:
		return v.Float64, nil
	}

	rv := reflect.ValueOf(src)
	switch rv.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return float64(rv.Int()), nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return float64(rv.Uint()), nil
	case reflect.Float64, reflect.Float32:
		return float64(rv.Float()), nil
	case reflect.String:
		return strconv.ParseFloat(rv.String(), 64)
	}
	return 0, fmt.Errorf("unsupported value %T as int64", src)
}

// AsBigFloat converts interface as big.Float
func AsBigFloat(src interface{}) (*big.Float, error) {
	res := big.NewFloat(0)
	switch v := src.(type) {
	case int:
		res.SetInt64(int64(v))
		return res, nil
	case int16:
		res.SetInt64(int64(v))
		return res, nil
	case int32:
		res.SetInt64(int64(v))
		return res, nil
	case int8:
		res.SetInt64(int64(v))
		return res, nil
	case int64:
		res.SetInt64(int64(v))
		return res, nil
	case uint:
		res.SetUint64(uint64(v))
		return res, nil
	case uint8:
		res.SetUint64(uint64(v))
		return res, nil
	case uint16:
		res.SetUint64(uint64(v))
		return res, nil
	case uint32:
		res.SetUint64(uint64(v))
		return res, nil
	case uint64:
		res.SetUint64(uint64(v))
		return res, nil
	case []byte:
		res.SetString(string(v))
		return res, nil
	case string:
		res.SetString(v)
		return res, nil
	case *sql.NullString:
		if v.Valid {
			res.SetString(v.String)
			return res, nil
		}
		return nil, nil
	case *sql.NullInt32:
		if v.Valid {
			res.SetInt64(int64(v.Int32))
			return res, nil
		}
		return nil, nil
	case *sql.NullInt64:
		if v.Valid {
			res.SetInt64(int64(v.Int64))
			return res, nil
		}
		return nil, nil
	}

	rv := reflect.ValueOf(src)
	switch rv.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		res.SetInt64(rv.Int())
		return res, nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		res.SetUint64(rv.Uint())
		return res, nil
	case reflect.Float64, reflect.Float32:
		res.SetFloat64(rv.Float())
		return res, nil
	case reflect.String:
		res.SetString(rv.String())
		return res, nil
	}
	return nil, fmt.Errorf("unsupported value %T as big.Float", src)
}