package goja

import (
	
	
	
	
	
	
	
	
)

type regexp2MatchCache struct {
	target String
	runes  []rune
	posMap []int
}

// Not goroutine-safe. Use regexp2Wrapper.clone()
type regexp2Wrapper struct {
	rx    *regexp2.Regexp
	cache *regexp2MatchCache
}

type regexpWrapper regexp.Regexp

type positionMapItem struct {
	src, dst int
}
type positionMap []positionMapItem

func ( positionMap) ( int) int {
	if  <= 0 {
		return 
	}
	 := sort.Search(len(), func( int) bool { return [].src >=  })
	if  >= len() || [].src !=  {
		panic("index not found")
	}
	return [].dst
}

type arrayRuneReader struct {
	runes []rune
	pos   int
}

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

// Not goroutine-safe. Use regexpPattern.clone()
type regexpPattern struct {
	src string

	global, ignoreCase, multiline, dotAll, sticky, unicode bool

	regexpWrapper  *regexpWrapper
	regexp2Wrapper *regexp2Wrapper
}

func compileRegexp2( string, , , ,  bool) (*regexp2Wrapper, error) {
	var  regexp2.RegexOptions = regexp2.ECMAScript
	if  {
		 |= regexp2.Multiline
	}
	if  {
		 |= regexp2.Singleline
	}
	if  {
		 |= regexp2.IgnoreCase
	}
	if  {
		 |= regexp2.Unicode
	}
	,  := regexp2.Compile(, )
	if  != nil {
		return nil, fmt.Errorf("Invalid regular expression (regexp2): %s (%v)", , )
	}

	return &regexp2Wrapper{rx: }, nil
}

func ( *regexpPattern) () {
	if .regexp2Wrapper != nil {
		return
	}
	,  := compileRegexp2(.src, .multiline, .dotAll, .ignoreCase, .unicode)
	if  != nil {
		// At this point the regexp should have been successfully converted to re2, if it fails now, it's a bug.
		panic()
	}
	.regexp2Wrapper = 
}

func buildUTF8PosMap( unicodeString) (positionMap, string) {
	 := make(positionMap, 0, .Length())
	 := .Reader()
	,  := 0, 0
	var  strings.Builder
	for {
		, ,  := .ReadRune()
		if  == io.EOF {
			break
		}
		if  != nil {
			// the string contains invalid UTF-16, bailing out
			return nil, ""
		}
		,  := .WriteRune()
		 += 
		 += 
		 = append(, positionMapItem{src: , dst: })
	}
	return , .String()
}

func ( *regexpPattern) ( String,  int) []int {
	if .regexpWrapper == nil {
		return .regexp2Wrapper.findSubmatchIndex(, , .unicode, .global || .sticky)
	}
	if  != 0 {
		// Unfortunately Go's regexp library does not allow starting from an arbitrary position.
		// If we just drop the first _start_ characters of the string the assertions (^, $, \b and \B) will not
		// work correctly.
		.createRegexp2()
		return .regexp2Wrapper.findSubmatchIndex(, , .unicode, .global || .sticky)
	}
	return .regexpWrapper.findSubmatchIndex(, .unicode)
}

func ( *regexpPattern) ( String,  int,  int,  bool) [][]int {
	if .regexpWrapper == nil {
		return .regexp2Wrapper.findAllSubmatchIndex(, , , , .unicode)
	}
	if  == 0 {
		,  := devirtualizeString()
		if  == nil {
			return .regexpWrapper.findAllSubmatchIndex(string(), , )
		}
		if  == 1 {
			 := .regexpWrapper.findSubmatchIndexUnicode(, .unicode)
			if  == nil {
				return nil
			}
			return [][]int{}
		}
		// Unfortunately Go's regexp library lacks FindAllReaderSubmatchIndex(), so we have to use a UTF-8 string as an
		// input.
		if .unicode {
			// Try to convert s to UTF-8. If it does not contain any invalid UTF-16 we can do the matching in UTF-8.
			,  := buildUTF8PosMap()
			if  != nil {
				 := .regexpWrapper.findAllSubmatchIndex(, , )
				for ,  := range  {
					for ,  := range  {
						[] = .get()
					}
				}
				return 
			}
		}
	}

	.createRegexp2()
	return .regexp2Wrapper.findAllSubmatchIndex(, , , , .unicode)
}

