package meg

import (
	
	
	
	
)

// Pattern is a curried MatchState. Given the slice of current MatchStates and a
// handle (int index) to the next MatchState, it returns a (possibly modified)
// slice of next MatchStates and handle to the inserted MatchState.
type Pattern = func(states []MatchState, nextIdx int) ([]MatchState, int)

// Matcher holds a graph of match state nodes. Use PatternToMatcher to create.
type Matcher struct {
	states   []MatchState
	startIdx int
}

func ( Matcher) () string {
	 := make([]string, len(.states))
	for ,  := range .states {
		[] = .String() + "@" + strconv.Itoa()
	}
	return fmt.Sprintf("RootMatchState{states: [%s], startIdx: %d}", strings.Join(, ", "), .startIdx)
}

func ( ...Pattern) Matcher {
	// Preallocate a slice to hold the MatchStates.
	// Avoids small allocations for each pattern.
	// The number is chosen experimentally. It is subject to change.
	 := make([]MatchState, 0, len()*3)
	// Append the done state.
	 = append(, MatchState{codeOrKind: done})
	 := len() - 1
	// Build the chain by composing patterns from right to left.
	for  := len() - 1;  >= 0; -- {
		,  = [](, )
	}
	return Matcher{states: , startIdx: }
}

func ( ...Pattern) Pattern {
	switch len() {
	case 0:
		return func( []MatchState,  int) ([]MatchState, int) {
			return , 
		}
	case 1:
		return [0]
	case 2:
		return func( []MatchState,  int) ([]MatchState, int) {
			 := [0]
			 := [1]
			// First run the right pattern, then feed the result into left.
			,  = (, )
			return (, )
		}
	default:
		return (
			([:len()-1]...),
			[len()-1],
		)
	}
}

func ( ...Pattern) Pattern {
	return func( []MatchState,  int) ([]MatchState, int) {
		if len() == 0 {
			return , 
		}
		// Evaluate the last pattern and use its result as the initial accumulator.
		,  := [len()-1](, )
		// Iterate backwards from the second-to-last pattern to the first.
		for  := len() - 2;  >= 0; -- {
			var  int
			,  = [](, )
			 := MatchState{
				next:       ,
				codeOrKind: encodeSplitIdx(),
			}
			 = append(, )
			 = len() - 1
		}
		return , 
	}
}

var errAlreadyCapture = errors.New("already captured")

func captureOneBytesOrErr( *[]byte) CaptureFunc {
	if  == nil {
		return nil
	}
	var  bool
	 := func( Matchable) error {
		if  {
			* = nil
			return errAlreadyCapture
		}
		* = .RawValue()
		return nil
	}
	return 
}

func captureOneStringValueOrErr( *string) CaptureFunc {
	if  == nil {
		return nil
	}
	var  bool
	 := func( Matchable) error {
		if  {
			* = ""
			return errAlreadyCapture
		}
		* = .Value()
		return nil
	}
	return 
}

func captureManyBytes( *[][]byte) CaptureFunc {
	if  == nil {
		return nil
	}
	 := func( Matchable) error {
		* = append(*, .RawValue())
		return nil
	}
	return 
}

func captureManyStrings( *[]string) CaptureFunc {
	if  == nil {
		return nil
	}
	 := func( Matchable) error {
		* = append(*, .Value())
		return nil
	}
	return 
}

func ( int,  CaptureFunc) Pattern {
	return func( []MatchState,  int) ([]MatchState, int) {
		 := MatchState{
			capture:    ,
			codeOrKind: ,
			next:       ,
		}
		 = append(, )
		return , len() - 1
	}
}

func ( int) Pattern {
	return CaptureString(, nil)
}

// Any is a special code that matches any value.
var Any int = matchAny

func ( int,  *string) Pattern {
	return CaptureWithF(, captureOneStringValueOrErr())
}

func ( int,  *[]byte) Pattern {
	return CaptureWithF(, captureOneBytesOrErr())
}

func ( int) Pattern {
	return CaptureZeroOrMoreStrings(, nil)
}

func ( int,  CaptureFunc) Pattern {
	return func( []MatchState,  int) ([]MatchState, int) {
		// Create the match state.
		 := MatchState{
			codeOrKind: ,
			capture:    ,
		}
		 = append(, )
		 := len() - 1

		// Create the split state that branches to the match state and to the next state.
		 := MatchState{
			next:       ,
			codeOrKind: encodeSplitIdx(),
		}
		 = append(, )
		 := len() - 1

		// Close the loop: update the match state's next field.
		[].next = 

		return , 
	}
}

func ( int,  *[][]byte) Pattern {
	return CaptureZeroOrMoreWithF(, captureManyBytes())
}

func ( int,  *[]string) Pattern {
	return CaptureZeroOrMoreWithF(, captureManyStrings())
}

func ( int) Pattern {
	return CaptureOneOrMoreStrings(, nil)
}

func ( int,  *[]string) Pattern {
	 := captureManyStrings()
	return func( []MatchState,  int) ([]MatchState, int) {
		// First attach the zero-or-more loop.
		,  := CaptureZeroOrMoreWithF(, )(, )
		// Then put the capture state before the loop.
		return CaptureWithF(, )(, )
	}
}

func ( int,  *[][]byte) Pattern {
	 := captureManyBytes()
	return func( []MatchState,  int) ([]MatchState, int) {
		// First attach the zero-or-more loop.
		,  := CaptureZeroOrMoreWithF(, )(, )
		// Then put the capture state before the loop.
		return CaptureWithF(, )(, )
	}
}

func ( Pattern) Pattern {
	return func( []MatchState,  int) ([]MatchState, int) {
		,  := (, )
		 := MatchState{
			next:       ,
			codeOrKind: encodeSplitIdx(),
		}
		 = append(, )
		return , len() - 1
	}
}