package jsoniter

import (
	"fmt"
	"strings"
)

// ReadObject read one field from object.
// If object ended, returns empty string.
// Otherwise, returns the field name.
func (iter *Iterator) ReadObject() (ret string) {
	c := iter.nextToken()
	switch c {
	case 'n':
		iter.skipThreeBytes('u', 'l', 'l')
		return "" // null
	case '{':
		c = iter.nextToken()
		if c == '"' {
			iter.unreadByte()
			field := iter.ReadString()
			c = iter.nextToken()
			if c != ':' {
				iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
			}
			return field
		}
		if c == '}' {
			return "" // end of object
		}
		iter.ReportError("ReadObject", `expect " after {, but found `+string([]byte{c}))
		return
	case ',':
		field := iter.ReadString()
		c = iter.nextToken()
		if c != ':' {
			iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
		}
		return field
	case '}':
		return "" // end of object
	default:
		iter.ReportError("ReadObject", fmt.Sprintf(`expect { or , or } or n, but found %s`, string([]byte{c})))
		return
	}
}

// CaseInsensitive
func (iter *Iterator) readFieldHash() int64 {
	hash := int64(0x811c9dc5)
	c := iter.nextToken()
	if c != '"' {
		iter.ReportError("readFieldHash", `expect ", but found `+string([]byte{c}))
		return 0
	}
	for {
		for i := iter.head; i < iter.tail; i++ {
			// require ascii string and no escape
			b := iter.buf[i]
			if b == '\\' {
				iter.head = i
				for _, b := range iter.readStringSlowPath() {
					if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
						b += 'a' - 'A'
					}
					hash ^= int64(b)
					hash *= 0x1000193
				}
				c = iter.nextToken()
				if c != ':' {
					iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
					return 0
				}
				return hash
			}
			if b == '"' {
				iter.head = i + 1
				c = iter.nextToken()
				if c != ':' {
					iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c}))
					return 0
				}
				return hash
			}
			if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
				b += 'a' - 'A'
			}
			hash ^= int64(b)
			hash *= 0x1000193
		}
		if !iter.loadMore() {
			iter.ReportError("readFieldHash", `incomplete field name`)
			return 0
		}
	}
}

func calcHash(str string, caseSensitive bool) int64 {
	if !caseSensitive {
		str = strings.ToLower(str)
	}
	hash := int64(0x811c9dc5)
	for _, b := range []byte(str) {
		hash ^= int64(b)
		hash *= 0x1000193
	}
	return int64(hash)
}

// ReadObjectCB read object with callback, the key is ascii only and field name not copied
func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
	c := iter.nextToken()
	var field string
	if c == '{' {
		if !iter.incrementDepth() {
			return false
		}
		c = iter.nextToken()
		if c == '"' {
			iter.unreadByte()
			field = iter.ReadString()
			c = iter.nextToken()
			if c != ':' {
				iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
			}
			if !callback(iter, field) {
				iter.decrementDepth()
				return false
			}
			c = iter.nextToken()
			for c == ',' {
				field = iter.ReadString()
				c = iter.nextToken()
				if c != ':' {
					iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
				}
				if !callback(iter, field) {
					iter.decrementDepth()
					return false
				}
				c = iter.nextToken()
			}
			if c != '}' {
				iter.ReportError("ReadObjectCB", `object not ended with }`)
				iter.decrementDepth()
				return false
			}
			return iter.decrementDepth()
		}
		if c == '}' {
			return iter.decrementDepth()
		}
		iter.ReportError("ReadObjectCB", `expect " after }, but found `+string([]byte{c}))
		iter.decrementDepth()
		return false
	}
	if c == 'n' {
		iter.skipThreeBytes('u', 'l', 'l')
		return true // null
	}
	iter.ReportError("ReadObjectCB", `expect { or n, but found `+string([]byte{c}))
	return false
}

// ReadMapCB read map with callback, the key can be any string
func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
	c := iter.nextToken()
	if c == '{' {
		if !iter.incrementDepth() {
			return false
		}
		c = iter.nextToken()
		if c == '"' {
			iter.unreadByte()
			field := iter.ReadString()
			if iter.nextToken() != ':' {
				iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
				iter.decrementDepth()
				return false
			}
			if !callback(iter, field) {
				iter.decrementDepth()
				return false
			}
			c = iter.nextToken()
			for c == ',' {
				field = iter.ReadString()
				if iter.nextToken() != ':' {
					iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
					iter.decrementDepth()
					return false
				}
				if !callback(iter, field) {
					iter.decrementDepth()
					return false
				}
				c = iter.nextToken()
			}
			if c != '}' {
				iter.ReportError("ReadMapCB", `object not ended with }`)
				iter.decrementDepth()
				return false
			}
			return iter.decrementDepth()
		}
		if c == '}' {
			return iter.decrementDepth()
		}
		iter.ReportError("ReadMapCB", `expect " after }, but found `+string([]byte{c}))
		iter.decrementDepth()
		return false
	}
	if c == 'n' {
		iter.skipThreeBytes('u', 'l', 'l')
		return true // null
	}
	iter.ReportError("ReadMapCB", `expect { or n, but found `+string([]byte{c}))
	return false
}

func (iter *Iterator) readObjectStart() bool {
	c := iter.nextToken()
	if c == '{' {
		c = iter.nextToken()
		if c == '}' {
			return false
		}
		iter.unreadByte()
		return true
	} else if c == 'n' {
		iter.skipThreeBytes('u', 'l', 'l')
		return false
	}
	iter.ReportError("readObjectStart", "expect { or n, but found "+string([]byte{c}))
	return false
}

func (iter *Iterator) readObjectFieldAsBytes() (ret []byte) {
	str := iter.ReadStringAsSlice()
	if iter.skipWhitespacesWithoutLoadMore() {
		if ret == nil {
			ret = make([]byte, len(str))
			copy(ret, str)
		}
		if !iter.loadMore() {
			return
		}
	}
	if iter.buf[iter.head] != ':' {
		iter.ReportError("readObjectFieldAsBytes", "expect : after object field, but found "+string([]byte{iter.buf[iter.head]}))
		return
	}
	iter.head++
	if iter.skipWhitespacesWithoutLoadMore() {
		if ret == nil {
			ret = make([]byte, len(str))
			copy(ret, str)
		}
		if !iter.loadMore() {
			return
		}
	}
	if ret == nil {
		return str
	}
	return ret
}
