package goja

import (
	
	
	
	
	
	
	
	

	
)

const hexUpper = "0123456789ABCDEF"

var (
	parseFloatRegexp = regexp.MustCompile(`^([+-]?(?:Infinity|[0-9]*\.?[0-9]*(?:[eE][+-]?[0-9]+)?))`)
)

func ( *Runtime) ( FunctionCall) Value {
	if math.IsNaN(.Argument(0).ToFloat()) {
		return valueTrue
	} else {
		return valueFalse
	}
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).toString().toTrimmedUTF8()
	 := int(toInt32(.Argument(1)))
	,  := parseInt(, )
	return 
}

func ( *Runtime) ( FunctionCall) Value {
	 := parseFloatRegexp.FindStringSubmatch(.Argument(0).toString().toTrimmedUTF8())
	if len() == 2 {
		if  := [1];  != "" &&  != "+" &&  != "-" {
			switch  {
			case "+", "-":
			case "Infinity", "+Infinity":
				return _positiveInf
			case "-Infinity":
				return _negativeInf
			default:
				,  := strconv.ParseFloat(, 64)
				if  == nil || isRangeErr() {
					return floatToValue()
				}
			}
		}
	}
	return _NaN
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).ToFloat()
	if math.IsNaN() || math.IsInf(, 0) {
		return valueFalse
	}
	return valueTrue
}

func ( *Runtime) ( String,  *[256]bool) String {
	 := .Reader()
	 := make([]byte, utf8.UTFMax)
	 := false
	 := 0
	for {
		, ,  := .ReadRune()
		if  != nil {
			if  != io.EOF {
				panic(.newError(.getURIError(), "Malformed URI"))
			}
			break
		}

		if  >= utf8.RuneSelf {
			 = true
			 += utf8.EncodeRune(, ) * 3
		} else if ![] {
			 = true
			 += 3
		} else {
			++
		}
	}

	if ! {
		return 
	}

	 := make([]byte, )
	 := 0
	 = .Reader()
	for {
		, ,  := .ReadRune()
		if  == io.EOF {
			break
		}

		if  >= utf8.RuneSelf {
			 := utf8.EncodeRune(, )
			for ,  := range [:] {
				[] = '%'
				[+1] = hexUpper[>>4]
				[+2] = hexUpper[&15]
				 += 3
			}
		} else if ![] {
			[] = '%'
			[+1] = hexUpper[>>4]
			[+2] = hexUpper[&15]
			 += 3
		} else {
			[] = byte()
			++
		}
	}
	return asciiString()
}

func ( *Runtime) ( String,  *[256]bool) String {
	 := .String()
	 := 0
	for  := 0;  < len(); {
		switch [] {
		case '%':
			if +2 >= len() || !ishex([+1]) || !ishex([+2]) {
				panic(.newError(.getURIError(), "Malformed URI"))
			}
			 := unhex([+1])<<4 | unhex([+2])
			if ![] {
				++
			}
			 += 3
		default:
			++
		}
	}

	if  == 0 {
		return 
	}

	 := make([]byte, len()-*2)
	 := 0
	 := false
	for  := 0;  < len(); {
		 := []
		switch  {
		case '%':
			 := unhex([+1])<<4 | unhex([+2])
			if [] {
				[] = []
				[+1] = [+1]
				[+2] = [+2]
				 += 3
			} else {
				[] = 
				if  >= utf8.RuneSelf {
					 = true
				}
				++
			}
			 += 3
		default:
			if  >= utf8.RuneSelf {
				 = true
			}
			[] = 
			++
			++
		}
	}

	if ! {
		return asciiString()
	}

	 := make([]rune, 0, len())
	for len() > 0 {
		,  := utf8.DecodeRune()
		if  == utf8.RuneError {
			if  != 3 || [0] != 0xef || [1] != 0xbf || [2] != 0xbd {
				panic(.newError(.getURIError(), "Malformed URI"))
			}
		}
		 = append(, )
		 = [:]
	}
	return unicodeStringFromRunes()
}

func ishex( byte) bool {
	switch {
	case '0' <=  &&  <= '9':
		return true
	case 'a' <=  &&  <= 'f':
		return true
	case 'A' <=  &&  <= 'F':
		return true
	}
	return false
}

