import { createContext, useContext, useEffect } from "react";
import { HistoryNavigator } from "./HistoryNavigator";
import { PathMatcher } from "./PathMatcher";
import { isRootPath, joinPath } from "./path";
export class BaseRouter {
    // the path to use when navigating to the root of the router
    index;
    matcher = new PathMatcher();
    constructor(routes, index) {
        this.index = index;
        for (const route of routes) {
            this.matcher.addPath(route.path, route);
        }
    }
    match(path) {
        const useIndex = isRootPath(path) && this.index;
        return this.matcher.match(useIndex ? this.index : path);
    }
}
export class Router extends BaseRouter {
    prompt;
    observer;
    navigator = new HistoryNavigator();
    constructor(routes, updateState) {
        super(routes);
        this.navigator.addListener((event) => {
            if (event.isCancelable && this.prompt && !!this.prompt.when) {
                if (!window.confirm(this.prompt.message))
                    return;
            }
            if (this.observer) {
                this.observer(event);
            }
            // only process afterChange events
            if (event.name === "afterChange") {
                const match = this.match(event.location.pathname);
                if (match && match.value) {
                    updateState({
                        ...match,
                        state: event.state,
                    });
                }
                else {
                    updateState(null);
                }
            }
        });
    }
    getTopRouter() {
        return this;
    }
    /**
     * Subsequent navigations will preserve the given params in the query string.
     * Use null to clear the sticky params.
     * @param params
     */
    setStickyParams(params) {
        this.navigator.stickyParams = params != null ? params : undefined;
    }
    withObserver(observer) {
        this.observer = observer;
        return this;
    }
    start() {
        this.navigator.start();
        // initialize with the current location
        this.navigator.firePageLoad();
    }
    stop() {
        this.navigator.stop();
    }
    navigate(path, options) {
        this.navigator.navigate(path, options);
    }
}
export class NestedRouter extends BaseRouter {
    parent;
    basePath;
    constructor(parent, basePath, routes) {
        super(routes);
        this.parent = parent;
        this.basePath = basePath;
    }
    getTopRouter() {
        if (this.parent instanceof Router) {
            return this.parent;
        }
        else {
            return this.parent.getTopRouter();
        }
    }
    navigate(path, options) {
        // base path is nested by default in a NestedRouter unless explicitly set to false by caller
        const isBasePathNested = options?.isBasePathNested ?? true;
        let basePath;
        if (isBasePathNested) {
            const childBasePath = options?.basePath;
            // e.g. "/store" + "/objects/123" => "/store/objects/123"
            basePath = childBasePath ? joinPath(this.basePath, childBasePath) : this.basePath;
        }
        else {
            // e.g. "/store" + "/studio" => "/studio"
            basePath = options?.basePath ?? this.basePath;
        }
        this.parent.navigate(path, {
            ...options,
            basePath,
        });
    }
}
const ReactRouterContext = createContext(undefined);
export { ReactRouterContext };
export function useRouterContext() {
    const ctx = useContext(ReactRouterContext);
    if (!ctx) {
        throw new Error("useRouter must be used within a RouterProvider");
    }
    return ctx;
}
export function useNavigate() {
    const { navigate } = useRouterContext();
    return navigate;
}
export function useParams(arg) {
    const { params } = useRouterContext();
    if (arg) {
        return params[arg];
    }
    else {
        return params;
    }
}
export function useLocation() {
    const { location } = useRouterContext();
    return location;
}
export function useNavigationPrompt(prompt) {
    const { router } = useRouterContext();
    useEffect(() => {
        router.getTopRouter().prompt = prompt;
        return () => {
            router.getTopRouter().prompt = undefined;
        };
    }, []);
    useEffect(() => {
        if (prompt.when) {
            const doBlock = prompt.when;
            const listener = function (ev) {
                if (doBlock) {
                    ev.preventDefault();
                    ev.returnValue = "";
                }
            };
            window.addEventListener("beforeunload", listener);
            return () => {
                window.removeEventListener("beforeunload", listener);
            };
        }
    }, [prompt.when]);
}
//# sourceMappingURL=Router.js.map