// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file implements a decision tree for fast matching of requests to
// patterns.
//
// The root of the tree branches on the host of the request.
// The next level branches on the method.
// The remaining levels branch on consecutive segments of the path.
//
// The "more specific wins" precedence rule can result in backtracking.
// For example, given the patterns
//     /a/b/z
//     /a/{x}/c
// we will first try to match the path "/a/b/c" with /a/b/z, and
// when that fails we will try against /a/{x}/c.

package http

import (
	
)

// A routingNode is a node in the decision tree.
// The same struct is used for leaf and interior nodes.
type routingNode struct {
	// A leaf node holds a single pattern and the Handler it was registered
	// with.
	pattern *pattern
	handler Handler

	// An interior node maps parts of the incoming request to child nodes.
	// special children keys:
	//     "/"	trailing slash (resulting from {$})
	//	   ""   single wildcard
	//	   "*"  multi wildcard
	children   mapping[string, *routingNode]
	emptyChild *routingNode // optimization: child with key ""
}

// addPattern adds a pattern and its associated Handler to the tree
// at root.
func ( *routingNode) ( *pattern,  Handler) {
	// First level of tree is host.
	 := .addChild(.host)
	// Second level of tree is method.
	 = .addChild(.method)
	// Remaining levels are path.
	.addSegments(.segments, , )
}

// addSegments adds the given segments to the tree rooted at n.
// If there are no segments, then n is a leaf node that holds
// the given pattern and handler.
func ( *routingNode) ( []segment,  *pattern,  Handler) {
	if len() == 0 {
		.set(, )
		return
	}
	 := [0]
	if .multi {
		if len() != 1 {
			panic("multi wildcard not last")
		}
		.addChild("*").set(, )
	} else if .wild {
		.addChild("").([1:], , )
	} else {
		.addChild(.s).([1:], , )
	}
}

// set sets the pattern and handler for n, which
// must be a leaf node.
func ( *routingNode) ( *pattern,  Handler) {
	if .pattern != nil || .handler != nil {
		panic("non-nil leaf fields")
	}
	.pattern = 
	.handler = 
}

// addChild adds a child node with the given key to n
// if one does not exist, and returns the child.
func ( *routingNode) ( string) *routingNode {
	if  == "" {
		if .emptyChild == nil {
			.emptyChild = &routingNode{}
		}
		return .emptyChild
	}
	if  := .findChild();  != nil {
		return 
	}
	 := &routingNode{}
	.children.add(, )
	return 
}

// findChild returns the child of n with the given key, or nil
// if there is no child with that key.
func ( *routingNode) ( string) *routingNode {
	if  == "" {
		return .emptyChild
	}
	,  := .children.find()
	return 
}

// match returns the leaf node under root that matches the arguments, and a list
// of values for pattern wildcards in the order that the wildcards appear.
// For example, if the request path is "/a/b/c" and the pattern is "/{x}/b/{y}",
// then the second return value will be []string{"a", "c"}.
func ( *routingNode) (, ,  string) (*routingNode, []string) {
	if  != "" {
		// There is a host. If there is a pattern that specifies that host and it
		// matches, we are done. If the pattern doesn't match, fall through to
		// try patterns with no host.
		if ,  := .findChild().matchMethodAndPath(, );  != nil {
			return , 
		}
	}
	return .emptyChild.matchMethodAndPath(, )
}

// matchMethodAndPath matches the method and path.
// Its return values are the same as [routingNode.match].
// The receiver should be a child of the root.
func ( *routingNode) (,  string) (*routingNode, []string) {
	if  == nil {
		return nil, nil
	}
	if ,  := .findChild().matchPath(, nil);  != nil {
		// Exact match of method name.
		return , 
	}
	if  == "HEAD" {
		// GET matches HEAD too.
		if ,  := .findChild("GET").matchPath(, nil);  != nil {
			return , 
		}
	}
	// No exact match; try patterns with no method.
	return .emptyChild.matchPath(, nil)
}

// matchPath matches a path.
// Its return values are the same as [routingNode.match].
// matchPath calls itself recursively. The matches argument holds the wildcard matches
// found so far.
func ( *routingNode) ( string,  []string) (*routingNode, []string) {
	if  == nil {
		return nil, nil
	}
	// If path is empty, then we are done.
	// If n is a leaf node, we found a match; return it.
	// If n is an interior node (which means it has a nil pattern),
	// then we failed to match.
	if  == "" {
		if .pattern == nil {
			return nil, nil
		}
		return , 
	}
	// Get the first segment of path.
	,  := firstSegment()
	// First try matching against patterns that have a literal for this position.
	// We know by construction that such patterns are more specific than those
	// with a wildcard at this position (they are either more specific, equivalent,
	// or overlap, and we ruled out the first two when the patterns were registered).
	if ,  := .findChild().(, );  != nil {
		return , 
	}
	// If matching a literal fails, try again with patterns that have a single
	// wildcard (represented by an empty string in the child mapping).
	// Again, by construction, patterns with a single wildcard must be more specific than
	// those with a multi wildcard.
	// We skip this step if the segment is a trailing slash, because single wildcards
	// don't match trailing slashes.
	if  != "/" {
		if ,  := .emptyChild.(, append(, ));  != nil {
			return , 
		}
	}
	// Lastly, match the pattern (there can be at most one) that has a multi
	// wildcard in this position to the rest of the path.
	if  := .findChild("*");  != nil {
		// Don't record a match for a nameless wildcard (which arises from a
		// trailing slash in the pattern).
		if .pattern.lastSegment().s != "" {
			 = append(, pathUnescape([1:])) // remove initial slash
		}
		return , 
	}
	return nil, nil
}

// firstSegment splits path into its first segment, and the rest.
// The path must begin with "/".
// If path consists of only a slash, firstSegment returns ("/", "").
// The segment is returned unescaped, if possible.
func firstSegment( string) (,  string) {
	if  == "/" {
		return "/", ""
	}
	 = [1:] // drop initial slash
	 := strings.IndexByte(, '/')
	if  < 0 {
		 = len()
	}
	return pathUnescape([:]), [:]
}

// matchingMethods adds to methodSet all the methods that would result in a
// match if passed to routingNode.match with the given host and path.
func ( *routingNode) (,  string,  map[string]bool) {
	if  != "" {
		.findChild().matchingMethodsPath(, )
	}
	.emptyChild.matchingMethodsPath(, )
	if ["GET"] {
		["HEAD"] = true
	}
}

func ( *routingNode) ( string,  map[string]bool) {
	if  == nil {
		return
	}
	.children.eachPair(func( string,  *routingNode) bool {
		if ,  := .matchPath(, nil);  != nil {
			[] = true
		}
		return true
	})
	// Don't look at the empty child. If there were an empty
	// child, it would match on any method, but we only
	// call this when we fail to match on a method.
}