package httprule

import (
	
	
	
)

// InvalidTemplateError indicates that the path template is not valid.
type InvalidTemplateError struct {
	tmpl string
	msg  string
}

func ( InvalidTemplateError) () string {
	return fmt.Sprintf("%s: %s", .msg, .tmpl)
}

// Parse parses the string representation of path template
func ( string) (Compiler, error) {
	if !strings.HasPrefix(, "/") {
		return template{}, InvalidTemplateError{tmpl: , msg: "no leading /"}
	}
	,  := tokenize([1:])

	 := parser{tokens: }
	,  := .topLevelSegments()
	if  != nil {
		return template{}, InvalidTemplateError{tmpl: , msg: .Error()}
	}

	return template{
		segments: ,
		verb:     ,
		template: ,
	}, nil
}

func tokenize( string) ( []string,  string) {
	if  == "" {
		return []string{eof}, ""
	}

	const (
		 = iota
		
		
	)
	 := 
	for  != "" {
		var  int
		switch  {
		case :
			 = strings.IndexAny(, "/{")
		case :
			 = strings.IndexAny(, ".=}")
		case :
			 = strings.IndexAny(, "/}")
		}
		if  < 0 {
			 = append(, )
			break
		}
		switch  := [];  {
		case '/', '.':
		case '{':
			 = 
		case '=':
			 = 
		case '}':
			 = 
		}
		if  == 0 {
			 = append(, [:+1])
		} else {
			 = append(, [:], [:+1])
		}
		 = [+1:]
	}

	 := len()
	// See
	// https://github.com/grpc-ecosystem/grpc-gateway/pull/1947#issuecomment-774523693 ;
	// although normal and backwards-compat logic here is to use the last index
	// of a colon, if the final segment is a variable followed by a colon, the
	// part following the colon must be a verb. Hence if the previous token is
	// an end var marker, we switch the index we're looking for to Index instead
	// of LastIndex, so that we correctly grab the remaining part of the path as
	// the verb.
	var  bool
	switch  {
	case 0, 1:
		// Not enough to be variable so skip this logic and don't result in an
		// invalid index
	default:
		 = [-2] == "}"
	}
	 := [-1]
	var  int
	if  {
		 = strings.Index(, ":")
	} else {
		 = strings.LastIndex(, ":")
	}
	if  == 0 {
		,  = [:-1], [1:]
	} else if  > 0 {
		[-1],  = [:], [+1:]
	}
	 = append(, eof)
	return , 
}

// parser is a parser of the template syntax defined in github.com/googleapis/googleapis/google/api/http.proto.
type parser struct {
	tokens   []string
	accepted []string
}

// topLevelSegments is the target of this parser.
func ( *parser) () ([]segment, error) {
	if ,  := .accept(typeEOF);  == nil {
		.tokens = .tokens[:0]
		return []segment{literal(eof)}, nil
	}
	,  := .segments()
	if  != nil {
		return nil, 
	}
	if ,  := .accept(typeEOF);  != nil {
		return nil, fmt.Errorf("unexpected token %q after segments %q", .tokens[0], strings.Join(.accepted, ""))
	}
	return , nil
}

func ( *parser) () ([]segment, error) {
	,  := .segment()
	if  != nil {
		return nil, 
	}

	 := []segment{}
	for {
		if ,  := .accept("/");  != nil {
			return , nil
		}
		,  := .segment()
		if  != nil {
			return , 
		}
		 = append(, )
	}
}

func ( *parser) () (segment, error) {
	if ,  := .accept("*");  == nil {
		return wildcard{}, nil
	}
	if ,  := .accept("**");  == nil {
		return deepWildcard{}, nil
	}
	if ,  := .literal();  == nil {
		return , nil
	}

	,  := .variable()
	if  != nil {
		return nil, fmt.Errorf("segment neither wildcards, literal or variable: %w", )
	}
	return , nil
}

func ( *parser) () (segment, error) {
	,  := .accept(typeLiteral)
	if  != nil {
		return nil, 
	}
	return literal(), nil
}