func unhex( byte) byte {
	switch {
	case '0' <=  &&  <= '9':
		return  - '0'
	case 'a' <=  &&  <= 'f':
		return  - 'a' + 10
	case 'A' <=  &&  <= 'F':
		return  - 'A' + 10
	}
	return 0
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).toString()
	return ._decode(, &uriReservedHash)
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).toString()
	return ._decode(, &emptyEscapeSet)
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).toString()
	return ._encode(, &uriReservedUnescapedHash)
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).toString()
	return ._encode(, &uriUnescaped)
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).toString()
	var  strings.Builder
	 := .Length()
	for  := 0;  < ; ++ {
		 := .CharAt()
		if  >= 'A' &&  <= 'Z' ||  >= 'a' &&  <= 'z' ||  >= '0' &&  <= '9' ||
			 == '@' ||  == '*' ||  == '_' ||  == '+' ||  == '-' ||  == '.' ||  == '/' {
			.WriteByte(byte())
		} else if  <= 0xff {
			.WriteByte('%')
			.WriteByte(hexUpper[>>4])
			.WriteByte(hexUpper[&0xf])
		} else {
			.WriteString("%u")
			.WriteByte(hexUpper[>>12])
			.WriteByte(hexUpper[(>>8)&0xf])
			.WriteByte(hexUpper[(>>4)&0xf])
			.WriteByte(hexUpper[&0xf])
		}
	}
	return asciiString(.String())
}

func ( *Runtime) ( FunctionCall) Value {
	 := .Argument(0).toString()
	 := .Length()
	var  []byte
	var  []uint16
	,  := devirtualizeString()
	 :=  != nil
	if  {
		 = make([]uint16, 1, +1)
		[0] = unistring.BOM
	} else {
		 = make([]byte, 0, )
	}
	for  := 0;  < ; {
		 := .CharAt()
		if  == '%' {
			if  <= -6 && .CharAt(+1) == 'u' {
				 := .CharAt( + 2)
				 := .CharAt( + 3)
				 := .CharAt( + 4)
				 := .CharAt( + 5)
				if  <= 0xff && ishex(byte()) &&
					 <= 0xff && ishex(byte()) &&
					 <= 0xff && ishex(byte()) &&
					 <= 0xff && ishex(byte()) {
					 = uint16(unhex(byte()))<<12 |
						uint16(unhex(byte()))<<8 |
						uint16(unhex(byte()))<<4 |
						uint16(unhex(byte()))
					 += 5
					goto 
				}
			}
			if  <= -3 {
				 := .CharAt( + 1)
				 := .CharAt( + 2)
				if  <= 0xff && ishex(byte()) &&
					 <= 0xff && ishex(byte()) {
					 = uint16(unhex(byte())<<4 | unhex(byte()))
					 += 2
				}
			}
		}
	:
		if  >= utf8.RuneSelf && ! {
			 = make([]uint16, 1, +1)
			[0] = unistring.BOM
			for ,  := range  {
				 = append(, uint16())
			}
			 = nil
			 = true
		}
		if  {
			 = append(, )
		} else {
			 = append(, byte())
		}
		++
	}
	if  {
		return unicodeString()
	}

	return asciiString()
}

