package jsonvalue

import (
	"fmt"
	"reflect"
)

// Setter type is for At() only.
//
// Setter 类型仅用于 At() 函数。
type Setter interface {
	// At completes the following operation of Set(). It defines position of value
	// in Set() and return the new value set.
	//
	// The usage of At() is perhaps the most important. This function will recursively
	// search for child value, and set the new value specified by Set() or SetXxx()
	// series functions. Please unfold and read the following examples, they are important.
	//
	// At 完成 Set() 函数的后续操作并设置相应的子成员。其参数指定了应该在哪个位置设置子成员,
	// 并且返回被设置的子成员对象。
	//
	// 该函数的用法恐怕是 jsonvalue 中最重要的内容了：该函数会按照给定的可变参数递归地一层一层查找
	// JSON 值的子成员，并且设置到指定的位置上。设置的逻辑说明起来比较抽象，请打开以下的例子以了解,
	// 这非常重要。
	At(firstParam any, otherParams ...any) (*V, error)
}

type setter struct {
	v *V
	c *V // child

	err error
}

// Set starts setting a child JSON value. Any legal JSON value typped parameter
// is accepted, such as string, int, float, bool, nil, *jsonvalue.V, or even
// a struct or map or slice.
//
// Please refer to examples of "func (set Setter) At(...)"
//
// https://godoc.org/github.com/Andrew-M-C/go.jsonvalue/#Set.At
//
// Set 开始设置一个 JSON 子成员。任何合法的 JSON 类型都可以作为参数, 比如 string, int,
// float, bool, nil, *jsonvalue.V 等类型, 甚至也支持结构体、map、切片、数组。
//
// 请参见 "func (set Setter) At(...)" 例子.
//
// https://godoc.org/github.com/Andrew-M-C/go.jsonvalue/#Set.At
func (v *V) Set(child any) Setter {
	var ch *V
	var err error

	if child == nil {
		ch = NewNull()
	} else if childV, ok := child.(*V); ok {
		ch = childV
	} else {
		ch, err = Import(child)
	}

	return &setter{
		v:   v,
		c:   ch,
		err: err,
	}
}

// SetString is equivalent to Set(jsonvalue.NewString(s))
//
// SetString 等效于 Set(jsonvalue.NewString(s))
func (v *V) SetString(s string) Setter {
	return v.Set(NewString(s))
}

// SetBytes is equivalent to Set(NewString(base64.StdEncoding.EncodeToString(b)))
//
// SetBytes 等效于 Set(NewString(base64.StdEncoding.EncodeToString(b)))
func (v *V) SetBytes(b []byte) Setter {
	s := internal.b64.EncodeToString(b)
	return v.SetString(s)
}

// SetBool is equivalent to Set(jsonvalue.NewBool(b))
//
// SetBool 等效于 Set(jsonvalue.NewBool(b))
func (v *V) SetBool(b bool) Setter {
	return v.Set(NewBool(b))
}

// SetInt is equivalent to Set(jsonvalue.NewInt(b))
//
// SetInt 等效于 Set(jsonvalue.NewInt(b))
func (v *V) SetInt(i int) Setter {
	return v.Set(NewInt(i))
}

// SetInt64 is equivalent to Set(jsonvalue.NewInt64(b))
//
// SetInt64 等效于 Set(jsonvalue.NewInt64(b))
func (v *V) SetInt64(i int64) Setter {
	return v.Set(NewInt64(i))
}

// SetInt32 is equivalent to Set(jsonvalue.NewInt32(b))
//
// SetInt32 等效于 Set(jsonvalue.NewInt32(b))
func (v *V) SetInt32(i int32) Setter {
	return v.Set(NewInt32(i))
}

// SetUint is equivalent to Set(jsonvalue.NewUint(b))
//
// SetUint 等效于 Set(jsonvalue.NewUint(b))
func (v *V) SetUint(u uint) Setter {
	return v.Set(NewUint(u))
}

// SetUint64 is equivalent to Set(jsonvalue.NewUint64(b))
//
// SetUint64 is equivalent to Set(jsonvalue.NewUint64(b))
func (v *V) SetUint64(u uint64) Setter {
	return v.Set(NewUint64(u))
}

// SetUint32 is equivalent to Set(jsonvalue.NewUint32(b))
//
// SetUint32 等效于 Set(jsonvalue.NewUint32(b))
func (v *V) SetUint32(u uint32) Setter {
	return v.Set(NewUint32(u))
}

// SetFloat64 is equivalent to Set(jsonvalue.NewFloat64(b))
//
// SetFloat64 等效于 Set(jsonvalue.NewFloat64(b))
func (v *V) SetFloat64(f float64) Setter {
	return v.Set(NewFloat64(f))
}

// SetFloat32 is equivalent to Set(jsonvalue.NewFloat32(b))
//
// SetFloat32 等效于 Set(jsonvalue.NewFloat32(b))
func (v *V) SetFloat32(f float32) Setter {
	return v.Set(NewFloat32(f))
}

// SetNull is equivalent to Set(jsonvalue.NewNull())
//
// SetNull 等效于 Set(jsonvalue.NewNull())
func (v *V) SetNull() Setter {
	return v.Set(NewNull())
}

// SetObject is equivalent to Set(jsonvalue.NewObject())
//
// SetObject 等效于 Set(jsonvalue.NewObject())
func (v *V) SetObject() Setter {
	return v.Set(NewObject())
}

// SetArray is equivalent to Set(jsonvalue.NewArray())
//
// SetArray 等效于 Set(jsonvalue.NewArray())
func (v *V) SetArray() Setter {
	return v.Set(NewArray())
}

