package regexp2

import (
	
	
	

	
	
)

const maxStringFilterLiteralLen = 8

var (
	errStringStartAtTooLarge        = errors.New("startAt must be less than the length of the input string")
	errStringStartAtNotRuneBoundary = errors.New("startAt must align to the start of a valid rune in the input string")
)

// StringPrefixFilter optionally searches string input before the engine decodes it
// to runes. It returns a byte index for a candidate match start, or ok=false if
// the regex cannot match. The filter must be conservative: false positives are
// allowed, false negatives are not.
type StringPrefixFilter func(input string, startAt int) (candidateByteIndex int, ok bool)

func newStringPrefixFilter( *syntax.Code) StringPrefixFilter {
	if  == nil || .RightToLeft || .FindOptimizations == nil {
		return nil
	}

	 := .FindOptimizations
	 := .MinRequiredLength

	switch .FindMode {
	case syntax.LeadingString_LeftToRight:
		return stringIndexPrefixFilter(.LeadingPrefix, false, )
	case syntax.LeadingString_OrdinalIgnoreCase_LeftToRight:
		return stringIndexPrefixFilter(.LeadingPrefix, true, )
	case syntax.LeadingStrings_LeftToRight:
		return stringIndexPrefixesFilter(.LeadingPrefixes, false, )
	case syntax.LeadingStrings_OrdinalIgnoreCase_LeftToRight:
		return stringIndexPrefixesFilter(.LeadingPrefixes, true, )
	case syntax.LeadingSet_LeftToRight:
		if len(.FixedDistanceSets) == 0 {
			return nil
		}
		 := .FixedDistanceSets[0]
		if .Range == nil && (len(.Chars) == 0 || len(.Chars) > 5) {
			return nil
		}
		return stringFixedDistanceSetFilter(, )
	case syntax.FixedDistanceChar_LeftToRight:
		return stringFixedDistanceCharFilter(.FixedDistanceLiteral.C, .FixedDistanceLiteral.Distance, )
	case syntax.FixedDistanceString_LeftToRight:
		return stringFixedDistanceStringFilter(.FixedDistanceLiteral.S, .FixedDistanceLiteral.Distance, )
	case syntax.LiteralAfterLoop_LeftToRight:
		return stringLiteralAfterLoopFilter(.LiteralAfterLoop, )
	default:
		return nil
	}
}

type asciiSetStringScanner struct {
	chars    string
	first    byte
	last     byte
	useRange bool
	distance int
}

func newASCIISetStringScanner( syntax.FixedDistanceSet) (asciiSetStringScanner, bool) {
	if .Negated || .Distance < 0 {
		return asciiSetStringScanner{}, false
	}
	if .Range != nil {
		if .Range.First < 0 || .Range.Last > utf8.RuneSelf-1 {
			return asciiSetStringScanner{}, false
		}
		return asciiSetStringScanner{
			first:    byte(.Range.First),
			last:     byte(.Range.Last),
			useRange: true,
			distance: .Distance,
		}, true
	}
	if len(.Chars) == 0 {
		return asciiSetStringScanner{}, false
	}
	 := make([]byte, len(.Chars))
	for ,  := range .Chars {
		if  < 0 ||  > utf8.RuneSelf-1 {
			return asciiSetStringScanner{}, false
		}
		[] = byte()
	}
	return asciiSetStringScanner{chars: string(), distance: .Distance}, true
}

func stringFixedDistanceSetFilter( syntax.FixedDistanceSet,  int) StringPrefixFilter {
	,  := newASCIISetStringScanner()
	if ! {
		return nil
	}

	return func( string,  int) ( int,  bool) {
		if !hasMinRequiredBytes(, , ) {
			return 0, false
		}

		for  := ;  < len(); {
			 := .index([:])
			if  < 0 {
				return 0, false
			}
			 :=  + 
			,  := stringFixedDistanceCandidateStart(, , , .distance)
			if  && hasMinRequiredBytes(, , ) {
				return , true
			}
			if  {
				return 0, false
			}
			 =  + 1
		}
		return 0, false
	}
}

