/*!
 * Custom elements for Nanopore-Digital Web applications
 */
import { h, Host, getElement, Build, } from "@stencil/core";
// import type { VNode, HTMLStencilElement } from "@stencil/core";
const supportsConstructibleStylesheets = (() => {
    try {
        return !!new CSSStyleSheet();
    }
    catch (_e) {
        return false;
    }
})();
const cacheKeys = new WeakMap();
/**
 * Dynamically create a constructible stylesheet which is applied to the component.
 * The stylesheet is then cached for future instances of the component.
 * @usage
As a string:
```
@ConstructableStyle() style = `.bg { background: url('assets/${ this.mode }/bg.png'); }`;
```
As a function:
```
@ConstructableStyle() style = () => `.bg { background: url('assets/${ this.mode }/bg.png'); }`;
```
 * @param opts - optional `cacheKeyProperty` - in case an instance of a component could produce different styles based on variables.
 * @returns `@ConstructableStyle` decorator
 */
export function ConstructibleStyle(opts = {}) {
    return (target, propertyKey) => {
        if (!opts.cacheKeyProperty) {
            opts.cacheKeyProperty = propertyKey;
        }
        const { componentWillLoad, render, componentWillRender } = target;
        if (supportsConstructibleStylesheets && !Build.isServer) {
            // adds a constructible stylesheet to the component
            const addStylesheet = (instance) => {
                if (!instance[opts.cacheKeyProperty] ||
                    (cacheKeys.get(instance) &&
                        cacheKeys.get(instance) === instance[opts.cacheKeyProperty]))
                    return;
                cacheKeys.set(instance, instance[opts.cacheKeyProperty]);
                const host = getElement(instance);
                const cssText = typeof instance[propertyKey] === 'function'
                    ? instance[propertyKey]()
                    : instance[propertyKey];
                const root = (host.shadowRoot || document);
                root.adoptedStyleSheets = [
                    ...(root.adoptedStyleSheets || []),
                    getOrCreateStylesheet(instance, target, cssText, opts),
                ];
            };
            target.componentWillLoad = function () {
                const willLoadResult = componentWillLoad && componentWillLoad.call(this);
                addStylesheet(this);
                return willLoadResult;
            };
            target.componentWillRender = function () {
                const willRenderResult = componentWillRender && componentWillRender.call(this);
                addStylesheet(this);
                return willRenderResult;
            };
        }
        else {
            // adds a style element to the component
            target.render = function () {
                const cssText = typeof this[propertyKey] === 'function'
                    ? this[propertyKey]()
                    : this[propertyKey];
                let renderedNode = render.call(this);
                if (isHost(renderedNode)) {
                    appendStyleToHost(renderedNode, target.constructor.name, cssText);
                }
                else {
                    renderedNode = h(Host, null, renderedNode);
                    if (!('attachShadow' in HTMLElement.prototype)) {
                        appendStyleToHost(renderedNode, target.constructor.name, cssText);
                    }
                    else {
                        if (!target.__constructableStyle) {
                            const style = document.createElement('style');
                            style.setAttribute('type', 'text/css');
                            style.setAttribute('constructible-style', target.constructor.name);
                            style.innerHTML = cssText;
                            target.__constructableStyle = style;
                            document.head.appendChild(style);
                        }
                    }
                }
                return renderedNode;
            };
        }
    };
}
function appendStyleToHost(node, targetName, cssText) {
    (getHostChildren(node) || []).push(h("style", { type: "text/css", "constructible-style": targetName }, cssText));
}
function getOrCreateStylesheet(instance, target, cssText, opts) {
    if (!target.__constructableStyle) {
        target.__constructableStyle = {};
    }
    const key = instance[opts.cacheKeyProperty];
    if (!target.__constructableStyle[key]) {
        target.__constructableStyle[key] = new CSSStyleSheet();
        target.__constructableStyle[key].replace(cssText);
    }
    return target.__constructableStyle[key];
}
function isHost(node) {
    for (const prop in node) {
        if (prop in node) {
            if (node[prop] === Host) {
                return true;
            }
        }
    }
    return false;
}
function getHostChildren(node) {
    for (const prop in node) {
        if (prop in node) {
            if (Array.isArray(node[prop])) {
                return node[prop];
            }
        }
    }
}