// clone creates a copy of the regexpPattern which can be used concurrently.
func ( *regexpPattern) () *regexpPattern {
	 := &regexpPattern{
		src:        .src,
		global:     .global,
		ignoreCase: .ignoreCase,
		multiline:  .multiline,
		dotAll:     .dotAll,
		sticky:     .sticky,
		unicode:    .unicode,
	}
	if .regexpWrapper != nil {
		.regexpWrapper = .regexpWrapper.clone()
	}
	if .regexp2Wrapper != nil {
		.regexp2Wrapper = .regexp2Wrapper.clone()
	}
	return 
}

type regexpObject struct {
	baseObject
	pattern *regexpPattern
	source  String

	standard bool
}

func ( *regexp2Wrapper) ( String,  int, ,  bool) ( []int) {
	if  {
		return .findSubmatchIndexUnicode(, , )
	}
	return .findSubmatchIndexUTF16(, , )
}

func ( *regexp2Wrapper) ( String,  int,  bool) ( *regexp2.Match,  []rune,  error) {
	 := .rx
	 := .cache
	if  != nil && .posMap == nil && .target.SameAs() {
		 = .runes
	} else {
		 = .utf16Runes()
		 = nil
	}
	,  = .FindRunesMatchStartingAt(, )
	if  &&  != nil &&  == nil {
		if  == nil {
			if .cache == nil {
				.cache = new(regexp2MatchCache)
			}
			*.cache = regexp2MatchCache{
				target: ,
				runes:  ,
			}
		}
	} else {
		.cache = nil
	}
	return
}

func ( *regexp2Wrapper) ( String,  int,  bool) ( []int) {
	, ,  := .findUTF16Cached(, , )
	if  != nil {
		return
	}

	if  == nil {
		return
	}
	 := .Groups()

	 = make([]int, 0, len()<<1)
	for ,  := range  {
		if len(.Captures) > 0 {
			 = append(, .Index, .Index+.Length)
		} else {
			 = append(, -1, 0)
		}
	}
	return
}

func ( *regexp2Wrapper) ( String,  int,  bool) ( *regexp2.Match,  []int,  error) {
	var (
		       []rune
		 int
		   bool
		   rune
	)
	 := .rx
	 := .cache
	if  != nil && .posMap != nil && .target.SameAs() {
		,  = .runes, .posMap
		,  = posMapReverseLookup(, )
	} else {
		, , ,  = buildPosMap(&lenientUtf16Decoder{utf16Reader: .utf16Reader()}, .Length(), )
		 = nil
	}
	if  {
		// temporarily set the rune at mappedStart to the second code point of the pair
		,  := utf16.EncodeRune([])
		, [] = [], 
	}
	,  = .FindRunesMatchStartingAt(, )
	if  &&  != nil &&  == nil {
		if  {
			[] = 
		}
		if  == nil {
			if .cache == nil {
				.cache = new(regexp2MatchCache)
			}
			*.cache = regexp2MatchCache{
				target: ,
				runes:  ,
				posMap: ,
			}
		}
	} else {
		.cache = nil
	}

	return
}

func ( *regexp2Wrapper) ( String,  int,  bool) ( []int) {
	, ,  := .findUnicodeCached(, , )
	if  == nil ||  != nil {
		return
	}

	 := .Groups()

	 = make([]int, 0, len()<<1)
	for ,  := range  {
		if len(.Captures) > 0 {
			 = append(, [.Index], [.Index+.Length])
		} else {
			 = append(, -1, 0)
		}
	}
	return
}

