package goja

import (
	
	
	
	

	
)

const (
	__proto__ = "__proto__"
)

var (
	stringTrue        String = asciiString("true")
	stringFalse       String = asciiString("false")
	stringNull        String = asciiString("null")
	stringUndefined   String = asciiString("undefined")
	stringObjectC     String = asciiString("object")
	stringFunction    String = asciiString("function")
	stringBoolean     String = asciiString("boolean")
	stringString      String = asciiString("string")
	stringSymbol      String = asciiString("symbol")
	stringNumber      String = asciiString("number")
	stringBigInt      String = asciiString("bigint")
	stringNaN         String = asciiString("NaN")
	stringInfinity           = asciiString("Infinity")
	stringNegInfinity        = asciiString("-Infinity")
	stringBound_      String = asciiString("bound ")
	stringEmpty       String = asciiString("")

	stringError          String = asciiString("Error")
	stringAggregateError String = asciiString("AggregateError")
	stringTypeError      String = asciiString("TypeError")
	stringReferenceError String = asciiString("ReferenceError")
	stringSyntaxError    String = asciiString("SyntaxError")
	stringRangeError     String = asciiString("RangeError")
	stringEvalError      String = asciiString("EvalError")
	stringURIError       String = asciiString("URIError")
	stringGoError        String = asciiString("GoError")

	stringObjectNull      String = asciiString("[object Null]")
	stringObjectUndefined String = asciiString("[object Undefined]")
	stringInvalidDate     String = asciiString("Invalid Date")
)

type utf16Reader interface {
	readChar() (c uint16, err error)
}

// String represents an ECMAScript string Value. Its internal representation depends on the contents of the
// string, but in any case it is capable of holding any UTF-16 string, either valid or invalid.
// Instances of this type, as any other primitive values, are goroutine-safe and can be passed between runtimes.
// Strings can be created using Runtime.ToValue(goString) or StringFromUTF16.
type String interface {
	Value
	CharAt(int) uint16
	Length() int
	Concat(String) String
	Substring(start, end int) String
	CompareTo(String) int
	Reader() io.RuneReader
	utf16Reader() utf16Reader
	utf16RuneReader() io.RuneReader
	utf16Runes() []rune
	index(String, int) int
	lastIndex(String, int) int
	toLower() String
	toUpper() String
	toTrimmedUTF8() string
}

type stringIterObject struct {
	baseObject
	reader io.RuneReader
}

func isUTF16FirstSurrogate( uint16) bool {
	return  >= 0xD800 &&  <= 0xDBFF
}

func isUTF16SecondSurrogate( uint16) bool {
	return  >= 0xDC00 &&  <= 0xDFFF
}

func ( *stringIterObject) () Value {
	if .reader == nil {
		return .val.runtime.createIterResultObject(_undefined, true)
	}
	, ,  := .reader.ReadRune()
	if  == io.EOF {
		.reader = nil
		return .val.runtime.createIterResultObject(_undefined, true)
	}
	return .val.runtime.createIterResultObject(stringFromRune(), false)
}

func stringFromRune( rune) String {
	if  < utf8.RuneSelf {
		var  strings.Builder
		.WriteByte(byte())
		return asciiString(.String())
	}
	var  unicodeStringBuilder
	.WriteRune()
	return .String()
}

func ( *Runtime) ( String) Value {
	 := &Object{runtime: }

	 := &stringIterObject{
		reader: &lenientUtf16Decoder{utf16Reader: .utf16Reader()},
	}
	.class = classObject
	.val = 
	.extensible = true
	.self = 
	.prototype = .getStringIteratorPrototype()
	.init()

	return 
}

type stringObject struct {
	baseObject
	value      String
	length     int
	lengthProp valueProperty
}

func newStringValue( string) String {
	if  := unistring.Scan();  != nil {
		return unicodeString()
	}
	return asciiString()
}

func stringValueFromRaw( unistring.String) String {
	if  := .AsUtf16();  != nil {
		return unicodeString()
	}
	return asciiString()
}

func ( *stringObject) () {
	.baseObject.init()
	.setLength()
}

func ( *stringObject) () {
	if .value != nil {
		.length = .value.Length()
	}
	.lengthProp.value = intToValue(int64(.length))
	._put("length", &.lengthProp)
}

func ( *stringObject) ( unistring.String,  Value) Value {
	if  := strToGoIdx();  >= 0 &&  < .length {
		return ._getIdx()
	}
	return .baseObject.getStr(, )
}

func ( *stringObject) ( valueInt,  Value) Value {
	 := int()
	if  >= 0 &&  < .length {
		return ._getIdx()
	}
	return .baseObject.getStr(.string(), )
}

func ( *stringObject) ( unistring.String) Value {
	if  := strToGoIdx();  >= 0 &&  < .length {
		 := ._getIdx()
		return &valueProperty{
			value:      ,
			enumerable: true,
		}
	}

	return .baseObject.getOwnPropStr()
}

