import { __esDecorate, __runInitializers } from "tslib";
/* eslint-disable @typescript-eslint/no-explicit-any */
import { html, render } from 'lit';
import reactive from '../decorators/reactive.js';
export const renderingValue = Symbol('renderingValue');
export const renderPage = Symbol('renderPage');
export const setUpdatePromise = Symbol('setUpdatePromise');
export const updateResolver = Symbol('updateResolver');
export const hasPendingUpdatePromise = Symbol('hasPendingUpdatePromise');
export const resolveUpdatePromise = Symbol('resolveUpdatePromise');
/**
 * Adds methods that helps with asynchronous template rendering.
 *
 * The application page content is rendered into the `#app` container.
 *
 * @mixin
 */
export const RenderableMixin = (superClass) => {
    let RenderableMixinClass = (() => {
        let _classSuper = superClass;
        let _firstRendered_decorators;
        let _firstRendered_initializers = [];
        let _firstRendered_extraInitializers = [];
        return class RenderableMixinClass extends _classSuper {
            static {
                const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
                __esDecorate(this, null, _firstRendered_decorators, { kind: "accessor", name: "firstRendered", static: false, private: false, access: { has: obj => "firstRendered" in obj, get: obj => obj.firstRendered, set: (obj, value) => { obj.firstRendered = value; } }, metadata: _metadata }, _firstRendered_initializers, _firstRendered_extraInitializers);
                if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
            }
            #firstRendered_accessor_storage = __runInitializers(this, _firstRendered_initializers, false);
            /**
             * Determines whether the initial render had run and the `firstRender()`
             * function was called.
             *
             * @default false
             */
            get firstRendered() { return this.#firstRendered_accessor_storage; }
            set firstRendered(value) { this.#firstRendered_accessor_storage = value; }
            [(_firstRendered_decorators = [reactive()], renderingValue)] = (__runInitializers(this, _firstRendered_extraInitializers), false);
            [hasPendingUpdatePromise] = false;
            [updateResolver];
            #updateComplete;
            /**
             * @type A promise resolved when the render finished.
             */
            get updateComplete() {
                return this.#updateComplete;
            }
            /**
             * True when rendering debouncer is running.
             */
            get rendering() {
                return this[renderingValue];
            }
            constructor(...args) {
                super(...args);
                this[setUpdatePromise]();
            }
            /**
             * Helper function to be overridden by child classes. It is called when the view
             * is rendered for the first time.
             */
            firstRender() {
                //
            }
            /**
             * A function called when the template has been rendered
             */
            updated() {
                //
            }
            /**
             * This to be used by the child classes to render page template.
             * @returns Application page template
             */
            pageTemplate() {
                return html ``;
            }
            /**
             * The main render function. Sub classes should not override this method.
             * Override `[renderPage]()` instead.
             *
             * The function calls `[renderPage]()` in a micro task so it is safe to call this
             * multiple time in the same event loop.
             */
            render() {
                if (this.rendering) {
                    return;
                }
                this[renderingValue] = true;
                if (!this[hasPendingUpdatePromise]) {
                    this[setUpdatePromise]();
                }
                requestAnimationFrame(() => {
                    this[renderingValue] = false;
                    this[renderPage]();
                });
            }
            [renderPage]() {
                const root = document.querySelector('#app');
                if (!root) {
                    // eslint-disable-next-line no-console
                    console.warn(`The <div id="app"></div> is not in the document.`);
                    return;
                }
                if (!this.firstRendered) {
                    this.firstRendered = true;
                    // cleanup any pre-existing content.
                    Array.from(root.childNodes).forEach((node) => node.parentNode?.removeChild(node));
                    setTimeout(() => this.firstRender());
                }
                render(this.pageTemplate(), root, { host: this });
                this[resolveUpdatePromise]();
                this.updated();
            }
            [setUpdatePromise]() {
                this.#updateComplete = new Promise((resolve) => {
                    this[updateResolver] = resolve;
                    this[hasPendingUpdatePromise] = true;
                });
            }
            [resolveUpdatePromise]() {
                if (!this[hasPendingUpdatePromise]) {
                    return;
                }
                this[hasPendingUpdatePromise] = false;
                const resolver = this[updateResolver];
                if (resolver) {
                    resolver();
                }
            }
        };
    })();
    return RenderableMixinClass;
};
//# sourceMappingURL=RenderableMixin.js.map