// Copyright 2023 The Lynx Authors. All rights reserved.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
import { lynxDisposedAttribute } from '../../constants.js';
import { createIFrameRealm } from './createIFrameRealm.js';
import { templateManager } from './TemplateManager.js';
import(
/* webpackChunkName: "web-core-main-chunk" */
/* webpackFetchPriority: "high" */
'./LynxViewInstance.js');
/**
 * Based on our experiences, these elements are almost used in all lynx cards.
 */
/**
 * @property {string} url [required] (attribute: "url") The url of the entry of your Lynx card
 * @property {Cloneable} globalProps [optional] (attribute: "global-props") The globalProps value of this Lynx card
 * @property {Cloneable} initData [optional] (attribute: "init-data") The initial data of this Lynx card
 * @property {NativeModulesMap} nativeModulesMap [optional] use to customize NativeModules. key is module-name, value is esm url.
 * @property {NativeModulesCall} onNativeModulesCall [optional] the NativeModules value handler. Arguments will be cached before this property is assigned.
 * @property {"auto" | null} height [optional] (attribute: "height") set it to "auto" for height auto-sizing
 * @property {"auto" | null} width [optional] (attribute: "width") set it to "auto" for width auto-sizing
 * @property {NapiModulesMap} napiModulesMap [optional] the napiModule which is called in lynx-core. key is module-name, value is esm url.
 * @property {NapiModulesCall} onNapiModulesCall [optional] the NapiModule value handler.
 * @property {string[]} injectStyleRules [optional] the css rules which will be injected into shadowroot. Each items will be inserted by `insertRule` method. @see https://developer.mozilla.org/docs/Web/API/CSSStyleSheet/insertRule
 * @property {number} lynxGroupId [optional] (attribute: "lynx-group-id") the background shared context id, which is used to share webworker between different lynx cards
 * @property {InitI18nResources} initI18nResources [optional] (attribute: "init-i18n-resources") the complete set of i18nResources that on the container side, which can be obtained synchronously by _I18nResourceTranslation
 *
 * @event error lynx card fired an error
 * @event i18nResourceMissed i18n resource cache miss
 *
 * @example
 * HTML Example
 *
 * Note that you should declarae the size of lynx-view
 *
 * ```html
 * <lynx-view url="https://path/to/main-thread.js" raw-data="{}" global-props="{}" style="height:300px;width:300px">
 * </lynx-view>
 * ```
 *
 * React 19 Example
 * ```jsx
 * <lynx-view url={myLynxCardUrl} rawData={{}} globalProps={{}} style={{height:'300px', width:'300px'}}>
 * </lynx-view>
 * ```
 */