func setToObjectChildren(v *V, key string, child *V) {
	v.children.incrID++
	v.children.object[key] = childWithProperty{
		id: v.children.incrID,
		v:  child,
	}
	addCaselessKey(v, key)
}

func (s *setter) At(firstParam any, otherParams ...any) (*V, error) {
	if s.err != nil {
		return &V{}, s.err
	}
	if ok, p1, p2 := isSliceAndExtractDividedParams(firstParam); ok {
		if len(otherParams) > 0 {
			return &V{}, ErrMultipleParamNotSupportedWithIfSliceOrArrayGiven
		}
		return s.At(p1, p2...)
	}
	v := s.v
	c := s.c
	if nil == v || v.valueType == NotExist {
		return &V{}, ErrValueUninitialized
	}
	if nil == c || c.valueType == NotExist {
		return &V{}, ErrValueUninitialized
	}

	// this is the last iteration
	if len(otherParams) == 0 {
		return s.atLastParam(firstParam)
	}

	// this is not the last iterarion
	if v.valueType == Object {
		return s.atObject(firstParam, otherParams)
	}

	// array type
	if v.valueType == Array {
		return s.atArray(firstParam, otherParams)
	}

	// illegal type
	return &V{}, fmt.Errorf("%v type does not supports Set()", v.valueType)
}

func (s *setter) atLastParam(p any) (*V, error) {
	v := s.v
	c := s.c
	switch v.valueType {
	default:
		return &V{}, fmt.Errorf("%v type does not supports Set()", v.valueType)

	case Object:
		var k string
		k, err := anyToString(p)
		if err != nil {
			return &V{}, err
		}
		setToObjectChildren(v, k, c)
		return c, nil

	case Array:
		pos, err := anyToInt(p)
		if err != nil {
			return &V{}, err
		}
		err = setAtIndex(v, c, pos)
		if err != nil {
			return &V{}, err
		}
		return c, nil
	}
}

func (s *setter) atObject(firstParam any, otherParams []any) (*V, error) {
	v := s.v
	c := s.c
	k, err := anyToString(firstParam)
	if err != nil {
		return &V{}, err
	}
	child, exist := getFromObjectChildren(v, false, k)
	if !exist {
		if _, err := anyToString(otherParams[0]); err == nil {
			child = NewObject()
		} else if i, err := anyToInt(otherParams[0]); err == nil {
			if i != 0 {
				return &V{}, ErrOutOfRange
			}
			child = NewArray()
		} else {
			return &V{}, fmt.Errorf("unexpected type %v for Set()", reflect.TypeOf(otherParams[0]))
		}
	}
	next := &setter{
		v: child,
		c: c,
	}
	_, err = next.At(otherParams[0], otherParams[1:]...)
	if err != nil {
		return &V{}, err
	}
	if !exist {
		setToObjectChildren(v, k, child)
	}
	return c, nil
}

func (s *setter) atArray(firstParam any, otherParams []any) (*V, error) {
	v := s.v
	c := s.c
	pos, err := anyToInt(firstParam)
	if err != nil {
		return &V{}, err
	}
	child, ok := childAtIndex(v, pos)
	isNewChild := false
	if !ok {
		isNewChild = true
		if _, err := anyToString(otherParams[0]); err == nil {
			child = NewObject()
		} else if i, err := anyToInt(otherParams[0]); err == nil {
			if i != 0 {
				return &V{}, ErrOutOfRange
			}
			child = NewArray()
		} else {
			return &V{}, fmt.Errorf("unexpected type %v for Set()", reflect.TypeOf(otherParams[0]))
		}
	}
	next := &setter{
		v: child,
		c: c,
	}
	_, err = next.At(otherParams[0], otherParams[1:]...)
	if err != nil {
		return &V{}, err
	}
	// OK to add this object
	if isNewChild {
		appendToArr(v, child)
	}
	return c, nil
}

func posAtIndexForSet(v *V, pos int) (newPos int, appendToEnd bool) {
	if pos == len(v.children.arr) {
		return pos, true
	}
	pos = posAtIndexForRead(v, pos)
	return pos, false
}

func posAtIndexForInsertBefore(v *V, pos int) (newPos int) {
	le := len(v.children.arr)
	if le == 0 {
		return -1
	}

	if pos == 0 {
		return 0
	}

	if pos < 0 {
		pos += le
		if pos < 0 {
			return -1
		}
		return pos
	}

	if pos >= le {
		return -1
	}

	return pos
}

func posAtIndexForInsertAfter(v *V, pos int) (newPos int, appendToEnd bool) {
	le := len(v.children.arr)
	if le == 0 {
		return -1, false
	}

	if pos == -1 {
		return le, true
	}

	if pos < 0 {
		pos += le
		if pos < 0 {
			return -1, false
		}
		return pos + 1, false
	}

	if pos >= le {
		return -1, false
	}

	return pos + 1, false
}

func posAtIndexForRead(v *V, pos int) int {
	le := len(v.children.arr)
	if le == 0 {
		return -1
	}

	if pos < 0 {
		pos += le
		if pos < 0 {
			return -1
		}
		return pos
	}

	if pos >= le {
		return -1
	}

	return pos
}

func childAtIndex(v *V, pos int) (*V, bool) { // if nil returned, means that just push
	pos = posAtIndexForRead(v, pos)
	if pos < 0 {
		return &V{}, false
	}
	return v.children.arr[pos], true
}

func setAtIndex(v *V, child *V, pos int) error {
	pos, appendToEnd := posAtIndexForSet(v, pos)
	if pos < 0 {
		return ErrOutOfRange
	}
	if appendToEnd {
		v.children.arr = append(v.children.arr, child)
	} else {
		v.children.arr[pos] = child
	}
	return nil
}
