package gojay

import (
	"unicode/utf16"
	"unicode/utf8"
)

func (dec *Decoder) getUnicode() (rune, error) {
	i := 0
	r := rune(0)
	for ; (dec.cursor < dec.length || dec.read()) && i < 4; dec.cursor++ {
		c := dec.data[dec.cursor]
		if c >= '0' && c <= '9' {
			r = r*16 + rune(c-'0')
		} else if c >= 'a' && c <= 'f' {
			r = r*16 + rune(c-'a'+10)
		} else if c >= 'A' && c <= 'F' {
			r = r*16 + rune(c-'A'+10)
		} else {
			return 0, InvalidJSONError("Invalid unicode code point")
		}
		i++
	}
	return r, nil
}

func (dec *Decoder) appendEscapeChar(str []byte, c byte) ([]byte, error) {
	switch c {
	case 't':
		str = append(str, '\t')
	case 'n':
		str = append(str, '\n')
	case 'r':
		str = append(str, '\r')
	case 'b':
		str = append(str, '\b')
	case 'f':
		str = append(str, '\f')
	case '\\':
		str = append(str, '\\')
	default:
		return nil, InvalidJSONError("Invalid JSON")
	}
	return str, nil
}

func (dec *Decoder) parseUnicode() ([]byte, error) {
	// get unicode after u
	r, err := dec.getUnicode()
	if err != nil {
		return nil, err
	}
	// no error start making new string
	str := make([]byte, 16, 16)
	i := 0
	// check if code can be a surrogate utf16
	if utf16.IsSurrogate(r) {
		if dec.cursor >= dec.length && !dec.read() {
			return nil, dec.raiseInvalidJSONErr(dec.cursor)
		}
		c := dec.data[dec.cursor]
		if c != '\\' {
			i += utf8.EncodeRune(str, r)
			return str[:i], nil
		}
		dec.cursor++
		if dec.cursor >= dec.length && !dec.read() {
			return nil, dec.raiseInvalidJSONErr(dec.cursor)
		}
		c = dec.data[dec.cursor]
		if c != 'u' {
			i += utf8.EncodeRune(str, r)
			str, err = dec.appendEscapeChar(str[:i], c)
			if err != nil {
				dec.err = err
				return nil, err
			}
			i++
			dec.cursor++
			return str[:i], nil
		}
		dec.cursor++
		r2, err := dec.getUnicode()
		if err != nil {
			return nil, err
		}
		combined := utf16.DecodeRune(r, r2)
		if combined == '\uFFFD' {
			i += utf8.EncodeRune(str, r)
			i += utf8.EncodeRune(str, r2)
		} else {
			i += utf8.EncodeRune(str, combined)
		}
		return str[:i], nil
	}
	i += utf8.EncodeRune(str, r)
	return str[:i], nil
}