// Internal per-frame plumbing for the 2D engine. NOT user-facing — user game logic uses the host
// `setLoop` global. `onUpdate` taps the engine's render-synced pre-frame hook (_creator2d.onUpdate),
// for anything that must move in lockstep with the native render (no 1-frame lag).
//
// The animation-event registration is lazy (first use) so a 2D project that never animates a sprite
// pulls none of this in.

import { node2dRegistry } from "./Node2D"

export const onUpdate = (callback: (dt: number) => void): void => _creator2d.onUpdate(callback)
export const offUpdate = (callback: (dt: number) => void): void => _creator2d.offUpdate(callback)

// Route native animation events (queued in C++, drained once per frame) to the owning node, which
// translates the native clip id to a clip name and emits 'loopReached' / 'completed'.
let animEventsRegistered = false
export const ensureAnimEvents = (): void => {
  if (animEventsRegistered) return
  animEventsRegistered = true
  _creator2d.setOnAnimEvent(({ entityId, clipId, type }) => {
    node2dRegistry.get(entityId)?._dispatchAnimEvent(clipId, type)
  })
}

// Route native physics contact/sensor events to the owning nodes' 'enter'/'exit' events. Each event
// is delivered to BOTH nodes with the other as the argument. type: 0 contactBegin, 1 contactEnd,
// 2 sensorBegin, 3 sensorEnd. Registered lazily on the first physics body.
let physicsEventsRegistered = false
export const ensurePhysicsEvents = (): void => {
  if (physicsEventsRegistered) return
  physicsEventsRegistered = true
  _creator2d.setOnPhysicsEvent((a, b, type) => {
    const na = node2dRegistry.get(a)
    const nb = node2dRegistry.get(b)
    if (!na || !nb) return
    const channel = type === 1 || type === 3 ? "exit" : "enter"
    na._emitCollision(channel, nb)
    nb._emitCollision(channel, na)
  })
}
