package jsoniter

import (
	"errors"
	"fmt"
	"github.com/modern-go/reflect2"
	"io"
	"reflect"
	"strconv"
	"unsafe"
)

// Any generic object representation.
// The lazy json implementation holds []byte and parse lazily.
type Any interface {
	LastError() error
	ValueType() ValueType
	MustBeValid() Any
	ToBool() bool
	ToInt() int
	ToInt32() int32
	ToInt64() int64
	ToUint() uint
	ToUint32() uint32
	ToUint64() uint64
	ToFloat32() float32
	ToFloat64() float64
	ToString() string
	ToVal(val interface{})
	Get(path ...interface{}) Any
	Size() int
	Keys() []string
	GetInterface() interface{}
	WriteTo(stream *Stream)
}

type baseAny struct{}

func (any *baseAny) Get(path ...interface{}) Any {
	return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)}
}

func (any *baseAny) Size() int {
	return 0
}

func (any *baseAny) Keys() []string {
	return []string{}
}

func (any *baseAny) ToVal(obj interface{}) {
	panic("not implemented")
}

// WrapInt32 turn int32 into Any interface
func WrapInt32(val int32) Any {
	return &int32Any{baseAny{}, val}
}

// WrapInt64 turn int64 into Any interface
func WrapInt64(val int64) Any {
	return &int64Any{baseAny{}, val}
}

// WrapUint32 turn uint32 into Any interface
func WrapUint32(val uint32) Any {
	return &uint32Any{baseAny{}, val}
}

// WrapUint64 turn uint64 into Any interface
func WrapUint64(val uint64) Any {
	return &uint64Any{baseAny{}, val}
}

// WrapFloat64 turn float64 into Any interface
func WrapFloat64(val float64) Any {
	return &floatAny{baseAny{}, val}
}

// WrapString turn string into Any interface
func WrapString(val string) Any {
	return &stringAny{baseAny{}, val}
}

// Wrap turn a go object into Any interface
func Wrap(val interface{}) Any {
	if val == nil {
		return &nilAny{}
	}
	asAny, isAny := val.(Any)
	if isAny {
		return asAny
	}
	typ := reflect2.TypeOf(val)
	switch typ.Kind() {
	case reflect.Slice:
		return wrapArray(val)
	case reflect.Struct:
		return wrapStruct(val)
	case reflect.Map:
		return wrapMap(val)
	case reflect.String:
		return WrapString(val.(string))
	case reflect.Int:
		if strconv.IntSize == 32 {
			return WrapInt32(int32(val.(int)))
		}
		return WrapInt64(int64(val.(int)))
	case reflect.Int8:
		return WrapInt32(int32(val.(int8)))
	case reflect.Int16:
		return WrapInt32(int32(val.(int16)))
	case reflect.Int32:
		return WrapInt32(val.(int32))
	case reflect.Int64:
		return WrapInt64(val.(int64))
	case reflect.Uint:
		if strconv.IntSize == 32 {
			return WrapUint32(uint32(val.(uint)))
		}
		return WrapUint64(uint64(val.(uint)))
	case reflect.Uintptr:
		if ptrSize == 32 {
			return WrapUint32(uint32(val.(uintptr)))
		}
		return WrapUint64(uint64(val.(uintptr)))
	case reflect.Uint8:
		return WrapUint32(uint32(val.(uint8)))
	case reflect.Uint16:
		return WrapUint32(uint32(val.(uint16)))
	case reflect.Uint32:
		return WrapUint32(uint32(val.(uint32)))
	case reflect.Uint64:
		return WrapUint64(val.(uint64))
	case reflect.Float32:
		return WrapFloat64(float64(val.(float32)))
	case reflect.Float64:
		return WrapFloat64(val.(float64))
	case reflect.Bool:
		if val.(bool) == true {
			return &trueAny{}
		}
		return &falseAny{}
	}
	return &invalidAny{baseAny{}, fmt.Errorf("unsupported type: %v", typ)}
}

// ReadAny read next JSON element as an Any object. It is a better json.RawMessage.
func (iter *Iterator) ReadAny() Any {
	return iter.readAny()
}

