package value

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

func ValueOf(val interface{}) Value {
	return Value{
		val: val,
	}
}

type Value struct {
	val interface{}
}

func (s *Value) IsValid() bool {
	return s.val != nil
}

func (s *Value) Setup(val interface{}) (res bool) {
	rr := reflect.ValueOf(val)
	if rr.Kind() != reflect.Ptr {
		panic(fmt.Errorf("Expected ptr, given %v", rr.Type()))
	}
	rKind, rType := rr.Elem().Kind(), rr.Elem().Type()
	if rKind == reflect.Interface {
		rKind, rType = rr.Elem().Elem().Kind(), rr.Elem().Elem().Type()
	}
	if rKind == reflect.String {
		rls := strings.TrimSpace(fmt.Sprint(s.val))
		if len(rls) > 0 {
			rr.Elem().Set(reflect.ValueOf(rls))
			res = true
		}
	} else {
		rl := reflect.ValueOf(s.val)
		if rl.Kind() == reflect.String {
			switch rKind {
			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
				{
					if tmp, err := strconv.ParseInt(rl.String(), 10, 64); err == nil {
						rr.Elem().Set(reflect.ValueOf(tmp).Convert(rType))
						res = true
					}
				}
			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
				{
					if tmp, err := strconv.ParseUint(rl.String(), 10, 64); err == nil {
						rr.Elem().Set(reflect.ValueOf(tmp).Convert(rType))
						res = true
					}
				}
			case reflect.Float32, reflect.Float64:
				{
					if tmp, err := strconv.ParseFloat(rl.String(), 64); err == nil {
						rr.Elem().Set(reflect.ValueOf(tmp).Convert(rType))
						res = true
					}
				}
			case reflect.Slice:
				{
					slType := rType.Elem()
					if rr.Kind() == reflect.Slice {
						slRes := reflect.MakeSlice(rType, rr.Len(), rr.Cap())
						for i := 0; i < rl.Len(); i++ {
							if val := rl.Index(i); val.CanConvert(slType) {
								slRes.Index(i).Set(val.Convert(slType))
							} else {
								return false
							}
						}
						rr.Elem().Set(slRes)
						res = true
					} else if rl.Kind() == reflect.String {
						slType := rType.Elem()
						sls := strings.Split(rl.String(), ",")
						slRes := reflect.MakeSlice(rType, len(sls), cap(sls))
						for i, sVal := range sls {
							sItem := reflect.ValueOf(strings.TrimSpace(sVal))
							switch {
							case isKindNumber(slType.Kind()):
								{
									numVal, err := strconv.ParseFloat(sItem.String(), 64)
									if err != nil {
										return false
									}
									sItem = reflect.ValueOf(numVal)
									slRes.Index(i).Set(sItem.Convert(slType))
								}
							default:
								{
									if !sItem.CanConvert(slType) {
										return false
									}
									slRes.Index(i).Set(sItem.Convert(slType))
								}
							}
						}
						rr.Elem().Set(slRes)
						res = true
					}
					/*
						log.Println("Slice")
						slType := rType.Elem()
						if rr.Kind() == reflect.Slice {
							slRes := reflect.MakeSlice(slType, rr.Len(), rr.Cap())
							for i := 0; i < rl.Len(); i++ {
								if val := rl.Index(i); val.CanConvert(slType) {
									slRes.Index(i).Set(val.Convert(slType))
								} else {
									return false
								}
							}
							rr.Elem().Set(slRes)
							res = true
						} else if rl.Kind() == reflect.String {
							log.Println("sl <===> str")
							sls := strings.Split(rl.String(), ",")
							log.Println(slType, rr.Type())
							slRes := reflect.MakeSlice(slType, len(sls), cap(sls))
							switch {
							case isKindNumber(slType.Kind()):
								log.Println("AAAAAAAAAAAAAAAAA")
								for i, sVal := range sls {
									numVal, err := strconv.ParseFloat(sVal, 64)
									if err != nil {
										return false
									}
									rl := reflect.ValueOf(numVal)
									if val := rl.Index(i); val.CanConvert(slType) {
										slRes.Index(i).Set(val.Convert(slType))
									} else {
										return false
									}
								}
								rr.Elem().Set(slRes)
								res = true
							}
						}
					*/
				}
			default:
				// json
				i := reflect.New(rr.Elem().Type()).Interface()
				if err := json.Unmarshal([]byte(rl.String()), i); err == nil {
					rr.Elem().Set(reflect.ValueOf(i).Elem())
				}
			}
		} else {
			var rVal reflect.Value
			defer func() {
				if r := recover(); r == nil {
					rr.Elem().Set(rVal)
					res = true
				}
			}()
			rVal = rl.Convert(rType)
		}
	}
	return
}

func (s *Value) String() string {
	return fmt.Sprint(s.val)
}

func (s *Value) Int() int {
	var i int
	s.Setup(&i)
	return i
}

func (s *Value) Int8() int8 {
	var i int8
	s.Setup(&i)
	return i
}

func (s *Value) Int16() int16 {
	var i int16
	s.Setup(&i)
	return i
}

func (s *Value) Int32() int32 {
	return int32(s.Int())
}

func (s *Value) Float32() float32 {
	var i float32
	s.Setup(&i)
	return i
}

func (s *Value) Float64() float64 {
	var i float64
	s.Setup(&i)
	return i
}