// #region Imports
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { HTMLElementEventEmitter } from '@breadstone/mosaik-elements';
import { CustomElement } from '../Controls/Components/Abstracts/CustomElement';
import { Attribute } from '../Controls/Decorators/AttributeDecorator';
import { Component } from '../Controls/Decorators/ComponentDecorator';
import { Property } from '../Controls/Decorators/PropertyDecorator';
import { ScrollerServiceLocator } from '../Dom/Scroller';
import { Emit } from '../events/Decorators/EmitDecorator';
import { RouterController } from './Controllers/RouterController';
import { RouterServiceLocator } from './Router';
import { routerElementStyle } from './RouterElementStyle';
import { routerElementTemplate } from './RouterElementTemplate';
import { RouterLocation } from './RouterLocation';
import { RouterOutletElement } from './RouterOutletElement';
// #endregion
/**
 * The `{@link RouterElement}` element.
 *
 * @fires navigated {RouterNavigatedEvent} - Fired when the route has changed.
 *
 * @public
 */
let RouterElement = class RouterElement extends CustomElement {
    // #region Fields
    _routerController;
    _location;
    _navigated;
    _useHash;
    _activeRoute;
    _scrollTargetFn;
    // #endregion
    // #region Ctor
    /**
     * @public
     */
    constructor() {
        super();
        this._routerController = new RouterController(this, [], {
            callback: (r) => {
                // This.requestUpdate();
                void this.updateComplete.then(() => {
                    this._navigated.emit({ route: r });
                });
            }
        });
        this._location = new RouterLocation(() => this.useHash);
        this._useHash = false;
        this._activeRoute = null;
        this._scrollTargetFn = null;
        this._navigated = new HTMLElementEventEmitter(this, 'navigated');
        // Make the router globally available
        RouterServiceLocator.set(this);
    }
    // #endregion
    // #region Properties
    /**
     * Returns the `{@link is}` property.
     * The `{@link is}` property represents natural name of this element.
     *
     * @public
     * @static
     * @readonly
     */
    static get is() {
        return 'mosaik-router';
    }
    /**
     * Gets or sets the `useHash` property.
     *
     * @public
     * @attr
     */
    get useHash() {
        return this._useHash;
    }
    set useHash(value) {
        if (this._useHash !== value) {
            this._useHash = value;
            this._routerController.useHash = value;
            this.requestUpdate('useHash');
        }
    }
    /**
     * Gets or sets the `scrollRestoration` property.
     *
     * @public
     * @attr
     */
    get scrollRestoration() {
        return this._routerController.scrollRestoration;
    }
    set scrollRestoration(value) {
        if (this._routerController.scrollRestoration !== value) {
            this._routerController.scrollRestoration = value;
            this.requestUpdate('scrollRestoration');
        }
    }
    /**
     * Gets or sets the `routes` property.
     *
     * @public
     */
    get routes() {
        return this._routerController.routes;
    }
    set routes(value) {
        if (this._routerController.routes !== value) {
            value = value.map((x) => ({
                ...x,
                enter: (_data) => {
                    const scrollTarget = this._scrollTargetFn?.();
                    if (scrollTarget) {
                        // If the route hash only changed. if so, do not scroll to top
                        if (this._location.hash) {
                            return true;
                        }
                        ScrollerServiceLocator.current.scrollToTop(scrollTarget, { behavior: 'instant' });
                    }
                    this._activeRoute = x;
                    return true;
                }
            }));
            this._routerController.routes = value;
            this.requestUpdate('routes');
        }
    }
    /**
     * Gets or sets the `scrollTargetFn` property.
     *
     * @public
     */
    get scrollTargetFn() {
        return this._scrollTargetFn;
    }
    set scrollTargetFn(value) {
        if (this._scrollTargetFn !== value) {
            this._scrollTargetFn = value;
            this.requestUpdate('scrollTargetFn');
        }
    }
    /**
     * Returns the `currentRoute` property.
     *
     * @public
     * @readonly
     */
    get currentRoute() {
        return this.routes.find((x) => x.name === this._activeRoute?.name) ?? null;
    }
    /**
     * Returns the `previousRoute` property.
     *
     * @public
     * @readonly
     */
    get previousRoute() {
        const currentIndex = this.routes.findIndex((x) => x.name === this._activeRoute?.name);
        return this.routes.at(currentIndex - 1) ?? null;
    }
    /**
     * Returns the `nextRoute` property.
     *
     * @public
     * @readonly
     */
    get nextRoute() {
        const currentIndex = this.routes.findIndex((x) => x.name === this._activeRoute?.name);
        return this.routes.at(currentIndex + 1) ?? null;
    }
    /**
     * Called when the router has navigated to a new route.
     * Provides reference to `{@link IRouterNavigatedEventDetail}` as event detail.
     *
     * @public
     * @readonly
     * @eventProperty
     */
    get navigated() {
        return this._navigated;
    }
    // #endregion
    // #region Methods
    /**
     * @public
     */
    async navigate(name) {
        // ignore name casing here
        const route = this.routes.find((x) => x.name?.toLowerCase() === name.toLowerCase() || x.path === name);
        if (route) {
            try {
                await this._routerController.navigate(route.path);
                this.requestUpdate();
                void this.updateComplete.then(() => {
                    this._navigated.emit({ route: route });
                });
                return true;
            }
            catch {
                return false;
            }
        }
        throw new Error(`Could not navigate to route. The route with name '${name}' was not found.`);
    }
    /**
     * @public
     */
    next() {
        const route = this.nextRoute;
        if (route) {
            return this.navigate(route.path);
        }
        throw new Error('Could not navigate to next route.');
    }
    /**
     * @public
     */
    previous() {
        const route = this.previousRoute;
        if (route) {
            return this.navigate(route.path);
        }
        throw new Error('Could not navigate to previous route.');
    }
    /**
     * @public
     */
    outlet() {
        return this._routerController.outlet();
    }
};
__decorate([
    Attribute({ type: Boolean,
        useDefault: true }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], RouterElement.prototype, "useHash", null);
__decorate([
    Attribute({ type: String }),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], RouterElement.prototype, "scrollRestoration", null);
__decorate([
    Property({ type: (Array) }),
    __metadata("design:type", Array),
    __metadata("design:paramtypes", [Array])
], RouterElement.prototype, "routes", null);
__decorate([
    Property({ type: Function }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [Object])
], RouterElement.prototype, "scrollTargetFn", null);
__decorate([
    Property({ type: Object }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [])
], RouterElement.prototype, "currentRoute", null);
__decorate([
    Property({ type: Object }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [])
], RouterElement.prototype, "previousRoute", null);
__decorate([
    Property({ type: Object }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [])
], RouterElement.prototype, "nextRoute", null);
__decorate([
    Emit({ eventName: 'navigated' }),
    __metadata("design:type", Object),
    __metadata("design:paramtypes", [])
], RouterElement.prototype, "navigated", null);
RouterElement = __decorate([
    Component({
        selector: 'mosaik-router',
        template: routerElementTemplate,
        styles: routerElementStyle,
        imports: [RouterOutletElement]
    }),
    __metadata("design:paramtypes", [])
], RouterElement);
export { RouterElement };
//# sourceMappingURL=RouterElement.js.map