func ( asciiSetStringScanner) ( string) int {
	if !.useRange {
		if len(.chars) == 1 {
			return strings.IndexByte(, .chars[0])
		}
		return strings.IndexAny(, .chars)
	}
	for  := 0;  < len(); ++ {
		if [] >= .first && [] <= .last {
			return 
		}
	}
	return -1
}

func stringIndexPrefixFilter( string,  bool,  int) StringPrefixFilter {
	if  == "" {
		return nil
	}
	if  && !isASCIIString() {
		return nil
	}

	return func( string,  int) ( int,  bool) {
		if !hasMinRequiredBytes(, , ) {
			return 0, false
		}

		var  int
		if  {
			 = helpers.IndexStringIgnoreCaseASCII([:], )
		} else {
			 = strings.Index([:], )
		}
		if  < 0 {
			return 0, false
		}
		return  + , true
	}
}

func stringIndexPrefixesFilter( []string,  bool,  int) StringPrefixFilter {
	if len() == 0 {
		return nil
	}
	if  {
		for ,  := range  {
			if !isASCIIString() {
				return nil
			}
		}
	}

	if ,  := compileASCIIStringSetPrefixFilter(, , );  {
		return .index
	}

	return func( string,  int) ( int,  bool) {
		return indexAnyPrefixFallback(, , , , )
	}
}

func indexAnyPrefixFallback( string,  int,  []string,  bool,  int) ( int,  bool) {
	if !hasMinRequiredBytes(, , ) {
		return 0, false
	}

	 := -1
	 := [:]
	for ,  := range  {
		var  int
		if  {
			 = helpers.IndexStringIgnoreCaseASCII(, )
		} else {
			 = strings.Index(, )
		}
		if  >= 0 && ( < 0 ||  < ) {
			 = 
		}
	}
	if  < 0 {
		return 0, false
	}
	return  + , true
}

type asciiStringSetPrefixFilter struct {
	firstChars       string
	prefixesByFirst  [256][]string
	minRequiredBytes int
}

// compileASCIIStringSetPrefixFilter builds a byte-oriented multi-prefix scanner
// for the narrow shape where it beats running strings.Index once per prefix:
// case-sensitive ASCII prefixes with at least two prefixes sharing a first byte.
// It indexes possible first bytes with strings.IndexAny, then verifies only the
// bucket for the byte found. Other shapes fall back to the old implementation.
func compileASCIIStringSetPrefixFilter( []string,  bool,  int) (*asciiStringSetPrefixFilter, bool) {
	if  {
		return nil, false
	}

	 := &asciiStringSetPrefixFilter{
		minRequiredBytes: ,
	}
	var  [256]bool
	var  bool
	for ,  := range  {
		if  == "" || !isASCIIString() {
			return nil, false
		}

		 := [0]
		.prefixesByFirst[] = append(.prefixesByFirst[], )
		if len(.prefixesByFirst[]) > 1 {
			 = true
		}
		[] = true
	}

	if ! {
		return nil, false
	}

	 := make([]byte, 0, len()*2)
	for ,  := range  {
		if  {
			 = append(, byte())
		}
	}
	if len() == 0 {
		return nil, false
	}
	.firstChars = string()
	return , true
}

func ( *asciiStringSetPrefixFilter) ( string,  int) ( int,  bool) {
	if !hasMinRequiredBytes(, , .minRequiredBytes) {
		return 0, false
	}

	for  := ;  < len(); {
		 := strings.IndexAny([:], .firstChars)
		if  < 0 {
			return 0, false
		}
		 :=  + 
		 := []
		for ,  := range .prefixesByFirst[] {
			if len()- >= len() && strings.HasPrefix([:], ) {
				return , true
			}
		}
		 =  + 1
	}
	return 0, false
}