func ( *parser) () (segment, error) {
	if ,  := .accept("{");  != nil {
		return nil, 
	}

	,  := .fieldPath()
	if  != nil {
		return nil, 
	}

	var  []segment
	if ,  := .accept("=");  == nil {
		,  = .segments()
		if  != nil {
			return nil, fmt.Errorf("invalid segment in variable %q: %w", , )
		}
	} else {
		 = []segment{wildcard{}}
	}

	if ,  := .accept("}");  != nil {
		return nil, fmt.Errorf("unterminated variable segment: %s", )
	}
	return variable{
		path:     ,
		segments: ,
	}, nil
}

func ( *parser) () (string, error) {
	,  := .accept(typeIdent)
	if  != nil {
		return "", 
	}
	 := []string{}
	for {
		if ,  := .accept(".");  != nil {
			return strings.Join(, "."), nil
		}
		,  := .accept(typeIdent)
		if  != nil {
			return "", fmt.Errorf("invalid field path component: %w", )
		}
		 = append(, )
	}
}

// A termType is a type of terminal symbols.
type termType string

// These constants define some of valid values of termType.
// They improve readability of parse functions.
//
// You can also use "/", "*", "**", "." or "=" as valid values.
const (
	typeIdent   = termType("ident")
	typeLiteral = termType("literal")
	typeEOF     = termType("$")
)

// eof is the terminal symbol which always appears at the end of token sequence.
const eof = "\u0000"

// accept tries to accept a token in "p".
// This function consumes a token and returns it if it matches to the specified "term".
// If it doesn't match, the function does not consume any tokens and return an error.
func ( *parser) ( termType) (string, error) {
	 := .tokens[0]
	switch  {
	case "/", "*", "**", ".", "=", "{", "}":
		if  != string() &&  != "/" {
			return "", fmt.Errorf("expected %q but got %q", , )
		}
	case typeEOF:
		if  != eof {
			return "", fmt.Errorf("expected EOF but got %q", )
		}
	case typeIdent:
		if  := expectIdent();  != nil {
			return "", 
		}
	case typeLiteral:
		if  := expectPChars();  != nil {
			return "", 
		}
	default:
		return "", fmt.Errorf("unknown termType %q", )
	}
	.tokens = .tokens[1:]
	.accepted = append(.accepted, )
	return , nil
}

// expectPChars determines if "t" consists of only pchars defined in RFC3986.
//
// https://www.ietf.org/rfc/rfc3986.txt, P.49
//
//	pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
//	unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
//	sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
//	              / "*" / "+" / "," / ";" / "="
//	pct-encoded   = "%" HEXDIG HEXDIG
func expectPChars( string) error {
	const (
		 = iota
		
		
	)
	 := 
	for ,  := range  {
		if  !=  {
			if !isHexDigit() {
				return fmt.Errorf("invalid hexdigit: %c(%U)", , )
			}
			switch  {
			case :
				 = 
			case :
				 = 
			}
			continue
		}

		// unreserved
		switch {
		case 'A' <=  &&  <= 'Z':
			continue
		case 'a' <=  &&  <= 'z':
			continue
		case '0' <=  &&  <= '9':
			continue
		}
		switch  {
		case '-', '.', '_', '~':
			// unreserved
		case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=':
			// sub-delims
		case ':', '@':
			// rest of pchar
		case '%':
			// pct-encoded
			 = 
		default:
			return fmt.Errorf("invalid character in path segment: %q(%U)", , )
		}
	}
	if  !=  {
		return fmt.Errorf("invalid percent-encoding in %q", )
	}
	return nil
}

// expectIdent determines if "ident" is a valid identifier in .proto schema ([[:alpha:]_][[:alphanum:]_]*).
func expectIdent( string) error {
	if  == "" {
		return errors.New("empty identifier")
	}
	for ,  := range  {
		switch {
		case '0' <=  &&  <= '9':
			if  == 0 {
				return fmt.Errorf("identifier starting with digit: %s", )
			}
			continue
		case 'A' <=  &&  <= 'Z':
			continue
		case 'a' <=  &&  <= 'z':
			continue
		case  == '_':
			continue
		default:
			return fmt.Errorf("invalid character %q(%U) in identifier: %s", , , )
		}
	}
	return nil
}

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