import { __decorate } from "tslib";
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable class-methods-use-this */
/* eslint-disable max-classes-per-file */
import { dedupeMixin } from '@open-wc/dedupe-mixin';
import { html, render, css } from 'lit';
import { reactive } from '../lib/decorators.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');
/**
 * Whether the current browser supports `adoptedStyleSheets`.
 */
export const supportsAdoptingStyleSheets = window.ShadowRoot &&
    'adoptedStyleSheets' in Document.prototype &&
    'replace' in CSSStyleSheet.prototype;
/**
 * Adds methods that helps with asynchronous template rendering.
 *
 * The application page content is rendered into the `#app` container.
 *
 * @mixin
 */
export const RenderableMixin = dedupeMixin((superClass) => {
    var _a, _b;
    class RenderableMixinClass extends superClass {
        static get styles() {
            return css ``;
        }
        /**
         * True when rendering debouncer is running.
         */
        get rendering() {
            return this[renderingValue];
        }
        constructor(...args) {
            super(...args);
            /**
             * Determines whether the initial render had run and the `firstRender()`
             * function was called.
             *
             * @default false
             */
            this.firstRendered = false;
            this[_a] = false;
            this[_b] = false;
            this[setUpdatePromise]();
            this.adaptStyles();
        }
        adaptStyles() {
            // @ts-ignore
            const input = this.constructor.styles;
            if (!input) {
                return;
            }
            let styles = input;
            if (!Array.isArray(input)) {
                styles = [input];
            }
            if (supportsAdoptingStyleSheets) {
                const sheets = styles.map(i => i.styleSheet);
                // @ts-ignore
                document.adoptedStyleSheets = [...document.adoptedStyleSheets, ...sheets];
            }
            else {
                styles.forEach((s) => {
                    const style = document.createElement('style');
                    style.textContent = s.cssText;
                    document.head.appendChild(style);
                });
            }
        }
        /**
         * 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]();
            });
        }
        [(_a = renderingValue, _b = hasPendingUpdatePromise, 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();
            }
        }
    }
    __decorate([
        reactive()
    ], RenderableMixinClass.prototype, "firstRendered", void 0);
    return RenderableMixinClass;
});
//# sourceMappingURL=RenderableMixin.js.map