package mathutil

import (
	
	
	
	

	
	
	
)

// ConvOption convert options
type ConvOption[ any] struct {
	// if ture: value is nil, will return convert error;
	// if false(default): value is nil, will convert to zero value
	NilAsFail bool
	// HandlePtr auto convert ptr type(int,float,string) value. eg: *int to int
	// 	- if true: will use real type try convert. default is false
	//	- NOTE: current T type's ptr is default support.
	HandlePtr bool
	// StrictMode for convert value. default is false
	//
	// TRUE:
	//  - to int: string, float will return error
	StrictMode bool
	// set custom fallback convert func for not supported type.
	UserConvFn ToTypeFunc[]
}

// NewConvOption create a new ConvOption
func [ any]( ...ConvOptionFn[]) *ConvOption[] {
	 := &ConvOption[]{}
	.WithOption(...)
	return 
}

// WithOption set convert option
func ( *ConvOption[]) ( ...ConvOptionFn[]) {
	for ,  := range  {
		if  != nil {
			()
		}
	}
}

// ConvOptionFn convert option func
type ConvOptionFn[ any] func(opt *ConvOption[])

// WithNilAsFail set ConvOption.NilAsFail option
//
// Example:
//
//	ToIntWithFunc(val, mathutil.WithNilAsFail[int])
func [ any]( *ConvOption[]) { .NilAsFail = true }

// WithHandlePtr set ConvOption.HandlePtr option
func [ any]( *ConvOption[]) { .HandlePtr = true }

// WithStrictMode set ConvOption.StrictMode option
func [ any]( *ConvOption[]) { .StrictMode = true }

// WithUserConvFn set ConvOption.UserConvFn option
func [ any]( ToTypeFunc[]) ConvOptionFn[] {
	return func( *ConvOption[]) {
		.UserConvFn = 
	}
}

/*************************************************************
 * region Strict to int/uint
 *************************************************************/

// StrictInt check the given value is an integer(intX,uintX), return the int64 value and true if success
func ( any) (int64, bool) {
	switch tVal := .(type) {
	case int:
		return int64(), true
	case int8:
		return int64(), true
	case int16:
		return int64(), true
	case int32:
		return int64(), true
	case int64:
		return , true
	case uint:
		return int64(), true
	case uint8:
		return int64(), true
	case uint16:
		return int64(), true
	case uint32:
		return int64(), true
	case uint64:
		return int64(), true
	case uintptr:
		return int64(), true
	default:
		return 0, false
	}
}

// StrictUint strict check value is integer(intX,uintX) and convert to uint64.
func ( any) (uint64, bool) {
	switch tVal := .(type) {
	case int:
		return uint64(), true
	case int8:
		return uint64(), true
	case int16:
		return uint64(), true
	case int32:
		return uint64(), true
	case int64:
		return uint64(), true
	case uint:
		return uint64(), true
	case uint8:
		return uint64(), true
	case uint16:
		return uint64(), true
	case uint32:
		return uint64(), true
	case uint64:
		return , true
	case uintptr:
		return uint64(), true
	default:
		return 0, false
	}
}

/*************************************************************
 * region convert to float64
 *************************************************************/

// QuietFloat convert value to float64, will ignore error. alias of SafeFloat
func ( any) float64 { return SafeFloat() }

// SafeFloat convert value to float64, will ignore error
func ( any) float64 {
	,  := ToFloatWith()
	return 
}

// FloatOrPanic convert value to float64, will panic on error
func ( any) float64 { return MustFloat() }

// MustFloat convert value to float64, will panic on error
func ( any) float64 {
	,  := ToFloatWith()
	if  != nil {
		panic()
	}
	return 
}

// FloatOrDefault convert value to float64, will return default value on error
func ( any,  float64) float64 { return FloatOr(, ) }

// FloatOr convert value to float64, will return default value on error
func ( any,  float64) float64 {
	,  := ToFloatWith()
	if  != nil {
		return 
	}
	return 
}

// Float convert value to float64, return error on failed
func ( any) (float64, error) { return ToFloatWith() }

// FloatOrErr convert value to float64, return error on failed
func ( any) (float64, error) { return ToFloatWith() }

// ToFloat convert value to float64, return error on failed
func ( any) (float64, error) { return ToFloatWith() }

// ToFloatWith try to convert value to float64. can with some option func, more see ConvOption.
func ( any,  ...ConvOptionFn[float64]) ( float64,  error) {
	 := NewConvOption(...)
	if !.NilAsFail &&  == nil {
		return 0, nil
	}

	switch tVal := .(type) {
	case string:
		,  = strconv.ParseFloat(strings.TrimSpace(), 64)
	case int:
		 = float64()
	case int8:
		 = float64()
	case int16:
		 = float64()
	case int32:
		 = float64()
	case int64:
		 = float64()
	case uint:
		 = float64()
	case uint8:
		 = float64()
	case uint16:
		 = float64()
	case uint32:
		 = float64()
	case uint64:
		 = float64()
	case float32:
		 = float64()
	case float64:
		 = 
	case *float64: // default support float64 ptr type
		 = *
	case time.Duration:
		 = float64()
	case comdef.Float64able: // eg: json.Number
		,  = .Float64()
	default:
		if .HandlePtr {
			if  := reflect.ValueOf(); .Kind() == reflect.Pointer {
				 = .Elem()
				if checkfn.IsSimpleKind(.Kind()) {
					return (.Interface(), ...)
				}
			}
		}

		if .UserConvFn != nil {
			,  = .UserConvFn()
		} else {
			 = comdef.ErrConvType
		}
	}
	return
}

/*************************************************************
 * region intX/floatX to string
 *************************************************************/

// MustString convert intX/floatX value to string, will panic on error
func ( any) string {
	,  := ToStringWith()
	if  != nil {
		panic()
	}
	return 
}

// StringOrPanic convert intX/floatX value to string, will panic on error
func ( any) string { return MustString() }

// StringOrDefault convert intX/floatX value to string, will return default value on error
func ( any,  string) string { return StringOr(, ) }

// StringOr convert intX/floatX value to string, will return default value on error
func ( any,  string) string {
	,  := ToStringWith()
	if  != nil {
		return 
	}
	return 
}

// ToString convert intX/floatX value to string, return error on failed
func ( any) (string, error) { return ToStringWith() }

// StringOrErr convert intX/floatX value to string, return error on failed
func ( any) (string, error) { return ToStringWith() }

// QuietString convert intX/floatX value to string, other type convert by fmt.Sprint
func ( any) string { return SafeString() }

// String convert intX/floatX value to string, other type convert by fmt.Sprint
func ( any) string {
	,  := TryToString(, false)
	return 
}

// SafeString convert intX/floatX value to string, other type convert by fmt.Sprint
func ( any) string {
	,  := TryToString(, false)
	return 
}

// TryToString try convert intX/floatX value to string
//
// if defaultAsErr is False, will use fmt.Sprint convert other type
func ( any,  bool) (string, error) {
	var  comfunc.ConvOptionFn
	if ! {
		 = comfunc.WithUserConvFn(comfunc.StrBySprintFn)
	}
	return ToStringWith(, )
}

// ToStringWith try to convert value to string. can with some option func, more see comfunc.ConvOption.
func ( any,  ...comfunc.ConvOptionFn) (string, error) {
	return comfunc.ToStringWith(, ...)
}