import { FrameEvent } from "./engine_context.js";
import { ContextEvent } from "./engine_context_registry.js";
import { type LifecycleMethod, LifecycleMethodOptions,registerFrameEventCallback, unregisterFrameEventCallback } from "./engine_lifecycle_functions_internal.js";


/**
 * Register a callback in the engine context created event.
 * This happens once per context (after the context has been created and the first content has been loaded)
 * @param cb The callback to be called
 * @returns A function that can be called to unregister the callback
 * @example
 * ```ts
 * onInitialized((ctx : Context) => {
 *     // do something
 * }
 * ```
 * */
export function onInitialized(cb: LifecycleMethod, opts?: LifecycleMethodOptions): () => void {
    registerFrameEventCallback(cb, ContextEvent.ContextCreated, opts);
    return () => unregisterFrameEventCallback(cb, ContextEvent.ContextCreated);
}
/**
 * Register a callback before the engine context is cleared.   
 * This happens if e.g. `<needle-engine src>` changes
 */
export function onClear(cb: LifecycleMethod, opts?: LifecycleMethodOptions): () => void {
    registerFrameEventCallback(cb, ContextEvent.ContextClearing, opts);
    return () => unregisterFrameEventCallback(cb, ContextEvent.ContextClearing);
}

/**
 * Register a callback in the engine before the context is destroyed
 * This happens once per context (before the context is destroyed)
 */
export function onDestroy(cb: LifecycleMethod, opts?: LifecycleMethodOptions): () => void {
    registerFrameEventCallback(cb, ContextEvent.ContextDestroying, opts);
    return () => unregisterFrameEventCallback(cb, ContextEvent.ContextDestroying);
}

/** Register a callback in the engine start event.   
 * This happens once at the beginning of a frame   
 * (e.g. once when the method is registered, after <needle-engine> is created or the src has changed)   
 * @param cb The callback to be called. Optionally return a function that will be called when the onStart callback is removed again
 * @returns A function that can be called to unregister the callback
 * @example
 * ```ts
 * onStart((ctx : Context) => {
 *     // do something
 *     console.log("Needle Engine: onStart registered")
 *     // optional to cleanup:
 *     return () => { console.log("OnStart removed") }
 * }
 * ```
 * */
export function onStart(cb: LifecycleMethod, opts?: LifecycleMethodOptions): () => void {
    registerFrameEventCallback(cb, FrameEvent.Start, opts);
    return () => unregisterFrameEventCallback(cb, FrameEvent.Start);
}


/** Register a callback in the engine update event   
 * This is called every frame   
 * @param cb The callback to be called
 * @returns A function that can be called to unregister the callback
 * @example
 * ```ts
 * onUpdate((ctx : Context) => {
 *     // do something
 *     console.log("Needle Engine: onUpdate registered")
 *     // optional to cleanup:
 *     return () => { console.log("onUpdate removed") }
 * }
 * ```
 * */
export function onUpdate(cb: LifecycleMethod, opts?: LifecycleMethodOptions): () => void {
    registerFrameEventCallback(cb, FrameEvent.Update, opts);
    return () => unregisterFrameEventCallback(cb, FrameEvent.Update);
}

/** Register a callback in the engine onBeforeRender event   
 * This is called every frame before the main camera renders   
 * @param cb The callback to be called
 * @returns A function that can be called to unregister the callback
 * @example
 * ```ts
 * onBeforeRender((ctx : Context) => {
 *     // do something
 * }
 * ```
 * */
export function onBeforeRender(cb: LifecycleMethod, opts?: LifecycleMethodOptions): () => void {
    registerFrameEventCallback(cb, FrameEvent.OnBeforeRender, opts);
    return () => unregisterFrameEventCallback(cb, FrameEvent.OnBeforeRender);
}

/**
 * Register a callback in the engine onAfterRender event
 * This is called every frame after the main camera has rendered
 * @param cb The callback to be called
 * @returns A function that can be called to unregister the callback
 * @example
 * ```ts
 * const unsubscribe = onAfterRender((ctx : Context) => {
 *    // do something...
 *    console.log("After render");
 *    // if you want to unsubscribe after the first call:
 *    unsubscribe();
 * });
 * ```
 */
export function onAfterRender(cb: LifecycleMethod, opts?: LifecycleMethodOptions): () => void {
    registerFrameEventCallback(cb, FrameEvent.OnAfterRender, opts);
    return () => unregisterFrameEventCallback(cb, FrameEvent.OnAfterRender);
}