package jsoniter

import (
	"fmt"
	"reflect"
)

type Any interface {
	LastError() error
	ValueType() ValueType
	ToBool() bool
	ToInt() int
	ToInt32() int32
	ToInt64() int64
	ToUint() uint
	ToUint32() uint32
	ToUint64() uint64
	ToFloat32() float32
	ToFloat64() float64
	ToString() string
	Get(path ...interface{}) Any
	Size() int
	Keys() []string
	IterateObject() (func() (string, Any, bool), bool)
	IterateArray() (func() (Any, bool), bool)
	GetArray() []Any
	SetArray(newList []Any) bool
	GetObject() map[string]Any
	SetObject(map[string]Any) bool
	GetInterface() interface{}
	WriteTo(stream *Stream)
	Parse() *Iterator
}

type baseAny struct{}

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

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

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

func (any *baseAny) IterateObject() (func() (string, Any, bool), bool) {
	return nil, false
}

func (any *baseAny) IterateArray() (func() (Any, bool), bool) {
	return nil, false
}

func (any *baseAny) GetArray() []Any {
	return []Any{}
}

func (any *baseAny) SetArray(newList []Any) bool {
	return false
}

func (any *baseAny) GetObject() map[string]Any {
	return map[string]Any{}
}

func (any *baseAny) SetObject(map[string]Any) bool {
	return false
}

func WrapInt32(val int32) Any {
	return &int32Any{baseAny{}, val}
}

func WrapInt64(val int64) Any {
	return &int64Any{baseAny{}, val}
}

func WrapUint32(val uint32) Any {
	return &uint32Any{baseAny{}, val}
}

func WrapUint64(val uint64) Any {
	return &uint64Any{baseAny{}, val}
}

func WrapFloat64(val float64) Any {
	return &floatAny{baseAny{}, val}
}

func WrapString(val string) Any {
	return &stringAny{baseAny{}, nil, val}
}

func Wrap(val interface{}) Any {
	if val == nil {
		return &nilAny{}
	}
	asAny, isAny := val.(Any)
	if isAny {
		return asAny
	}
	type_ := reflect.TypeOf(val)
	switch type_.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:
		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:
		return WrapUint64(uint64(val.(uint)))
	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{}
		} else {
			return &falseAny{}
		}
	}
	return &invalidAny{baseAny{}, fmt.Errorf("unsupported type: %v", type_)}
}

func (iter *Iterator) ReadAny() Any {
	return iter.readAny(nil)
}

func (iter *Iterator) readAny(reusableIter *Iterator) Any {
	c := iter.nextToken()
	switch c {
	case '"':
		return iter.readStringAny(reusableIter)
	case 'n':
		iter.skipFixedBytes(3) // null
		return &nilAny{}
	case 't':
		iter.skipFixedBytes(3) // true
		return &trueAny{}
	case 'f':
		iter.skipFixedBytes(4) // false
		return &falseAny{}
	case '{':
		return iter.readObjectAny(reusableIter)
	case '[':
		return iter.readArrayAny(reusableIter)
	default:
		return iter.readNumberAny(reusableIter, c)
	}
}

func (iter *Iterator) readNumberAny(reusableIter *Iterator, firstByte byte) Any {
	dotFound := false
	lazyBuf := make([]byte, 1, 8)
	lazyBuf[0] = firstByte
	for {
		for i := iter.head; i < iter.tail; i++ {
			c := iter.buf[i]
			if c == '.' {
				dotFound = true
				continue
			}
			switch c {
			case ' ', '\n', '\r', '\t', ',', '}', ']':
				lazyBuf = append(lazyBuf, iter.buf[iter.head:i]...)
				iter.head = i
				if dotFound {
					return &float64LazyAny{baseAny{}, lazyBuf, reusableIter, nil, 0}
				} else {
					if firstByte == '-' {
						return &int64LazyAny{baseAny{}, lazyBuf, reusableIter, nil, 0}
					} else {
						return &uint64LazyAny{baseAny{}, lazyBuf, reusableIter, nil, 0}
					}
				}
			}
		}
		lazyBuf = append(lazyBuf, iter.buf[iter.head:iter.tail]...)
		if !iter.loadMore() {
			iter.head = iter.tail
			if dotFound {
				return &float64LazyAny{baseAny{}, lazyBuf, reusableIter, nil, 0}
			} else {
				if firstByte == '-' {
					return &int64LazyAny{baseAny{}, lazyBuf, reusableIter, nil, 0}
				} else {
					return &uint64LazyAny{baseAny{}, lazyBuf, reusableIter, nil, 0}
				}
			}
		}
	}
}

func (iter *Iterator) readStringAny(reusableIter *Iterator) Any {
	lazyBuf := make([]byte, 1, 8)
	lazyBuf[0] = '"'
	for {
		end, escaped := iter.findStringEnd()
		if end == -1 {
			lazyBuf = append(lazyBuf, iter.buf[iter.head:iter.tail]...)
			if !iter.loadMore() {
				iter.reportError("readStringAny", "incomplete string")
				return &invalidAny{}
			}
			if escaped {
				iter.head = 1 // skip the first char as last char read is \
			}
		} else {
			lazyBuf = append(lazyBuf, iter.buf[iter.head:end]...)
			iter.head = end
			return &stringLazyAny{baseAny{}, lazyBuf, reusableIter, nil, ""}
		}
	}
}

func (iter *Iterator) readObjectAny(reusableIter *Iterator) Any {
	level := 1
	lazyBuf := make([]byte, 1, 32)
	lazyBuf[0] = '{'
	for {
		start := iter.head
		for i := iter.head; i < iter.tail; i++ {
			switch iter.buf[i] {
			case '"': // If inside string, skip it
				iter.head = i + 1
				iter.skipString()
				i = iter.head - 1 // it will be i++ soon
			case '{': // If open symbol, increase level
				level++
			case '}': // If close symbol, increase level
				level--

				// If we have returned to the original level, we're done
				if level == 0 {
					iter.head = i + 1
					lazyBuf = append(lazyBuf, iter.buf[start:iter.head]...)
					return &objectLazyAny{baseAny{}, lazyBuf, reusableIter, nil, nil, lazyBuf}
				}
			}
		}
		lazyBuf = append(lazyBuf, iter.buf[iter.head:iter.tail]...)
		if !iter.loadMore() {
			iter.reportError("skipObject", "incomplete object")
			return &invalidAny{}
		}
	}
}

func (iter *Iterator) readArrayAny(reusableIter *Iterator) Any {
	level := 1
	lazyBuf := make([]byte, 1, 32)
	lazyBuf[0] = '['
	for {
		start := iter.head
		for i := iter.head; i < iter.tail; i++ {
			switch iter.buf[i] {
			case '"': // If inside string, skip it
				iter.head = i + 1
				iter.skipString()
				i = iter.head - 1 // it will be i++ soon
			case '[': // If open symbol, increase level
				level++
			case ']': // If close symbol, increase level
				level--

				// If we have returned to the original level, we're done
				if level == 0 {
					iter.head = i + 1
					lazyBuf = append(lazyBuf, iter.buf[start:iter.head]...)
					return &arrayLazyAny{baseAny{}, lazyBuf, reusableIter, nil, nil, lazyBuf}
				}
			}
		}
		lazyBuf = append(lazyBuf, iter.buf[iter.head:iter.tail]...)
		if !iter.loadMore() {
			iter.reportError("skipArray", "incomplete array")
			return &invalidAny{}
		}
	}
}