export class LynxViewElement extends HTMLElement {
    static lynxViewCount = 0;
    static tag = 'lynx-view';
    static observedAttributeAsProperties = [
        'url',
        'global-props',
        'init-data',
        'browser-config',
        'transform-vw',
        'transform-vh',
        'transform-rem',
    ];
    /**
     * @private
     */
    static observedAttributes = LynxViewElement.observedAttributeAsProperties.map(nm => nm.toLowerCase());
    #instance;
    #connected = false;
    #url;
    /**
     * @public
     * @property nativeModulesMap
     * @default {}
     */
    nativeModulesMap;
    /**
     * @param
     * @property napiModulesMap
     * @default {}
     */
    napiModulesMap;
    /**
     * @param
     * @property
     */
    onNapiModulesCall;
    #browserConfig;
    /**
     * @public
     * @property browserConfig
     */
    get browserConfig() {
        return this.#browserConfig;
    }
    set browserConfig(val) {
        if (typeof val === 'string') {
            try {
                this.#browserConfig = JSON.parse(val);
            }
            catch (e) {
                console.error('Invalid browser-config', e);
            }
        }
        else {
            this.#browserConfig = val;
        }
    }
    #transformVW = false;
    /**
     * @public
     * @property transformVW
     * Enable evaluating vw subset to the current LynxView container width
     */
    get transformVW() {
        return this.#transformVW;
    }
    set transformVW(val) {
        this.#transformVW = val;
        if (val) {
            this.setAttribute('transform-vw', '');
        }
        else {
            this.removeAttribute('transform-vw');
        }
    }
    #transformVH = false;
    /**
     * @public
     * @property transformVH
     * Enable evaluating vh subset to the current LynxView container height
     */
    get transformVH() {
        return this.#transformVH;
    }
    set transformVH(val) {
        this.#transformVH = val;
        if (val) {
            this.setAttribute('transform-vh', '');
        }
        else {
            this.removeAttribute('transform-vh');
        }
    }
    #transformREM = false;
    /**
     * @public
     * @property transformREM
     * Enable evaluating rem unit to the current CSS var(--rem-unit)
     */
    get transformREM() {
        return this.#transformREM;
    }
    set transformREM(val) {
        this.#transformREM = val;
        if (val) {
            this.setAttribute('transform-rem', '');
        }
        else {
            this.removeAttribute('transform-rem');
        }
    }
    constructor() {
        super();
        if (!this.onNativeModulesCall) {
            this.onNativeModulesCall = (name, data, moduleName) => {
                return new Promise((resolve) => {
                    this.#cachedNativeModulesCall.push({
                        args: [name, data, moduleName],
                        resolve,
                    });
                });
            };
        }
    }
    /**
     * @public
     * @property the url of lynx view output entry file
     */
    get url() {
        return this.#url;
    }
    set url(val) {
        this.#url = val;
        this.#render();
    }
    #globalProps = {};
    /**
     * @public
     * @property globalProps
     * @default {}
     */
    get globalProps() {
        return this.#globalProps;
    }
    set globalProps(val) {
        if (typeof val === 'string') {
            this.#globalProps = JSON.parse(val);
        }
        else {
            this.#globalProps = val;
        }
    }
    #initData = {};
    /**
     * @public
     * @property initData
     * @default {}
     */
    get initData() {
        return this.#initData;
    }
    set initData(val) {
        if (typeof val === 'string') {
            this.#initData = JSON.parse(val);
        }
        else {
            this.#initData = val;
        }
    }
    #initI18nResources = [];
    /**
     * @public
     * @property initI18nResources
     * @default {}
     */
    get initI18nResources() {
        return this.#initI18nResources;
    }
    set initI18nResources(val) {
        if (typeof val === 'string') {
            this.#initI18nResources = JSON.parse(val);
        }
        else {
            this.#initI18nResources = val;
        }
    }
    /**
     * @public
     * @method
     * update the `__initData` and trigger essential flow
     */
    updateI18nResources(data, options) {
        this.#instance?.i18nManager.updateData(data, options);
    }
    #cachedNativeModulesCall = [];
    #onNativeModulesCall;
    /**
     * @param
     * @property
     */
    get onNativeModulesCall() {
        return this.#onNativeModulesCall;
    }
    set onNativeModulesCall(handler) {
        this.#onNativeModulesCall = handler;
        for (const callInfo of this.#cachedNativeModulesCall) {
            callInfo.resolve(handler.apply(undefined, callInfo.args));
        }
        this.#cachedNativeModulesCall = [];
    }
    /**
     * @param
     * @property
     */
    get lynxGroupId() {
        return this.getAttribute('lynx-group-id')
            ? Number(this.getAttribute('lynx-group-id'))
            : undefined;
    }
    set lynxGroupId(val) {
        if (val) {
            this.setAttribute('lynx-group-id', val.toString());
        }
        else {
            this.removeAttribute('lynx-group-id');
        }
    }
    /**
     * @public
     * @method
     * update the `__initData` and trigger essential flow
     */
    updateData(data, processorName, callback) {
        this.#instance?.updateData(data, processorName).then(() => {
            callback?.();
        });
    }
    /**
     * @public
     * @method
     * update the `__globalProps`
     */
    updateGlobalProps(data) {
        this.#instance?.updateGlobalProps(data);
        this.globalProps = data;
    }
    /**
     * @public
     * @method
     * send global events, which can be listened to using the GlobalEventEmitter
     */
    sendGlobalEvent(eventName, params) {
        this.#instance?.backgroundThread.sendGlobalEvent(eventName, params);
    }
    /**
     * @public
     * @method
     * reload the current page
     */
    reload() {
        this.removeAttribute('ssr');
        this.#render();
    }
    /**
     * @override
     * "false" value will be omitted
     *
     * {@inheritdoc HTMLElement.setAttribute}
     */
    setAttribute(qualifiedName, value) {
        if (value === 'false') {
            this.removeAttribute(qualifiedName);
        }
        else {
            super.setAttribute(qualifiedName, value);
        }
    }
    /**
     * @private
     */
    attributeChangedCallback(name, oldValue, newValue) {
        if (oldValue !== newValue) {
            switch (name) {
                case 'url':
                    this.#url = newValue;
                    break;
                case 'global-props':
                    this.#globalProps = JSON.parse(newValue);
                    break;
                case 'browser-config':
                    this.browserConfig = JSON.parse(newValue);
                    break;
                case 'init-data':
                    this.#initData = JSON.parse(newValue);
                    break;
                case 'transform-vw':
                    this.transformVW = newValue !== 'false' && newValue !== null;
                    break;
                case 'transform-vh':
                    this.transformVH = newValue !== 'false' && newValue !== null;
                    break;
                case 'transform-rem':
                    this.transformREM = newValue !== 'false' && newValue !== null;
                    break;
            }
        }
    }
    injectStyleRules;
    #disposePromise;
    /**
     * @private
     */
    disconnectedCallback() {
        this.#connected = false;
        this.#disposeInstance();
    }
    async #disposeInstance() {
        if (this.#disposePromise) {
            return this.#disposePromise;
        }
        const dispose = async () => {
            this.shadowRoot?.querySelector('[part="page"]')
                ?.setAttribute(lynxDisposedAttribute, '');
            const oldInstance = this.#instance;
            this.#instance = undefined;
            if (oldInstance) {
                await oldInstance[Symbol.asyncDispose]();
            }
            if (this.shadowRoot) {
                this.shadowRoot.innerHTML = '';
                this.shadowRoot.adoptedStyleSheets = [];
            }
        };
        this.#disposePromise = dispose();
        await this.#disposePromise;
        this.#disposePromise = undefined;
    }
    /**
     * @#the flag to group all changes into one render operation
     */
    #rendering = false;
    /**
     * @private
     */
    async #render() {
        if (!this.#rendering && this.#connected) {
            this.#rendering = true;
            if (!this.shadowRoot) {
                this.attachShadow({ mode: 'open' });
            }
            if (this.#instance || this.#disposePromise) {
                await this.#disposeInstance();
            }
            const mtsRealmPromise = createIFrameRealm(this.shadowRoot);
            queueMicrotask(async () => {
                if (this.injectStyleRules && this.injectStyleRules.length > 0) {
                    const styleSheet = new CSSStyleSheet();
                    for (const rule of this.injectStyleRules) {
                        styleSheet.insertRule(rule);
                    }
                    this.shadowRoot.adoptedStyleSheets = this.shadowRoot
                        .adoptedStyleSheets.concat(styleSheet);
                }
                const mtsRealm = await mtsRealmPromise;
                if (this.#url) {
                    const lynxViewInstance = import(
                    /* webpackChunkName: "web-core-main-chunk" */
                    /* webpackFetchPriority: "high" */
                    './LynxViewInstance.js').then(({ LynxViewInstance }) => {
                        const isSSR = this.hasAttribute('ssr');
                        if (isSSR) {
                            this.removeAttribute('ssr');
                        }
                        return new LynxViewInstance(this, this.initData, this.globalProps, this.#url, this.shadowRoot, mtsRealm, isSSR, lynxGroupId, this.nativeModulesMap, this.napiModulesMap, this.#initI18nResources, this.transformVW, this.transformVH, this.transformREM, this.browserConfig);
                    });
                    templateManager.fetchBundle(this.#url, lynxViewInstance, this.transformVW, this.transformVH, this.transformREM, undefined);
                    const lynxGroupId = this.lynxGroupId;
                    this.#instance = await lynxViewInstance;
                    this.#rendering = false;
                }
            });
        }
    }
    #upgradeProperty(prop) {
        if (Object.prototype.hasOwnProperty.call(this, prop)) {
            const value = this[prop];
            delete this[prop];
            this[prop] = value;
        }
    }
    /**
     * @private
     */
    connectedCallback() {
        this.#upgradeProperty('browserConfig');
        this.#upgradeProperty('transformVW');
        this.#upgradeProperty('transformVH');
        this.#upgradeProperty('transformREM');
        if (this.url) {
            this.#url = this.url;
        }
        this.#connected = true;
        this.#render();
    }
}
if (customElements.get(LynxViewElement.tag)) {
    console.error(`[${LynxViewElement.tag}] has already been defined`);
}
else {
    customElements.define(LynxViewElement.tag, LynxViewElement);
}
//# sourceMappingURL=LynxView.js.map