// Copyright 2024 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 { Element } from './api/element.js';
import { initRunOnBackgroundDelay } from './delayRunOnBackground.js';
import { delayExecUntilJsReady, initEventDelay } from './delayWorkletEvent.js';
import { initEomImpl } from './eomImpl.js';
import { addEventMethodsIfNeeded } from './eventPropagation.js';
import { hydrateCtx } from './hydrate.js';
import { JsFunctionLifecycleManager, isRunOnBackgroundEnabled } from './jsFunctionLifecycle.js';
import { runRunOnMainThreadTask } from './runOnMainThread.js';
import { profile } from './utils/profile.js';
import { getFromWorkletRefMap, initWorkletRef } from './workletRef.js';
function initWorklet() {
    globalThis.lynxWorkletImpl = {
        _workletMap: {},
        _refImpl: initWorkletRef(),
        _runOnBackgroundDelayImpl: initRunOnBackgroundDelay(),
        _hydrateCtx: hydrateCtx,
        _eventDelayImpl: initEventDelay(),
        _eomImpl: initEomImpl(),
        _runRunOnMainThreadTask: runRunOnMainThreadTask,
    };
    if (isRunOnBackgroundEnabled()) {
        globalThis.lynxWorkletImpl._jsFunctionLifecycleManager = new JsFunctionLifecycleManager();
    }
    globalThis.registerWorklet = registerWorklet;
    globalThis.registerWorkletInternal = registerWorklet;
    globalThis.runWorklet = runWorklet;
}
/**
 * Register a worklet function, allowing it to be executed by `runWorklet()`.
 * This is called in lepus.js.
 * @param _type worklet type, 'main-thread' or 'ui'
 * @param id worklet hash
 * @param worklet worklet function
 */
function registerWorklet(_type, id, worklet) {
    lynxWorkletImpl._workletMap[id] = worklet;
}
/**
 * Entrance of all worklet calls.
 * Native event touch handler will call this function.
 * @param ctx worklet object.
 * @param params worklet params.
 * @param options run worklet options.
 */
function runWorklet(ctx, params, options) {
    if (!validateWorklet(ctx)) {
        console.warn('Worklet: Invalid worklet object: ' + JSON.stringify(ctx));
        return;
    }
    if ('_lepusWorkletHash' in ctx) {
        delayExecUntilJsReady(ctx._lepusWorkletHash, params);
        return;
    }
    return runWorkletImpl(ctx, params, options);
}
function runWorkletImpl(ctx, params, options) {
    const worklet = profile('transformWorkletCtx ' + ctx._wkltId, () => transformWorklet(ctx, true));
    const params_ = profile('transformWorkletParams', () => transformWorklet(params || [], false));
    const [hasEventMethods, eventCtx] = addEventMethodsIfNeeded(params_, options);
    const result = profile('runWorklet', () => worklet(...params_));
    if (hasEventMethods) {
        return {
            returnValue: result,
            eventReturnResult: eventCtx._eventReturnResult,
        };
    }
    return result;
}
function validateWorklet(ctx) {
    return typeof ctx === 'object' && ctx !== null && ('_wkltId' in ctx || '_lepusWorkletHash' in ctx);
}
const workletCache = new WeakMap();
function transformWorklet(ctx, isWorklet) {
    /* v8 ignore next 3 */
    if (typeof ctx !== 'object' || ctx === null) {
        return ctx;
    }
    if (isWorklet) {
        const res = workletCache.get(ctx);
        if (res) {
            return res;
        }
    }
    const worklet = { main: ctx };
    transformWorkletInner(worklet, 0, ctx);
    if (isWorklet) {
        workletCache.set(ctx, worklet.main);
    }
    return worklet.main;
}
const transformWorkletInner = (value, depth, ctx) => {
    const limit = 1000;
    if (++depth >= limit) {
        throw new Error('Depth of value exceeds limit of ' + limit + '.');
    }
    /* v8 ignore next 3 */
    if (typeof value !== 'object' || value === null) {
        return;
    }
    const obj = value;
    for (const key in obj) {
        const subObj = obj[key];
        if (typeof subObj !== 'object' || subObj === null) {
            continue;
        }
        if ( /** isEventTarget */'elementRefptr' in subObj) {
            obj[key] = new Element(subObj['elementRefptr']);
            continue;
        }
        else if (subObj instanceof Element) {
            continue;
        }
        transformWorkletInner(subObj, depth, ctx);
        const isWorkletRef = '_wvid' in subObj;
        if (isWorkletRef) {
            obj[key] = getFromWorkletRefMap(subObj);
            continue;
        }
        const isWorklet = '_wkltId' in subObj;
        if (isWorklet) {
            // `subObj` is worklet ctx. Shallow copy it to prevent the transformed worklet from referencing ctx.
            // This would result in the value of `workletCache` referencing its key.
            obj[key] = lynxWorkletImpl._workletMap[subObj._wkltId]
                .bind({ ...subObj });
            obj[key].ctx = subObj;
            continue;
        }
        const isJsFn = '_jsFnId' in subObj;
        if (isJsFn) {
            subObj['_execId'] = ctx._execId;
            lynxWorkletImpl._jsFunctionLifecycleManager?.addRef(ctx._execId, subObj);
            continue;
        }
    }
};
export { initWorklet };
//# sourceMappingURL=workletRuntime.js.map