package router

import "log"
import "net"
// import "errors"
import "net/http"
import "hnakra/config"
import "hnakra/protocol"
import "hnakra/router/mux"

type Pattern struct {
	Scheme string
	Host   string
	Path   string
}

func (pattern Pattern) MuxPattern () string {
	return pattern.Host + pattern.Path
}

func (pattern Pattern) String () string {
	return pattern.Scheme + "://" + pattern.Host + pattern.Path
}

func (pattern *Pattern) FillDefaults () {
	if pattern.Scheme == "" { pattern.Scheme = "https" }
	if pattern.Host   == "" { pattern.Host   = "@"     }
	if pattern.Path   == "" { pattern.Path   = "/"     }
}

type Router struct {
	config   config.Config
	http     *mux.HTTP
	services map[*Service] struct { }
}

func New (config config.Config) *Router {
	router := &Router {
		config:   config,
		http:     mux.NewHTTP(config),
		services: make(map[*Service] struct { }),
	}
	return router
}

func (router *Router) HTTPMux () *mux.HTTP {
	return router.http
}

func (router *Router) Accept (conn net.Conn) {
	router.newService(conn)
}

func (router *Router) ServeHTTP (res http.ResponseWriter, req *http.Request) {
	log.Println("->?", req.RemoteAddr, "requests", req.URL.String())
	router.http.ServeHTTP(res, req)
}

// TODO: uniquely identify connected services by an ID

func (router *Router) Service (name string) *Service {
	for service := range router.services {
		if service.Name() == name {
			return service
		}
	}
	return nil
}

func (router *Router) OverServices (callback func (service *Service) bool) {
	for service := range router.services {
		if !callback(service) { break }
	}
}

func (router *Router) list (service *Service) {
	router.services[service] = struct { } { }
}

func (router *Router) unlist (service *Service) {
	delete(router.services, service)
}

func (router *Router) Validate (name string, key []byte, pattern Pattern) protocol.Status {
	user := router.config.User(name)
	if user == nil || !user.Validate(key) {
		return protocol.StatusBadCredentials
	}
	if !user.CanMountOn(pattern.Scheme, pattern.Host, pattern.Path) {
		return protocol.StatusBadMount
	}
	return protocol.StatusOk
}
