package goja

import (
	
	
	
	
	
	
	
	

	
	
	
	
)

type unicodeString []uint16

type unicodeRuneReader struct {
	s   unicodeString
	pos int
}

type utf16RuneReader struct {
	s   unicodeString
	pos int
}

// passes through invalid surrogate pairs
type lenientUtf16Decoder struct {
	utf16Reader utf16Reader
	prev        uint16
	prevSet     bool
}

// StringBuilder serves similar purpose to strings.Builder, except it works with ECMAScript String.
// Use it to efficiently build 'native' ECMAScript values that either contain invalid UTF-16 surrogate pairs
// (and therefore cannot be represented as UTF-8) or never expected to be exported to Go. See also
// StringFromUTF16.
type StringBuilder struct {
	asciiBuilder   strings.Builder
	unicodeBuilder unicodeStringBuilder
}

type unicodeStringBuilder struct {
	buf     []uint16
	unicode bool
}

var (
	InvalidRuneError = errors.New("invalid rune")
)

func ( *utf16RuneReader) () ( uint16,  error) {
	if .pos < len(.s) {
		 = .s[.pos]
		.pos++
		return
	}
	 = io.EOF
	return
}

func ( *utf16RuneReader) () ( rune,  int,  error) {
	if .pos < len(.s) {
		 = rune(.s[.pos])
		.pos++
		 = 1
		return
	}
	 = io.EOF
	return
}

func ( *lenientUtf16Decoder) () ( rune,  int,  error) {
	var  uint16
	if .prevSet {
		 = .prev
		.prevSet = false
	} else {
		,  = .utf16Reader.readChar()
		if  != nil {
			return
		}
	}
	 = 1
	if isUTF16FirstSurrogate() {
		,  := .utf16Reader.readChar()
		if  != nil {
			if  != io.EOF {
				 = 
			} else {
				 = rune()
			}
			return
		}
		if isUTF16SecondSurrogate() {
			 = utf16.DecodeRune(rune(), rune())
			++
			return
		} else {
			.prev = 
			.prevSet = true
		}
	}
	 = rune()
	return
}

func ( *unicodeRuneReader) () ( rune,  int,  error) {
	if .pos < len(.s) {
		 := .s[.pos]
		++
		.pos++
		if isUTF16FirstSurrogate() {
			if .pos < len(.s) {
				 := .s[.pos]
				if isUTF16SecondSurrogate() {
					 = utf16.DecodeRune(rune(), rune())
					++
					.pos++
					return
				}
			}
			 = InvalidRuneError
		} else if isUTF16SecondSurrogate() {
			 = InvalidRuneError
		}
		 = rune()
	} else {
		 = io.EOF
	}
	return
}

func ( *unicodeStringBuilder) ( int) {
	if len(.buf) == 0 {
		++
	}
	if cap(.buf)-len(.buf) <  {
		 := make([]uint16, len(.buf), 2*cap(.buf)+)
		copy(, .buf)
		.buf = 
	}
}

func ( *unicodeStringBuilder) ( int) {
	.Grow()
	if len(.buf) == 0 {
		.buf = append(.buf, unistring.BOM)
	}
}

// assumes already started
func ( *unicodeStringBuilder) ( String) {
	,  := devirtualizeString()
	if  != nil {
		.buf = append(.buf, [1:]...)
		.unicode = true
	} else {
		for  := 0;  < len(); ++ {
			.buf = append(.buf, uint16([]))
		}
	}
}

func ( *unicodeStringBuilder) () String {
	if .unicode {
		return unicodeString(.buf)
	}
	if len(.buf) < 2 {
		return stringEmpty
	}
	 := make([]byte, 0, len(.buf)-1)
	for ,  := range .buf[1:] {
		 = append(, byte())
	}
	return asciiString()
}

func ( *unicodeStringBuilder) ( rune) {
	.ensureStarted(2)
	.writeRuneFast()
}

// assumes already started
func ( *unicodeStringBuilder) ( rune) {
	if  <= 0xFFFF {
		.buf = append(.buf, uint16())
		if !.unicode &&  >= utf8.RuneSelf {
			.unicode = true
		}
	} else {
		,  := utf16.EncodeRune()
		.buf = append(.buf, uint16(), uint16())
		.unicode = true
	}
}

func ( *unicodeStringBuilder) ( string) {
	for ,  := range  {
		.buf = append(.buf, uint16())
	}
}

