import { nothing } from 'lit';
import { ActivityManager, ActivityLifecycle } from './ActivityManager.js';
import { ApplicationRenderer } from './renderer/ApplicationRenderer.js';
import { ThemeManager } from './ThemeManager.js';
/**
 * The application represents an application screen that hosts
 * Activities. Activities are managed by the Activity manager.
 *
 * An application is a separate app within the ecosystem. For example,
 * the file browser is one application. The domain designer is another.
 *
 * Each application manages it's own lifecycle. The application is rendered
 * by the lit's library `render()` function. Activities can call application's
 * main `requestUpdate()` function to render the entire application.
 *
 * Activities have the render function that renders its UI.
 * The Activity's UI can be composed of fragments, but this is optional.
 * When an activity uses fragments, they receive control over rendering their
 * UIs. An activity implementation has to take care of passing HTMLElement
 * references to their Fragments to make that happen. Otherwise, the activity
 * has to communicate with their fragments to request to re-render the application.
 */
export class Application extends EventTarget {
    activityUpdateListener = this.handleActivityUpdate.bind(this);
    #activities;
    #theme;
    /**
     * Application's theme manager.
     */
    get theme() {
        return this.#theme;
    }
    /**
     * Returns the application's activity manager.
     */
    get manager() {
        return this.#activities;
    }
    #events;
    /**
     * The application main event bus.
     * All activities and fragments use it to communicate.
     * This way, events do not leak outside the application.
     */
    get events() {
        return this.#events;
    }
    #renderer;
    get renderer() {
        return this.#renderer;
    }
    /**
     * @type A promise resolved when the render finished.
     */
    get updateComplete() {
        return this.#renderer.updateComplete;
    }
    /**
     * @type True when the application was first rendered.
     */
    get firstRendered() {
        return this.#renderer.firstRendered;
    }
    /**
     * @param renderRoot The element or a CSS query to the element where the application will be rendered.
     */
    constructor(renderRoot) {
        super();
        this.#events = new EventTarget();
        this.#activities = new ActivityManager(this);
        this.#renderer = new ApplicationRenderer(this);
        this.#renderer.setRenderRoot(renderRoot);
        this.#theme = new ThemeManager();
        // Listen for activity updates
        this.#events.addEventListener('activity:update', this.activityUpdateListener);
    }
    /**
     * @see {@link ./Renderer.js}
     */
    setRenderRoot(renderRoot) {
        this.#renderer.setRenderRoot(renderRoot);
    }
    /**
     * Called when the application is stopped/closed.
     * Perform cleanup here.
     */
    onStop() {
        //
    }
    /**
     * Called when the application was rendered for the first time.
     * Call `super.onFirstRender()` when overriding this method.
     */
    onFirstRender() {
        this.#events.dispatchEvent(new Event('app:first-render'));
    }
    /**
     * Called when the application was rendered.
     */
    onRendered() {
        //
    }
    /**
     * The main render method for the Application.  Called whenever an activity
     * requests an update. Renders the currently active Activity's content.
     *
     * If you override this method then any activity won't be rendered.
     *
     * @returns The Lit template to render.
     */
    render() {
        const currentActivity = this.#activities.getCurrentActivity();
        // Only render if started or beyond
        if (currentActivity && currentActivity.lifecycle >= ActivityLifecycle.Started) {
            return currentActivity.render();
        }
        return nothing;
    }
    /**
     * Handles activity update requests and re-renders the application.
     */
    handleActivityUpdate(e) {
        this.requestUpdate(e.detail || undefined);
    }
    /**
     * Requests an update of the application UI. This will call the `render()` method
     * and update the DOM.
     */
    requestUpdate(opts = {}) {
        const { app = true } = opts;
        if (app) {
            this.#renderer.requestUpdate();
        }
    }
    /**
     * Starts the application by calling onStart and performing an initial render.
     */
    async run() {
        await this.onStart();
        this.requestUpdate();
    }
    /**
     * Stops the application, performing cleanup if necessary.
     */
    async stop() {
        this.#events.removeEventListener('activity:update', this.activityUpdateListener);
        // Allow subclasses to clean up
        await this.onStop();
        // ... any other application-level cleanup
    }
    getCurrentActivity() {
        return this.#activities.getCurrentActivity();
    }
    /**
     * Unifies Fragment and Application interfaces.
     * @returns `this`
     */
    getApplication() {
        return this;
    }
    findActiveActivity(id) {
        return this.#activities.findActiveActivity(id);
    }
}
//# sourceMappingURL=Application.js.map