package form

import (
	"fmt"
	"log"
	"net/url"
	"reflect"
	"strconv"
	"time"
)

const (
	errArraySize           = "Array size of '%d' is larger than the maximum currently set on the decoder of '%d'. To increase this limit please see, SetMaxArraySize(size uint)"
	errMissingStartBracket = "Invalid formatting for key '%s' missing '[' bracket"
	errMissingEndBracket   = "Invalid formatting for key '%s' missing ']' bracket"
)

type decoder struct {
	d         *Decoder
	errs      DecodeErrors
	dm        dataMap
	values    url.Values
	maxKeyLen int
	namespace []byte
}

func (d *decoder) setError(namespace []byte, err error) {
	if d.errs == nil {
		d.errs = make(DecodeErrors)
	}
	d.errs[string(namespace)] = err
}

func (d *decoder) findAlias(ns string) *recursiveData {
	for i := 0; i < len(d.dm); i++ {
		if d.dm[i].alias == ns {
			return d.dm[i]
		}
	}
	return nil
}

func (d *decoder) parseMapData() {
	// already parsed
	if len(d.dm) > 0 {
		return
	}

	d.maxKeyLen = 0
	d.dm = d.dm[0:0]

	var i int
	var idx int
	var l int
	var insideBracket bool
	var rd *recursiveData
	var isNum bool

	for k := range d.values {

		if len(k) > d.maxKeyLen {
			d.maxKeyLen = len(k)
		}

		for i = 0; i < len(k); i++ {

			switch k[i] {
			case '[':
				idx = i
				insideBracket = true
				isNum = true
			case ']':

				if !insideBracket {
					log.Panicf(errMissingStartBracket, k)
				}

				if rd = d.findAlias(k[:idx]); rd == nil {

					l = len(d.dm) + 1

					if l > cap(d.dm) {
						dm := make(dataMap, l)
						copy(dm, d.dm)
						rd = new(recursiveData)
						dm[len(d.dm)] = rd
						d.dm = dm
					} else {
						l = len(d.dm)
						d.dm = d.dm[:l+1]
						rd = d.dm[l]
						rd.sliceLen = 0
						rd.keys = rd.keys[0:0]
					}

					rd.alias = k[:idx]
				}

				// is map + key
				ke := key{
					ivalue:      -1,
					value:       k[idx+1 : i],
					searchValue: k[idx : i+1],
				}

				// is key is number, most likely array key, keep track of just in case an array/slice.
				if isNum {

					// no need to check for error, it will always pass
					// as we have done the checking to ensure
					// the value is a number ahead of time.
					var err error
					ke.ivalue, err = strconv.Atoi(ke.value)
					if err != nil {
						ke.ivalue = -1
					}

					if ke.ivalue > rd.sliceLen {
						rd.sliceLen = ke.ivalue

					}
				}

				rd.keys = append(rd.keys, ke)

				insideBracket = false
			default:
				// checking if not a number, 0-9 is 48-57 in byte, see for yourself fmt.Println('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')
				if insideBracket && (k[i] > 57 || k[i] < 48) {
					isNum = false
				}
			}
		}

		// if still inside bracket, that means no ending bracket was ever specified
		if insideBracket {
			log.Panicf(errMissingEndBracket, k)
		}
	}
}

func (d *decoder) traverseStruct(v reflect.Value, typ reflect.Type, namespace []byte) (set bool) {

	l := len(namespace)
	first := l == 0

	// anonymous structs will still work for caching as the whole definition is stored
	// including tags
	s, ok := d.d.structCache.Get(typ)
	if !ok {
		s = d.d.structCache.parseStruct(d.d.mode, v, typ, d.d.tagName)
	}

	for _, f := range s.fields {
		namespace = namespace[:l]

		if f.isAnonymous {
			if d.setFieldByType(v.Field(f.idx), namespace, 0) {
				set = true
			}
		}

		if first {
			namespace = append(namespace, f.name...)
		} else {
			namespace = append(namespace, d.d.namespacePrefix...)
			namespace = append(namespace, f.name...)
			namespace = append(namespace, d.d.namespaceSuffix...)
		}

		if d.setFieldByType(v.Field(f.idx), namespace, 0) {
			set = true
		}
	}

	return
}