func ( *unicodeStringBuilder) ( unicodeString) {
	.buf = append(.buf, [1:]...)
	.unicode = true
}

func ( *StringBuilder) () bool {
	return len(.unicodeBuilder.buf) == 0
}

func ( *StringBuilder) ( String) {
	,  := devirtualizeString()
	if  != nil {
		.switchToUnicode(.Length())
		.unicodeBuilder.writeUnicodeString()
	} else {
		if .ascii() {
			.asciiBuilder.WriteString(string())
		} else {
			.unicodeBuilder.writeASCIIString(string())
		}
	}
}

func ( *StringBuilder) ( string) {
	 := 0
	if .ascii() {
		for  := 0;  < len(); ++ {
			if [] >= utf8.RuneSelf {
				.switchToUnicode(len())
				.unicodeBuilder.writeASCIIString([:])
				 = 
				goto 
			}
		}
		.asciiBuilder.WriteString()
		return
	}
:
	for ,  := range [:] {
		.unicodeBuilder.writeRuneFast()
	}
}

func ( *StringBuilder) ( string) {
	if .ascii() {
		.asciiBuilder.WriteString()
	} else {
		.unicodeBuilder.writeASCIIString()
	}
}

func ( *StringBuilder) ( rune) {
	if  < utf8.RuneSelf {
		if .ascii() {
			.asciiBuilder.WriteByte(byte())
		} else {
			.unicodeBuilder.writeRuneFast()
		}
	} else {
		var  int
		if  <= 0xFFFF {
			 = 1
		} else {
			 = 2
		}
		.switchToUnicode()
		.unicodeBuilder.writeRuneFast()
	}
}

func ( *StringBuilder) () String {
	if .ascii() {
		return asciiString(.asciiBuilder.String())
	}
	return .unicodeBuilder.String()
}

func ( *StringBuilder) ( int) {
	if .ascii() {
		.asciiBuilder.Grow()
	} else {
		.unicodeBuilder.Grow()
	}
}

// LikelyUnicode hints to the builder that the resulting string is likely to contain Unicode (non-ASCII) characters.
// The argument is an extra capacity (in characters) to reserve on top of the current length (it's like calling
// Grow() afterwards).
// This method may be called at any point (not just when the buffer is empty), although for efficiency it should
// be called as early as possible.
func ( *StringBuilder) ( int) {
	.switchToUnicode()
}

func ( *StringBuilder) ( int) {
	if .ascii() {
		 := .asciiBuilder.Cap()
		 := .asciiBuilder.Len() + 
		if  <  {
			 = 
		}
		.unicodeBuilder.ensureStarted()
		.unicodeBuilder.writeASCIIString(.asciiBuilder.String())
		.asciiBuilder.Reset()
	}
}

func ( *StringBuilder) ( String,  int,  int) {
	,  := devirtualizeString()
	if  == nil {
		if .ascii() {
			.asciiBuilder.WriteString(string([:]))
		} else {
			.unicodeBuilder.writeASCIIString(string([:]))
		}
		return
	}
	if .ascii() {
		 := false
		for  := ;  < ; ++ {
			if .CharAt() >= utf8.RuneSelf {
				 = true
				break
			}
		}
		if  {
			.switchToUnicode( -  + 1)
		} else {
			.asciiBuilder.Grow( -  + 1)
			for  := ;  < ; ++ {
				.asciiBuilder.WriteByte(byte(.CharAt()))
			}
			return
		}
	}
	.unicodeBuilder.buf = append(.unicodeBuilder.buf, [+1:+1]...)
	.unicodeBuilder.unicode = true
}

func ( unicodeString) () io.RuneReader {
	return &unicodeRuneReader{
		s: [1:],
	}
}

func ( unicodeString) () utf16Reader {
	return &utf16RuneReader{
		s: [1:],
	}
}

func ( unicodeString) () io.RuneReader {
	return &utf16RuneReader{
		s: [1:],
	}
}

func ( unicodeString) () []rune {
	 := make([]rune, len()-1)
	for ,  := range [1:] {
		[] = rune()
	}
	return 
}

func ( unicodeString) () int64 {
	return 0
}

func ( unicodeString) () String {
	return 
}

func ( unicodeString) () Value {
	return 
}

func ( unicodeString) () float64 {
	return math.NaN()
}

func ( unicodeString) () bool {
	return len() > 0
}

func ( unicodeString) () string {
	if len() == 0 {
		return ""
	}
	return strings.Trim(.String(), parser.WhitespaceChars)
}