func (iter *Iterator) readAny() Any {
	c := iter.nextToken()
	switch c {
	case '"':
		iter.unreadByte()
		return &stringAny{baseAny{}, iter.ReadString()}
	case 'n':
		iter.skipThreeBytes('u', 'l', 'l') // null
		return &nilAny{}
	case 't':
		iter.skipThreeBytes('r', 'u', 'e') // true
		return &trueAny{}
	case 'f':
		iter.skipFourBytes('a', 'l', 's', 'e') // false
		return &falseAny{}
	case '{':
		return iter.readObjectAny()
	case '[':
		return iter.readArrayAny()
	case '-':
		return iter.readNumberAny(false)
	case 0:
		return &invalidAny{baseAny{}, errors.New("input is empty")}
	default:
		return iter.readNumberAny(true)
	}
}

func (iter *Iterator) readNumberAny(positive bool) Any {
	iter.startCapture(iter.head - 1)
	iter.skipNumber()
	lazyBuf := iter.stopCapture()
	return &numberLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
}

func (iter *Iterator) readObjectAny() Any {
	iter.startCapture(iter.head - 1)
	iter.skipObject()
	lazyBuf := iter.stopCapture()
	return &objectLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
}

func (iter *Iterator) readArrayAny() Any {
	iter.startCapture(iter.head - 1)
	iter.skipArray()
	lazyBuf := iter.stopCapture()
	return &arrayLazyAny{baseAny{}, iter.cfg, lazyBuf, nil}
}

func locateObjectField(iter *Iterator, target string) []byte {
	var found []byte
	iter.ReadObjectCB(func(iter *Iterator, field string) bool {
		if field == target {
			found = iter.SkipAndReturnBytes()
			return false
		}
		iter.Skip()
		return true
	})
	return found
}

func locateArrayElement(iter *Iterator, target int) []byte {
	var found []byte
	n := 0
	iter.ReadArrayCB(func(iter *Iterator) bool {
		if n == target {
			found = iter.SkipAndReturnBytes()
			return false
		}
		iter.Skip()
		n++
		return true
	})
	return found
}

func locatePath(iter *Iterator, path []interface{}) Any {
	for i, pathKeyObj := range path {
		switch pathKey := pathKeyObj.(type) {
		case string:
			valueBytes := locateObjectField(iter, pathKey)
			if valueBytes == nil {
				return newInvalidAny(path[i:])
			}
			iter.ResetBytes(valueBytes)
		case int:
			valueBytes := locateArrayElement(iter, pathKey)
			if valueBytes == nil {
				return newInvalidAny(path[i:])
			}
			iter.ResetBytes(valueBytes)
		case int32:
			if '*' == pathKey {
				return iter.readAny().Get(path[i:]...)
			}
			return newInvalidAny(path[i:])
		default:
			return newInvalidAny(path[i:])
		}
	}
	if iter.Error != nil && iter.Error != io.EOF {
		return &invalidAny{baseAny{}, iter.Error}
	}
	return iter.readAny()
}

var anyType = reflect2.TypeOfPtr((*Any)(nil)).Elem()

func createDecoderOfAny(ctx *ctx, typ reflect2.Type) ValDecoder {
	if typ == anyType {
		return &directAnyCodec{}
	}
	if typ.Implements(anyType) {
		return &anyCodec{
			valType: typ,
		}
	}
	return nil
}

func createEncoderOfAny(ctx *ctx, typ reflect2.Type) ValEncoder {
	if typ == anyType {
		return &directAnyCodec{}
	}
	if typ.Implements(anyType) {
		return &anyCodec{
			valType: typ,
		}
	}
	return nil
}

type anyCodec struct {
	valType reflect2.Type
}

func (codec *anyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
	panic("not implemented")
}

func (codec *anyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
	obj := codec.valType.UnsafeIndirect(ptr)
	any := obj.(Any)
	any.WriteTo(stream)
}

func (codec *anyCodec) IsEmpty(ptr unsafe.Pointer) bool {
	obj := codec.valType.UnsafeIndirect(ptr)
	any := obj.(Any)
	return any.Size() == 0
}

type directAnyCodec struct {
}

func (codec *directAnyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
	*(*Any)(ptr) = iter.readAny()
}

func (codec *directAnyCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
	any := *(*Any)(ptr)
	if any == nil {
		stream.WriteNil()
		return
	}
	any.WriteTo(stream)
}

func (codec *directAnyCodec) IsEmpty(ptr unsafe.Pointer) bool {
	any := *(*Any)(ptr)
	return any.Size() == 0
}