func stringFixedDistanceCharFilter( rune, ,  int) StringPrefixFilter {
	if  < 0 {
		return nil
	}

	return func( string,  int) ( int,  bool) {
		if !hasMinRequiredBytes(, , ) {
			return 0, false
		}

		 := 
		for {
			 := strings.IndexRune([:], )
			if  < 0 {
				return 0, false
			}
			 :=  + 
			,  := stringFixedDistanceCandidateStart(, , , )
			if  && hasMinRequiredBytes(, , ) {
				return , true
			}
			if  {
				return 0, false
			}
			,  := utf8.DecodeRuneInString([:])
			if  == 0 {
				return 0, false
			}
			 =  + 
		}
	}
}

func stringFixedDistanceStringFilter( string, ,  int) StringPrefixFilter {
	if  == "" ||  < 0 || len() > maxStringFilterLiteralLen {
		return nil
	}

	return func( string,  int) ( int,  bool) {
		if !hasMinRequiredBytes(, , ) {
			return 0, false
		}

		 := 
		for  <= len()-len() {
			 := strings.Index([:], )
			if  < 0 {
				return 0, false
			}
			 :=  + 
			,  := stringFixedDistanceCandidateStart(, , , )
			if  && hasMinRequiredBytes(, , ) {
				return , true
			}
			if  {
				return 0, false
			}
			 =  + 1
		}
		return 0, false
	}
}

func stringLiteralAfterLoopFilter( *syntax.LiteralAfterLoop,  int) StringPrefixFilter {
	if  == nil || .LoopNode == nil || .LoopNode.Set == nil {
		return nil
	}
	if .StringIgnoreCase && (.String == "" || !isASCIIString(.String)) {
		return nil
	}

	return func( string,  int) ( int,  bool) {
		if !hasMinRequiredBytes(, , ) {
			return 0, false
		}
		if !stringHasLiteralAfterLoop(, , ) {
			return 0, false
		}
		return , true
	}
}

func stringHasLiteralAfterLoop( string,  int,  *syntax.LiteralAfterLoop) bool {
	switch {
	case .String != "":
		if .StringIgnoreCase {
			return helpers.IndexStringIgnoreCaseASCII([:], .String) >= 0
		}
		return strings.Contains([:], .String)
	case len(.Chars) > 0:
		 := string(.Chars)
		return strings.ContainsAny([:], )
	default:
		return strings.ContainsRune([:], .Char)
	}
}

func stringFixedDistanceCandidateStart( string, , ,  int) (int, bool) {
	 := 
	for  := 0;  < ; ++ {
		if  <=  {
			return 0, false
		}
		,  := utf8.DecodeLastRuneInString([:])
		if  == 0 {
			return 0, false
		}
		 -= 
	}
	return , true
}

func ( *Regexp) ( string,  int) ( int,  bool) {
	if .stringPrefixFilter == nil || .RightToLeft() {
		return , true
	}
	,  = .stringPrefixFilter(, )
	if ! {
		return 0, false
	}
	if  <  ||  > len() || !isStringRuneBoundary(, ) {
		return , true
	}
	return , true
}

func ( *Regexp) ( string,  int) ( int,  bool,  error) {
	if  > len() {
		return 0, false, errStringStartAtTooLarge
	}
	if  >= 0 && !isStringRuneBoundary(, ) {
		return 0, false, errStringStartAtNotRuneBoundary
	}

	if  < 0 {
		if .RightToLeft() {
			 = len()
		} else {
			 = 0
		}
	}

	,  = .findStringPrefixCandidate(, )
	return , , nil
}

func hasMinRequiredBytes( string, ,  int) bool {
	if  < 0 ||  > len() {
		return false
	}
	return  <= 0 || len()- >= 
}

func isStringRuneBoundary( string,  int) bool {
	if  == 0 ||  == len() {
		return true
	}
	if  < 0 ||  > len() {
		return false
	}
	for  := range  {
		if  ==  {
			return true
		}
		if  >  {
			return false
		}
	}
	return false
}

func isASCIIString( string) bool {
	for  := 0;  < len(); ++ {
		if [] >= utf8.RuneSelf {
			return false
		}
	}
	return true
}