package routes

import (
	"fmt"
	"strings"
)

type RouteMap map[string]*node

func NewRouteMap() RouteMap {
	return RouteMap{
		"GET":     newRadixTree(),
		"POST":    newRadixTree(),
		"DELETE":  newRadixTree(),
		"OPTIONS": newRadixTree(),
		"HEAD":    newRadixTree(),
		"PUT":     newRadixTree(),
		"TRACE":   newRadixTree(),
		"CONNECT": newRadixTree(),
		"PATCH":   newRadixTree(),
	}
}

// AddRoute appends a route to the map. Routes added first are
// considered higher priority if there's ever ambiguity during a
// lookup.
func (rm RouteMap) AddRoute(pattern string, method string, name string) error {
	node, ok := rm[method]
	if !ok {
		return fmt.Errorf("unknown method   pattern=%q  method=%q  name=%q", pattern, method, name)
	}
	paths := normalizeParens(pattern)
	for _, path := range paths {
		path = cleanWildcards(path)
		path = cleanGlobs(path)
		node.add(path, name)
	}
	return nil
}

func (rm RouteMap) AddRouteAllMethods(pattern string, name string) error {
	for method := range rm {
		if err := rm.AddRoute(pattern, method, name); err != nil {
			return err
		}
	}
	return nil
}

// LookupTrimSlash looks up the name of the path according to the map,
// removing a single extra trailing slash if present. If the path isn't found
// in the map, or if the method is unknown, then this returns empty string.
func (rm RouteMap) LookupTrimSlash(path string, method string) string {
	path = trimTrailingSlash(path)
	return rm.lookup(path, method)
}

// lookup looks up the name of the path according to the map. If the path
// isn't found in the map, or if the method is unknown, then this returns
// empty string.
func (rm RouteMap) lookup(path string, method string) string {
	node, ok := rm[method]
	if !ok {
		return ""
	}
	return node.lookup(path)
}

func trimTrailingSlash(s string) string {
	trimmed := strings.TrimSuffix(s, "/")
	if len(trimmed) == 0 {
		return s
	}
	return trimmed
}