func createGlobalObjectTemplate() *objectTemplate {
	 := newObjectTemplate()
	.protoFactory = func( *Runtime) *Object {
		return .global.ObjectPrototype
	}

	.putStr("Object", func( *Runtime) Value { return valueProp(.getObject(), true, false, true) })
	.putStr("Function", func( *Runtime) Value { return valueProp(.getFunction(), true, false, true) })
	.putStr("Array", func( *Runtime) Value { return valueProp(.getArray(), true, false, true) })
	.putStr("String", func( *Runtime) Value { return valueProp(.getString(), true, false, true) })
	.putStr("Number", func( *Runtime) Value { return valueProp(.getNumber(), true, false, true) })
	.putStr("BigInt", func( *Runtime) Value { return valueProp(.getBigInt(), true, false, true) })
	.putStr("RegExp", func( *Runtime) Value { return valueProp(.getRegExp(), true, false, true) })
	.putStr("Date", func( *Runtime) Value { return valueProp(.getDate(), true, false, true) })
	.putStr("Boolean", func( *Runtime) Value { return valueProp(.getBoolean(), true, false, true) })
	.putStr("Proxy", func( *Runtime) Value { return valueProp(.getProxy(), true, false, true) })
	.putStr("Reflect", func( *Runtime) Value { return valueProp(.getReflect(), true, false, true) })
	.putStr("Error", func( *Runtime) Value { return valueProp(.getError(), true, false, true) })
	.putStr("AggregateError", func( *Runtime) Value { return valueProp(.getAggregateError(), true, false, true) })
	.putStr("TypeError", func( *Runtime) Value { return valueProp(.getTypeError(), true, false, true) })
	.putStr("ReferenceError", func( *Runtime) Value { return valueProp(.getReferenceError(), true, false, true) })
	.putStr("SyntaxError", func( *Runtime) Value { return valueProp(.getSyntaxError(), true, false, true) })
	.putStr("RangeError", func( *Runtime) Value { return valueProp(.getRangeError(), true, false, true) })
	.putStr("EvalError", func( *Runtime) Value { return valueProp(.getEvalError(), true, false, true) })
	.putStr("URIError", func( *Runtime) Value { return valueProp(.getURIError(), true, false, true) })
	.putStr("GoError", func( *Runtime) Value { return valueProp(.getGoError(), true, false, true) })

	.putStr("eval", func( *Runtime) Value { return valueProp(.getEval(), true, false, true) })

	.putStr("Math", func( *Runtime) Value { return valueProp(.getMath(), true, false, true) })
	.putStr("JSON", func( *Runtime) Value { return valueProp(.getJSON(), true, false, true) })
	addTypedArrays()
	.putStr("Symbol", func( *Runtime) Value { return valueProp(.getSymbol(), true, false, true) })
	.putStr("WeakSet", func( *Runtime) Value { return valueProp(.getWeakSet(), true, false, true) })
	.putStr("WeakMap", func( *Runtime) Value { return valueProp(.getWeakMap(), true, false, true) })
	.putStr("Map", func( *Runtime) Value { return valueProp(.getMap(), true, false, true) })
	.putStr("Set", func( *Runtime) Value { return valueProp(.getSet(), true, false, true) })
	.putStr("Promise", func( *Runtime) Value { return valueProp(.getPromise(), true, false, true) })

	.putStr("globalThis", func( *Runtime) Value { return valueProp(.globalObject, true, false, true) })
	.putStr("NaN", func( *Runtime) Value { return valueProp(_NaN, false, false, false) })
	.putStr("undefined", func( *Runtime) Value { return valueProp(_undefined, false, false, false) })
	.putStr("Infinity", func( *Runtime) Value { return valueProp(_positiveInf, false, false, false) })

	.putStr("isNaN", func( *Runtime) Value { return .methodProp(.builtin_isNaN, "isNaN", 1) })
	.putStr("parseInt", func( *Runtime) Value { return valueProp(.getParseInt(), true, false, true) })
	.putStr("parseFloat", func( *Runtime) Value { return valueProp(.getParseFloat(), true, false, true) })
	.putStr("isFinite", func( *Runtime) Value { return .methodProp(.builtin_isFinite, "isFinite", 1) })
	.putStr("decodeURI", func( *Runtime) Value { return .methodProp(.builtin_decodeURI, "decodeURI", 1) })
	.putStr("decodeURIComponent", func( *Runtime) Value { return .methodProp(.builtin_decodeURIComponent, "decodeURIComponent", 1) })
	.putStr("encodeURI", func( *Runtime) Value { return .methodProp(.builtin_encodeURI, "encodeURI", 1) })
	.putStr("encodeURIComponent", func( *Runtime) Value { return .methodProp(.builtin_encodeURIComponent, "encodeURIComponent", 1) })
	.putStr("escape", func( *Runtime) Value { return .methodProp(.builtin_escape, "escape", 1) })
	.putStr("unescape", func( *Runtime) Value { return .methodProp(.builtin_unescape, "unescape", 1) })

	// TODO: Annex B

	.putSym(SymToStringTag, func( *Runtime) Value { return valueProp(asciiString(classGlobal), false, false, true) })

	return 
}