func ( *stringObject) ( valueInt) Value {
	 := int64()
	if  >= 0 {
		if  < int64(.length) {
			 := ._getIdx(int())
			return &valueProperty{
				value:      ,
				enumerable: true,
			}
		}
		return nil
	}

	return .baseObject.getOwnPropStr(.string())
}

func ( *stringObject) ( int) Value {
	return .value.Substring(, +1)
}

func ( *stringObject) ( unistring.String,  Value,  bool) bool {
	if  := strToGoIdx();  >= 0 &&  < .length {
		.val.runtime.typeErrorResult(, "Cannot assign to read only property '%d' of a String", )
		return false
	}

	return .baseObject.setOwnStr(, , )
}

func ( *stringObject) ( valueInt,  Value,  bool) bool {
	 := int64()
	if  >= 0 &&  < int64(.length) {
		.val.runtime.typeErrorResult(, "Cannot assign to read only property '%d' of a String", )
		return false
	}

	return .baseObject.setOwnStr(.string(), , )
}

func ( *stringObject) ( unistring.String, ,  Value,  bool) (bool, bool) {
	return ._setForeignStr(, .getOwnPropStr(), , , )
}

func ( *stringObject) ( valueInt, ,  Value,  bool) (bool, bool) {
	return ._setForeignIdx(, .getOwnPropIdx(), , , )
}

func ( *stringObject) ( unistring.String,  PropertyDescriptor,  bool) bool {
	if  := strToGoIdx();  >= 0 &&  < .length {
		,  := ._defineOwnProperty(, &valueProperty{enumerable: true}, , )
		return 
	}

	return .baseObject.defineOwnPropertyStr(, , )
}

func ( *stringObject) ( valueInt,  PropertyDescriptor,  bool) bool {
	 := int64()
	if  >= 0 &&  < int64(.length) {
		.val.runtime.typeErrorResult(, "Cannot redefine property: %d", )
		return false
	}

	return .baseObject.defineOwnPropertyStr(.string(), , )
}

type stringPropIter struct {
	str         String // separate, because obj can be the singleton
	obj         *stringObject
	idx, length int
}

func ( *stringPropIter) () (propIterItem, iterNextFunc) {
	if .idx < .length {
		 := strconv.Itoa(.idx)
		.idx++
		return propIterItem{name: asciiString(), enumerable: _ENUM_TRUE}, .
	}

	return .obj.baseObject.iterateStringKeys()()
}

func ( *stringObject) () iterNextFunc {
	return (&stringPropIter{
		str:    .value,
		obj:    ,
		length: .length,
	}).next
}

func ( *stringObject) ( bool,  []Value) []Value {
	for  := 0;  < .length; ++ {
		 = append(, asciiString(strconv.Itoa()))
	}

	return .baseObject.stringKeys(, )
}

func ( *stringObject) ( unistring.String,  bool) bool {
	if  := strToGoIdx();  >= 0 &&  < .length {
		.val.runtime.typeErrorResult(, "Cannot delete property '%d' of a String", )
		return false
	}

	return .baseObject.deleteStr(, )
}

func ( *stringObject) ( valueInt,  bool) bool {
	 := int64()
	if  >= 0 &&  < int64(.length) {
		.val.runtime.typeErrorResult(, "Cannot delete property '%d' of a String", )
		return false
	}

	return .baseObject.deleteStr(.string(), )
}

func ( *stringObject) ( unistring.String) bool {
	if  := strToGoIdx();  >= 0 &&  < .length {
		return true
	}
	return .baseObject.hasOwnPropertyStr()
}

func ( *stringObject) ( valueInt) bool {
	 := int64()
	if  >= 0 &&  < int64(.length) {
		return true
	}
	return .baseObject.hasOwnPropertyStr(.string())
}

func devirtualizeString( String) (asciiString, unicodeString) {
	switch s := .(type) {
	case asciiString:
		return , nil
	case unicodeString:
		return "", 
	case *importedString:
		.ensureScanned()
		if .u != nil {
			return "", .u
		}
		return asciiString(.s), nil
	default:
		panic(unknownStringTypeErr())
	}
}

func unknownStringTypeErr( Value) interface{} {
	return newTypeError("Internal bug: unknown string type: %T", )
}

// StringFromUTF16 creates a string value from an array of UTF-16 code units. The result is a copy, so the initial
// slice can be modified after calling this function (but it must not be modified while the function is running).
// No validation of any kind is performed.
func ( []uint16) String {
	 := true
	for ,  := range  {
		if  >= utf8.RuneSelf {
			 = false
			break
		}
	}
	if  {
		var  strings.Builder
		.Grow(len())
		for ,  := range  {
			.WriteByte(byte())
		}
		return asciiString(.String())
	}
	 := make([]uint16, len()+1)
	[0] = unistring.BOM
	copy([1:], )
	return unicodeString()
}