func ( *regexp2Wrapper) ( String, ,  int,  bool) [][]int {
	 := .rx
	, ,  := .findUTF16Cached(, , false)
	if  == nil ||  != nil {
		return nil
	}
	if  < 0 {
		 = len() + 1
	}
	 := make([][]int, 0, )
	for  != nil {
		 := .Groups()

		 := make([]int, 0, len()<<1)

		for ,  := range  {
			if len(.Captures) > 0 {
				 := .Index
				 := .Index + .Length
				 = append(, , )
			} else {
				 = append(, -1, 0)
			}
		}

		if  && len() > 1 {
			if [0] !=  {
				break
			}
			 = [1]
		}

		 = append(, )
		--
		if  <= 0 {
			break
		}
		,  = .FindNextMatch()
		if  != nil {
			return nil
		}
	}
	return 
}

func buildPosMap( io.RuneReader, ,  int) ( []int,  []rune,  int,  bool) {
	 = make([]int, 0, +1)
	 := 0
	 = make([]rune, 0, )
	 := false
	for {
		if ! {
			if  ==  {
				 = len()
				 = true
			}
			if  >  {
				// start position splits a surrogate pair
				 = len() - 1
				 = true
				 = true
			}
		}
		, ,  := .ReadRune()
		if  != nil {
			break
		}
		 = append(, )
		 = append(, )
		 += 
	}
	 = append(, )
	return
}

func posMapReverseLookup( []int,  int) (int, bool) {
	 := sort.SearchInts(, )
	if  < len() && [] !=  {
		return  - 1, true
	}
	return , false
}

func ( *regexp2Wrapper) ( unicodeString, ,  int,  bool) [][]int {
	 := .rx
	if  < 0 {
		 = len() + 1
	}
	 := make([][]int, 0, )
	, ,  := .findUnicodeCached(, , false)
	if  != nil {
		return nil
	}
	for  != nil {
		 := .Groups()

		 := make([]int, 0, len()<<1)

		for ,  := range  {
			if len(.Captures) > 0 {
				 := [.Index]
				 := [.Index+.Length]
				 = append(, , )
			} else {
				 = append(, -1, 0)
			}
		}

		if  && len() > 1 {
			if [0] !=  {
				break
			}
			 = [1]
		}

		 = append(, )
		,  = .FindNextMatch()
		if  != nil {
			return nil
		}
	}
	return 
}

func ( *regexp2Wrapper) ( String, ,  int, ,  bool) [][]int {
	,  := devirtualizeString()
	if  != nil {
		if  {
			return .findAllSubmatchIndexUnicode(, , , )
		}
		return .findAllSubmatchIndexUTF16(, , , )
	}
	return .findAllSubmatchIndexUTF16(, , , )
}

func ( *regexp2Wrapper) () *regexp2Wrapper {
	return &regexp2Wrapper{
		rx: .rx,
	}
}

func ( *regexpWrapper) ( string,  int,  bool) ( [][]int) {
	 := (*regexp.Regexp)()
	 = .FindAllStringSubmatchIndex(, )
	 := 0
	if  {
		for ,  := range  {
			if len() > 1 {
				if [0] !=  {
					return [:]
				}
				 = [1]
			}
		}
	}
	return
}

func ( *regexpWrapper) ( String,  bool) []int {
	,  := devirtualizeString()
	if  != nil {
		return .findSubmatchIndexUnicode(, )
	}
	return .findSubmatchIndexASCII(string())
}

func ( *regexpWrapper) ( string) []int {
	 := (*regexp.Regexp)()
	return .FindStringSubmatchIndex()
}