var globalObjectTemplate *objectTemplate
var globalObjectTemplateOnce sync.Once

func getGlobalObjectTemplate() *objectTemplate {
	globalObjectTemplateOnce.Do(func() {
		globalObjectTemplate = createGlobalObjectTemplate()
	})
	return globalObjectTemplate
}

func ( *Runtime) () *Object {
	 := .global.Eval
	if  == nil {
		 = .newNativeFunc(.builtin_eval, "eval", 1)
		.global.Eval = 
	}
	return 
}

func digitVal( byte) int {
	var  byte
	switch {
	case '0' <=  &&  <= '9':
		 =  - '0'
	case 'a' <=  &&  <= 'z':
		 =  - 'a' + 10
	case 'A' <=  &&  <= 'Z':
		 =  - 'A' + 10
	default:
		return 36
	}
	return int()
}

// ECMAScript compatible version of strconv.ParseInt
func parseInt( string,  int) (Value, error) {
	var  int64
	var  error
	var ,  int64
	var  bool
	 := 0

	if len() < 1 {
		 = strconv.ErrSyntax
		goto 
	}

	switch [0] {
	case '-':
		 = true
		 = [1:]
	case '+':
		 = [1:]
	}

	if len() < 1 {
		 = strconv.ErrSyntax
		goto 
	}

	// Look for hex prefix.
	if [0] == '0' && len() > 1 && ([1] == 'x' || [1] == 'X') {
		if  == 0 ||  == 16 {
			 = 16
			 = [2:]
		}
	}

	switch {
	case len() < 1:
		 = strconv.ErrSyntax
		goto 

	case 2 <=  &&  <= 36:
	// valid base; nothing to do

	case  == 0:
		// Look for hex prefix.
		switch {
		case [0] == '0' && len() > 1 && ([1] == 'x' || [1] == 'X'):
			if len() < 3 {
				 = strconv.ErrSyntax
				goto 
			}
			 = 16
			 = [2:]
		default:
			 = 10
		}

	default:
		 = errors.New("invalid base " + strconv.Itoa())
		goto 
	}

	// Cutoff is the smallest number such that cutoff*base > maxInt64.
	// Use compile-time constants for common cases.
	switch  {
	case 10:
		 = math.MaxInt64/10 + 1
	case 16:
		 = math.MaxInt64/16 + 1
	default:
		 = math.MaxInt64/int64() + 1
	}

	 = math.MaxInt64
	for ;  < len(); ++ {
		if  >=  {
			// n*base overflows
			return parseLargeInt(float64(), [:], , )
		}
		 := digitVal([])
		if  >=  {
			break
		}
		 *= int64()

		 :=  + int64()
		if  <  ||  >  {
			// n+v overflows
			return parseLargeInt(float64()+float64(), [+1:], , )
		}
		 = 
	}

	if  == 0 {
		 = strconv.ErrSyntax
		goto 
	}

	if  {
		 = -
	}
	return intToValue(), nil

:
	return _NaN, 
}

func parseLargeInt( float64,  string,  int,  bool) (Value, error) {
	 := 0
	 := float64()
	for ;  < len(); ++ {
		 := digitVal([])
		if  >=  {
			break
		}
		 = * + float64()
	}
	if  {
		 = -
	}
	// We know it can't be represented as int, so use valueFloat instead of floatToValue
	return valueFloat(), nil
}

var (
	uriUnescaped             [256]bool
	uriReserved              [256]bool
	uriReservedHash          [256]bool
	uriReservedUnescapedHash [256]bool
	emptyEscapeSet           [256]bool
)

func init() {
	for ,  := range "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()" {
		uriUnescaped[] = true
	}

	for ,  := range ";/?:@&=+$," {
		uriReserved[] = true
	}

	for  := 0;  < 256; ++ {
		if uriUnescaped[] || uriReserved[] {
			uriReservedUnescapedHash[] = true
		}
		uriReservedHash[] = uriReserved[]
	}
	uriReservedUnescapedHash['#'] = true
	uriReservedHash['#'] = true
}