func ( unicodeString) () Value {
	return asciiString(.toTrimmedUTF8()).ToNumber()
}

func ( unicodeString) ( *Runtime) *Object {
	return ._newString(, .getStringPrototype())
}

func ( unicodeString) ( unicodeString) bool {
	if len() != len() {
		return false
	}
	for ,  := range  {
		if  != [] {
			return false
		}
	}
	return true
}

func ( unicodeString) ( Value) bool {
	return .StrictEquals()
}

func ( unicodeString) ( Value) bool {
	if .StrictEquals() {
		return true
	}

	if ,  := .(*Object);  {
		return .(.toPrimitive())
	}
	return false
}

func ( unicodeString) ( Value) bool {
	if ,  := .(unicodeString);  {
		return .equals()
	}
	if ,  := .(*importedString);  {
		.ensureScanned()
		if .u != nil {
			return .equals(.u)
		}
	}

	return false
}

func ( unicodeString) ( *Runtime) *Object {
	 := .getStringSingleton()
	.value = 
	.setLength()
	return .val
}

func ( unicodeString) ( int) uint16 {
	return [+1]
}

func ( unicodeString) () int {
	return len() - 1
}

func ( unicodeString) ( String) String {
	,  := devirtualizeString()
	if  != nil {
		 := make(unicodeString, len()+len()-1)
		copy(, )
		copy([len():], [1:])
		return 
	}
	 := make([]uint16, len()+len())
	copy(, )
	 := [len():]
	for  := 0;  < len(); ++ {
		[] = uint16([])
	}
	return unicodeString()
}

func ( unicodeString) (,  int) String {
	 := [+1 : +1]
	for ,  := range  {
		if  >= utf8.RuneSelf {
			 := make(unicodeString, -+1)
			[0] = unistring.BOM
			copy([1:], )
			return 
		}
	}
	 := make([]byte, -)
	for ,  := range  {
		[] = byte()
	}
	return asciiString()
}

func ( unicodeString) () string {
	return string(utf16.Decode([1:]))
}

func ( unicodeString) ( String) int {
	// TODO handle invalid UTF-16
	return strings.Compare(.String(), .String())
}

func ( unicodeString) ( String,  int) int {
	var  []uint16
	,  := devirtualizeString()
	if  != nil {
		 = [1:]
	} else {
		 = make([]uint16, len())
		for  := 0;  < len(); ++ {
			[] = uint16([])
		}
	}
	 := [1:]
	// TODO: optimise
	 := len() - len()
	for  <=  {
		for  := 0;  < len(); ++ {
			if [+] != [] {
				goto 
			}
		}

		return 
	:
		++
	}
	return -1
}

func ( unicodeString) ( String,  int) int {
	var  []uint16
	,  := devirtualizeString()
	if  != nil {
		 = [1:]
	} else {
		 = make([]uint16, len())
		for  := 0;  < len(); ++ {
			[] = uint16([])
		}
	}

	 := [1:]
	if  := len() - len();  >  {
		 = 
	}
	// TODO: optimise
	for  >= 0 {
		for  := 0;  < len(); ++ {
			if [+] != [] {
				goto 
			}
		}

		return 
	:
		--
	}
	return -1
}

func unicodeStringFromRunes( []rune) unicodeString {
	return unistring.NewFromRunes().AsUtf16()
}

func toLower( string) String {
	 := cases.Lower(language.Und)
	 := []rune(.String())
	// Workaround
	 := true
	for  := 0;  < len()-1; ++ {
		if ( == 0 || [-1] != 0x3b1) && [] == 0x345 && [+1] == 0x3c2 {
			++
			[] = 0x3c3
		}
		if [] >= utf8.RuneSelf {
			 = false
		}
	}
	if  {
		 = [len()-1] < utf8.RuneSelf
	}
	if  {
		return asciiString()
	}
	return unicodeStringFromRunes()
}

func ( unicodeString) () String {
	return toLower(.String())
}

func ( unicodeString) () String {
	 := cases.Upper(language.Und)
	return newStringValue(.String(.String()))
}

func ( unicodeString) () interface{} {
	return .String()
}

func ( unicodeString) () reflect.Type {
	return reflectTypeString
}

func ( unicodeString) ( *maphash.Hash) uint64 {
	_, _ = .WriteString(string(unistring.FromUtf16()))
	 := .Sum64()
	.Reset()
	return 
}

func ( unicodeString) () unistring.String {
	return unistring.FromUtf16()
}