func ( *regexpWrapper) ( unicodeString,  bool) ( []int) {
	 := (*regexp.Regexp)()
	if  {
		, , ,  := buildPosMap(&lenientUtf16Decoder{utf16Reader: .utf16Reader()}, .Length(), 0)
		 := .FindReaderSubmatchIndex(&arrayRuneReader{runes: })
		for ,  := range  {
			if  >= 0 {
				[] = []
			}
		}
		return 
	}
	return .FindReaderSubmatchIndex(.utf16RuneReader())
}

func ( *regexpWrapper) () *regexpWrapper {
	return 
}

func ( *regexpObject) ( String,  []int) Value {
	 := len() >> 1
	 := make([]Value, )
	 := [0]
	[0] = .Substring([0], [1])
	 := 0
	for  := 1;  < ; ++ {
		 :=  << 1
		if [] >= 0 && [+1] >=  {
			[] = .Substring([], [+1])
			 = []
		} else {
			[] = _undefined
		}
	}
	 := .val.runtime.newArrayValues()
	.self.setOwnStr("input", , false)
	.self.setOwnStr("index", intToValue(int64()), false)
	return 
}

func ( *regexpObject) () int64 {
	 := toLength(.getStr("lastIndex", nil))
	if !.pattern.global && !.pattern.sticky {
		return 0
	}
	return 
}

func ( *regexpObject) ( int64, ,  []int) bool {
	if .pattern.sticky {
		if  == nil || int64([0]) !=  {
			.setOwnStr("lastIndex", intToValue(0), true)
			return false
		}
	} else {
		if  == nil {
			if .pattern.global {
				.setOwnStr("lastIndex", intToValue(0), true)
			}
			return false
		}
	}

	if .pattern.global || .pattern.sticky {
		.setOwnStr("lastIndex", intToValue(int64([1])), true)
	}
	return true
}

func ( *regexpObject) ( String) ( bool,  []int) {
	 := .getLastIndex()
	if  >= 0 &&  <= int64(.Length()) {
		 = .pattern.findSubmatchIndex(, int())
	}
	 = .updateLastIndex(, , )
	return
}

func ( *regexpObject) ( String) Value {
	,  := .execRegexp()
	if  {
		return .execResultToArray(, )
	}
	return _null
}

func ( *regexpObject) ( String) bool {
	,  := .execRegexp()
	return 
}

func ( *regexpObject) () *regexpObject {
	 := .val.runtime.newRegexpObject(.prototype)
	.source = .source
	.pattern = .pattern

	return 
}

func ( *regexpObject) () {
	.baseObject.init()
	.standard = true
	._putProp("lastIndex", intToValue(0), true, false, false)
}

func ( *regexpObject) ( *Object,  bool) bool {
	 := .baseObject.setProto(, )
	if  {
		.standard = false
	}
	return 
}

func ( *regexpObject) ( unistring.String,  PropertyDescriptor,  bool) bool {
	 := .baseObject.defineOwnPropertyStr(, , )
	if  {
		.standard = false
	}
	return 
}

func ( *regexpObject) ( *Symbol,  PropertyDescriptor,  bool) bool {
	 := .baseObject.defineOwnPropertySym(, , )
	if  && .standard {
		switch  {
		case SymMatch, SymMatchAll, SymSearch, SymSplit, SymReplace:
			.standard = false
		}
	}
	return 
}

func ( *regexpObject) ( unistring.String,  bool) bool {
	 := .baseObject.deleteStr(, )
	if  {
		.standard = false
	}
	return 
}

func ( *regexpObject) ( unistring.String,  Value,  bool) bool {
	 := .baseObject.setOwnStr(, , )
	if  && .standard &&  == "exec" {
		.standard = false
	}
	return 
}

func ( *regexpObject) ( *Symbol,  Value,  bool) bool {
	 := .baseObject.setOwnSym(, , )
	if  && .standard {
		switch  {
		case SymMatch, SymMatchAll, SymSearch, SymSplit, SymReplace:
			.standard = false
		}
	}
	return 
}