func (d *decoder) setFieldByType(current reflect.Value, namespace []byte, idx int) (set bool) {

	var err error
	v, kind := ExtractType(current)

	arr, ok := d.values[string(namespace)]

	if d.d.customTypeFuncs != nil {

		if ok {
			if cf, ok := d.d.customTypeFuncs[v.Type()]; ok {
				val, err := cf(arr[idx:])
				if err != nil {
					d.setError(namespace, err)
					return
				}

				v.Set(reflect.ValueOf(val))
				set = true
				return
			}
		}
	}
	switch kind {
	case reflect.Interface:
		if !ok || idx == len(arr) {
			return
		}
		v.Set(reflect.ValueOf(arr[idx]))
		set = true

	case reflect.Ptr:
		newVal := reflect.New(v.Type().Elem())
		if set = d.setFieldByType(newVal.Elem(), namespace, idx); set {
			v.Set(newVal)
		}

	case reflect.String:
		if !ok || idx == len(arr) {
			return
		}
		v.SetString(arr[idx])
		set = true

	case reflect.Uint, reflect.Uint64:
		if !ok || idx == len(arr) || len(arr[idx]) == 0 {
			return
		}
		var u64 uint64
		if u64, err = strconv.ParseUint(arr[idx], 10, 64); err != nil {
			d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
			return
		}
		v.SetUint(u64)
		set = true

	case reflect.Uint8:
		if !ok || idx == len(arr) || len(arr[idx]) == 0 {
			return
		}
		var u64 uint64
		if u64, err = strconv.ParseUint(arr[idx], 10, 8); err != nil {
			d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
			return
		}
		v.SetUint(u64)
		set = true

	case reflect.Uint16:
		if !ok || idx == len(arr) || len(arr[idx]) == 0 {
			return
		}
		var u64 uint64
		if u64, err = strconv.ParseUint(arr[idx], 10, 16); err != nil {
			d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
			return
		}
		v.SetUint(u64)
		set = true

	case reflect.Uint32:
		if !ok || idx == len(arr) || len(arr[idx]) == 0 {
			return
		}
		var u64 uint64
		if u64, err = strconv.ParseUint(arr[idx], 10, 32); err != nil {
			d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
			return
		}
		v.SetUint(u64)
		set = true

	case reflect.Int, reflect.Int64:
		if !ok || idx == len(arr) || len(arr[idx]) == 0 {
			return
		}
		var i64 int64
		if i64, err = strconv.ParseInt(arr[idx], 10, 64); err != nil {
			d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
			return
		}
		v.SetInt(i64)
		set = true

	case reflect.Int8:
		if !ok || idx == len(arr) || len(arr[idx]) == 0 {
			return
		}
		var i64 int64
		if i64, err = strconv.ParseInt(arr[idx], 10, 8); err != nil {
			d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
			return
		}
		v.SetInt(i64)
		set = true

	case reflect.Int16:
		if !ok || idx == len(arr) || len(arr[idx]) == 0 {
			return
		}
		var i64 int64
		if i64, err = strconv.ParseInt(arr[idx], 10, 16); err != nil {
			d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
			return
		}
		v.SetInt(i64)
		set = true

	case reflect.Int32:
		if !ok || idx == len(arr) || len(arr[idx]) == 0 {
			return
		}
		var i64 int64
		if i64, err = strconv.ParseInt(arr[idx], 10, 32); err != nil {
			d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
			return
		}
		v.SetInt(i64)
		set = true

	case reflect.Float32:
		if !ok || idx == len(arr) || len(arr[idx]) == 0 {
			return
		}
		var f float64
		if f, err = strconv.ParseFloat(arr[idx], 32); err != nil {
			d.setError(namespace, fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
			return
		}
		v.SetFloat(f)
		set = true

	case reflect.Float64:
		if !ok || idx == len(arr) || len(arr[idx]) == 0 {
			return
		}
		var f float64
		if f, err = strconv.ParseFloat(arr[idx], 64); err != nil {
			d.setError(namespace, fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
			return
		}
		v.SetFloat(f)
		set = true

	case reflect.Bool:
		if !ok || idx == len(arr) {
			return
		}
		var b bool
		if b, err = parseBool(arr[idx]); err != nil {
			d.setError(namespace, fmt.Errorf("Invalid Boolean Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
			return
		}
		v.SetBool(b)
		set = true

	case reflect.Slice:
		d.parseMapData()
		// slice elements could be mixed eg. number and non-numbers Value[0]=[]string{"10"} and Value=[]string{"10","20"}

		if ok && len(arr) > 0 {
			var varr reflect.Value

			var ol int
			l := len(arr)

			if v.IsNil() {
				varr = reflect.MakeSlice(v.Type(), len(arr), len(arr))
			} else {

				ol = v.Len()
				l += ol

				if v.Cap() <= l {
					varr = reflect.MakeSlice(v.Type(), l, l)
				} else {
					// preserve predefined capacity, possibly for reuse after decoding
					varr = reflect.MakeSlice(v.Type(), l, v.Cap())
				}
				reflect.Copy(varr, v)
			}

			for i := ol; i < l; i++ {
				newVal := reflect.New(v.Type().Elem()).Elem()

				if d.setFieldByType(newVal, namespace, i-ol) {
					set = true
					varr.Index(i).Set(newVal)
				}
			}

			v.Set(varr)
		}

		// maybe it's an numbered array i.e. Phone[0].Number
		if rd := d.findAlias(string(namespace)); rd != nil {

			var varr reflect.Value
			var kv key

			sl := rd.sliceLen + 1

			// checking below for maxArraySize, but if array exists and already
			// has sufficient capacity allocated then we do not check as the code
			// obviously allows a capacity greater than the maxArraySize.

			if v.IsNil() {

				if sl > d.d.maxArraySize {
					d.setError(namespace, fmt.Errorf(errArraySize, sl, d.d.maxArraySize))
					return
				}

				varr = reflect.MakeSlice(v.Type(), sl, sl)

			} else if v.Len() < sl {

				if v.Cap() <= sl {

					if sl > d.d.maxArraySize {
						d.setError(namespace, fmt.Errorf(errArraySize, sl, d.d.maxArraySize))
						return
					}

					varr = reflect.MakeSlice(v.Type(), sl, sl)
				} else {
					varr = reflect.MakeSlice(v.Type(), sl, v.Cap())
				}

				reflect.Copy(varr, v)

			} else {
				varr = v
			}

			for i := 0; i < len(rd.keys); i++ {

				kv = rd.keys[i]
				newVal := reflect.New(varr.Type().Elem()).Elem()

				if kv.ivalue == -1 {
					d.setError(namespace, fmt.Errorf("invalid slice index '%s'", kv.value))
					continue
				}

				if d.setFieldByType(newVal, append(namespace, kv.searchValue...), 0) {
					set = true
					varr.Index(kv.ivalue).Set(newVal)
				}
			}

			if !set {
				return
			}

			v.Set(varr)
		}

	case reflect.Array:
		d.parseMapData()

		// array elements could be mixed eg. number and non-numbers Value[0]=[]string{"10"} and Value=[]string{"10","20"}

		if ok && len(arr) > 0 {
			var varr reflect.Value
			l := len(arr)
			overCapacity := v.Len() < l
			if overCapacity {
				// more values than array capacity, ignore values over capacity as it's possible some would just want
				// to grab the first x number of elements; in the future strict mode logic should return an error
				fmt.Println("warning number of post form array values is larger than array capacity, ignoring overflow values")
			}
			varr = reflect.Indirect(reflect.New(reflect.ArrayOf(v.Len(), v.Type().Elem())))
			reflect.Copy(varr, v)

			if v.Len() < len(arr) {
				l = v.Len()
			}
			for i := 0; i < l; i++ {
				newVal := reflect.New(v.Type().Elem()).Elem()

				if d.setFieldByType(newVal, namespace, i) {
					set = true
					varr.Index(i).Set(newVal)
				}
			}
			v.Set(varr)
		}

		// maybe it's an numbered array i.e. Phone[0].Number
		if rd := d.findAlias(string(namespace)); rd != nil {
			var varr reflect.Value
			var kv key

			overCapacity := rd.sliceLen >= v.Len()
			if overCapacity {
				// more values than array capacity, ignore values over capacity as it's possible some would just want
				// to grab the first x number of elements; in the future strict mode logic should return an error
				fmt.Println("warning number of post form array values is larger than array capacity, ignoring overflow values")
			}
			varr = reflect.Indirect(reflect.New(reflect.ArrayOf(v.Len(), v.Type().Elem())))
			reflect.Copy(varr, v)

			for i := 0; i < len(rd.keys); i++ {
				kv = rd.keys[i]
				if kv.ivalue >= v.Len() {
					continue
				}
				newVal := reflect.New(varr.Type().Elem()).Elem()

				if kv.ivalue == -1 {
					d.setError(namespace, fmt.Errorf("invalid array index '%s'", kv.value))
					continue
				}

				if d.setFieldByType(newVal, append(namespace, kv.searchValue...), 0) {
					set = true
					varr.Index(kv.ivalue).Set(newVal)
				}
			}

			if !set {
				return
			}
			v.Set(varr)
		}

	case reflect.Map:
		var rd *recursiveData

		d.parseMapData()

		// no natural map support so skip directly to dm lookup
		if rd = d.findAlias(string(namespace)); rd == nil {
			return
		}

		var existing bool
		var kv key
		var mp reflect.Value
		var mk reflect.Value

		typ := v.Type()

		if v.IsNil() {
			mp = reflect.MakeMap(typ)
		} else {
			existing = true
			mp = v
		}

		for i := 0; i < len(rd.keys); i++ {
			newVal := reflect.New(typ.Elem()).Elem()
			mk = reflect.New(typ.Key()).Elem()
			kv = rd.keys[i]

			if err := d.getMapKey(kv.value, mk, namespace); err != nil {
				d.setError(namespace, err)
				continue
			}

			if d.setFieldByType(newVal, append(namespace, kv.searchValue...), 0) {
				set = true
				mp.SetMapIndex(mk, newVal)
			}
		}

		if !set || existing {
			return
		}

		v.Set(mp)

	case reflect.Struct:
		typ := v.Type()

		// if we get here then no custom time function declared so use RFC3339 by default
		if typ == timeType {

			if !ok || len(arr[idx]) == 0 {
				return
			}

			t, err := time.Parse(time.RFC3339, arr[idx])
			if err != nil {
				d.setError(namespace, err)
			}

			v.Set(reflect.ValueOf(t))
			set = true
			return
		}

		d.parseMapData()

		// we must be recursing infinitly...but that's ok we caught it on the very first overun.
		if len(namespace) > d.maxKeyLen {
			return
		}

		set = d.traverseStruct(v, typ, namespace)
	}
	return
}

func (d *decoder) getMapKey(key string, current reflect.Value, namespace []byte) (err error) {

	v, kind := ExtractType(current)

	if d.d.customTypeFuncs != nil {
		if cf, ok := d.d.customTypeFuncs[v.Type()]; ok {

			val, er := cf([]string{key})
			if er != nil {
				err = er
				return
			}

			v.Set(reflect.ValueOf(val))
			return
		}
	}

	switch kind {
	case reflect.Interface:
		// If interface would have been set on the struct before decoding,
		// say to a struct value we would not get here but kind would be struct.
		v.Set(reflect.ValueOf(key))
		return
	case reflect.Ptr:
		newVal := reflect.New(v.Type().Elem())
		if err = d.getMapKey(key, newVal.Elem(), namespace); err == nil {
			v.Set(newVal)
		}

	case reflect.String:
		v.SetString(key)

	case reflect.Uint, reflect.Uint64:

		u64, e := strconv.ParseUint(key, 10, 64)
		if e != nil {
			err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
			return
		}

		v.SetUint(u64)

	case reflect.Uint8:

		u64, e := strconv.ParseUint(key, 10, 8)
		if e != nil {
			err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
			return
		}

		v.SetUint(u64)

	case reflect.Uint16:

		u64, e := strconv.ParseUint(key, 10, 16)
		if e != nil {
			err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
			return
		}

		v.SetUint(u64)

	case reflect.Uint32:

		u64, e := strconv.ParseUint(key, 10, 32)
		if e != nil {
			err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
			return
		}

		v.SetUint(u64)

	case reflect.Int, reflect.Int64:

		i64, e := strconv.ParseInt(key, 10, 64)
		if e != nil {
			err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
			return
		}

		v.SetInt(i64)

	case reflect.Int8:

		i64, e := strconv.ParseInt(key, 10, 8)
		if e != nil {
			err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
			return
		}

		v.SetInt(i64)

	case reflect.Int16:

		i64, e := strconv.ParseInt(key, 10, 16)
		if e != nil {
			err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
			return
		}

		v.SetInt(i64)

	case reflect.Int32:

		i64, e := strconv.ParseInt(key, 10, 32)
		if e != nil {
			err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
			return
		}

		v.SetInt(i64)

	case reflect.Float32:

		f, e := strconv.ParseFloat(key, 32)
		if e != nil {
			err = fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
			return
		}

		v.SetFloat(f)

	case reflect.Float64:

		f, e := strconv.ParseFloat(key, 64)
		if e != nil {
			err = fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
			return
		}

		v.SetFloat(f)

	case reflect.Bool:

		b, e := parseBool(key)
		if e != nil {
			err = fmt.Errorf("Invalid Boolean Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
			return
		}

		v.SetBool(b)

	default:
		err = fmt.Errorf("Unsupported Map Key '%s', Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
	}

	return
}