
function createModule<T>(loader: () => Promise<T>) {
    const callbacks: Array<(module: T) => void> = [];
    const mod = {
        MODULE: undefined as unknown as T,
        MAYBEMODULE: null as T | null,
        /** Wait for the module to be loaded (doesn't trigger a load) */
        ready(): Promise<T> {
            if (mod.MODULE) return Promise.resolve(mod.MODULE);
            return new Promise(resolve => { callbacks.push(resolve); });
        },
        /** Load the module */
        async load(): Promise<T> {
            if (mod.MODULE) return mod.MODULE;
            const module = await loader();
            mod.MODULE = module;
            mod.MAYBEMODULE = module;
            for (const cb of callbacks) cb(module);
            callbacks.length = 0;
            return module;
        }
    };
    return mod;
}

/**
 * External dependencies that are loaded on demand either by the engine automatically when needed or they can be loaded manually by calling the `load` function.
 *
 * Use the `ready` function to wait for the module to be loaded if you do not wand to trigger a load.
 *
 * If a module is already loaded it's also available in the `MODULE` variable.
 */
export const MODULES = {
    MaterialX: createModule<typeof import("@needle-tools/materialx")>(
        () => import("@needle-tools/materialx")
    ),
    RAPIER_PHYSICS: createModule<typeof import("@dimforge/rapier3d-compat")>(
        () => import("@dimforge/rapier3d-compat")
    ),
    POSTPROCESSING: createModule<typeof import("postprocessing")>(
        () => import("postprocessing")
    ),
    POSTPROCESSING_AO: createModule<typeof import("n8ao")>(
        () => import("n8ao")
    ),
};
