package regexp2

import (
	
	

	
)

const (
	replaceSpecials     = 4
	replaceLeftPortion  = -1
	replaceRightPortion = -2
	replaceLastGroup    = -3
	replaceWholeString  = -4
)

// MatchEvaluator is a function that takes a match and returns a replacement string to be used
type MatchEvaluator func(Match) string

// Three very similar algorithms appear below: replace (pattern),
// replace (evaluator), and split.

func writeRunes( *bytes.Buffer,  []rune, ,  int) {
	for  := ;  < ; ++ {
		.WriteRune([])
	}
}

func compactBalancedMatches( *Match) {
	for  := 0;  < len(.matchcount); ++ {
		 := .matchcount[] * 2
		 := .matches[]

		var ,  int
		for  = 0;  < ; ++ {
			if [] < 0 {
				break
			}
		}

		for  = ;  < ; ++ {
			if [] < 0 {
				--
			} else {
				if  !=  {
					[] = []
				}
				++
			}
		}

		.matchcount[] =  / 2
	}
	.balancing = false
}

// Replace Replaces all occurrences of the regex in the string with the
// replacement pattern.
//
// Note that the special case of no matches is handled on its own:
// with no matches, the input string is returned unchanged.
// The right-to-left case is split out because StringBuilder
// doesn't handle right-to-left string building directly very well.
func replace( *Regexp,  *syntax.ReplacerData,  MatchEvaluator,  string, ,  int) (string, error) {
	if  < -1 {
		return "", errors.New("count too small")
	}
	if  == 0 {
		return "", nil
	}

	if  == nil {
		if !.RightToLeft() {
			return replaceRunnerLTR(, , , , )
		}
		return replaceRunnerRTL(, , , , )
	}

	,  := .FindStringMatchStartingAt(, )

	if  != nil {
		return "", 
	}
	if  == nil {
		return , nil
	}

	 := &bytes.Buffer{}
	 := .text.runes

	if !.RightToLeft() {
		 := 0
		for  != nil {
			if .RuneIndex !=  {
				.WriteString(string([:.RuneIndex]))
			}
			 = .RuneIndex + .RuneLength
			.WriteString((*))

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

		if  < len() {
			.WriteString(string([:]))
		}
	} else {
		 := len()
		var  []string

		for  != nil {
			if .RuneIndex+.RuneLength !=  {
				 = append(, string([.RuneIndex+.RuneLength:]))
			}
			 = .RuneIndex
			 = append(, (*))

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

		if  > 0 {
			.WriteString(string([:]))
		}

		for  := len() - 1;  >= 0; -- {
			.WriteString([])
		}
	}

	return .String(), nil
}

func replaceRunnerLTR( *Regexp,  *syntax.ReplacerData,  string, ,  int) (string, error) {
	if  > len() {
		return "", errors.New("startAt must be less than the length of the input string")
	}

	 := .getRunner()
	, ,  := .decodeStringWithStart(, )
	 := newStringMatchText(, )
	defer func() {
		.putRunner()
		if  != nil {
			pooledRuneBuffers.put()
		}
	}()
	if  >= 0 &&  < 0 {
		return "", errors.New("startAt must align to the start of a valid rune in the input string")
	}
	if  < 0 {
		 = 0
	}

	,  := .scan(, , , -1, true, .MatchTimeout)
	if  != nil {
		return "", 
	}
	if  == nil {
		return , nil
	}

	,  := getPooledReplaceBuffer(len(), .optimizations.MaxCachedReplaceBufferLength)
	if  != nil {
		defer putPooledReplaceBuffer(, )
	}

	 := 0
	for  != nil {
		if .balancing {
			compactBalancedMatches()
		}

		if .RuneIndex !=  {
			writeRunes(, , , .RuneIndex)
		}
		 = .RuneIndex + .RuneLength
		replacementImpl(, , )

		--
		if  == 0 {
			break
		}

		,  = .scan(, , .textpos, .RuneLength, true, .MatchTimeout)
		if  != nil {
			return "", 
		}
	}

	if  < len() {
		writeRunes(, , , len())
	}
	return .String(), nil
}

func replaceRunnerRTL( *Regexp,  *syntax.ReplacerData,  string, ,  int) (string, error) {
	if  > len() {
		return "", errors.New("startAt must be less than the length of the input string")
	}

	 := .getRunner()
	, ,  := .decodeStringWithStart(, )
	 := newStringMatchText(, )
	defer func() {
		.putRunner()
		if  != nil {
			pooledRuneBuffers.put()
		}
	}()
	if  >= 0 &&  < 0 {
		return "", errors.New("startAt must align to the start of a valid rune in the input string")
	}
	if  < 0 {
		 = len()
	}

	,  := .scan(, , , -1, true, .MatchTimeout)
	if  != nil {
		return "", 
	}
	if  == nil {
		return , nil
	}

	,  := getPooledReplaceBuffer(len(), .optimizations.MaxCachedReplaceBufferLength)
	if  != nil {
		defer putPooledReplaceBuffer(, )
	}

	 := len()
	var  []string

	for  != nil {
		if .balancing {
			compactBalancedMatches()
		}

		if .RuneIndex+.RuneLength !=  {
			 = append(, string([.RuneIndex+.RuneLength:]))
		}
		 = .RuneIndex
		replacementImplRTL(, &, )

		--
		if  == 0 {
			break
		}

		,  = .scan(, , .textpos, .RuneLength, true, .MatchTimeout)
		if  != nil {
			return "", 
		}
	}

	if  > 0 {
		writeRunes(, , 0, )
	}
	for  := len() - 1;  >= 0; -- {
		.WriteString([])
	}
	return .String(), nil
}

// Given a Match, emits into the StringBuilder the evaluated
// substitution pattern.
func replacementImpl( *syntax.ReplacerData,  *bytes.Buffer,  *Match) {
	for ,  := range .Rules {

		if  >= 0 { // string lookup
			.WriteString(.Strings[])
		} else if  < -replaceSpecials { // group lookup
			.groupValueAppendToBuf(-replaceSpecials-1-, )
		} else {
			switch -replaceSpecials - 1 -  { // special insertion patterns
			case replaceLeftPortion:
				for  := 0;  < .RuneIndex; ++ {
					.WriteRune(.text.runes[])
				}
			case replaceRightPortion:
				for  := .RuneIndex + .RuneLength;  < len(.text.runes); ++ {
					.WriteRune(.text.runes[])
				}
			case replaceLastGroup:
				.groupValueAppendToBuf(.GroupCount()-1, )
			case replaceWholeString:
				for  := 0;  < len(.text.runes); ++ {
					.WriteRune(.text.runes[])
				}
			}
		}
	}
}

func replacementImplRTL( *syntax.ReplacerData,  *[]string,  *Match) {
	 := *
	 := &bytes.Buffer{}

	for ,  := range .Rules {
		.Reset()
		if  >= 0 { // string lookup
			 = append(, .Strings[])
		} else if  < -replaceSpecials { // group lookup
			.groupValueAppendToBuf(-replaceSpecials-1-, )
			 = append(, .String())
		} else {
			switch -replaceSpecials - 1 -  { // special insertion patterns
			case replaceLeftPortion:
				for  := 0;  < .RuneIndex; ++ {
					.WriteRune(.text.runes[])
				}
			case replaceRightPortion:
				for  := .RuneIndex + .RuneLength;  < len(.text.runes); ++ {
					.WriteRune(.text.runes[])
				}
			case replaceLastGroup:
				.groupValueAppendToBuf(.GroupCount()-1, )
			case replaceWholeString:
				for  := 0;  < len(.text.runes); ++ {
					.WriteRune(.text.runes[])
				}
			}
			 = append(, .String())
		}
	}

	* = 
}