import type { Framebuffer } from "engine/render/Framebuffer"
import { RenderProtocol, type RenderWorkerInit } from "engine/render/RenderProtocol"
import { RenderScene, type RenderInstance } from "engine/render/RenderScene"

const ctx = globalThis as unknown as {
  addEventListener: (
    type: "message",
    handler: (e: { data: RenderWorkerInit }) => void,
  ) => void
}

ctx.addEventListener("message", (e) => {
  const m = e.data
  const fb: Framebuffer = {
    width: m.width,
    height: m.height,
    color: new Uint32Array(m.colorSAB),
    depth: new Float32Array(m.depthSAB),
  }
  const ctrl = new Int32Array(m.ctrlSAB)
  const cameraData = new Float64Array(m.cameraSAB)
  const viewProjection = new Float32Array(m.viewProjectionSAB)
  const visibleChunks = new Int32Array(m.visibleChunkSAB)
  const instanceIds = new Int32Array(m.instanceIdSAB)
  const instanceTransforms = new Float32Array(m.instanceTransformSAB)
  const times = new Float64Array(m.timesSAB)
  const { scene, band, config, skyStep, workerIndex } = m
  const instances: RenderInstance[] = []

  // Lock-free frame loop: block until main bumps the frame counter, render this
  // band, record the band time, and signal done. No messages per frame.
  let last = 0
  for (;;) {
    Atomics.wait(ctrl, RenderProtocol.FRAME, last)
    last = Atomics.load(ctrl, RenderProtocol.FRAME)
    const t0 = performance.now()
    const frameCamera = RenderProtocol.readCamera(cameraData)
    const count = Atomics.load(ctrl, RenderProtocol.VISIBLE_CHUNKS)
    const visible = [...visibleChunks.subarray(0, count)]
    const instanceCount = Atomics.load(ctrl, RenderProtocol.VISIBLE_INSTANCES)
    RenderProtocol.readInstances(
      instanceIds,
      instanceTransforms,
      instanceCount,
      instances,
    )
    RenderScene.renderBand(
      fb,
      scene,
      frameCamera.camera,
      viewProjection,
      visible,
      instances,
      config,
      skyStep,
      frameCamera.time,
      band[0],
      band[1],
    )
    times[workerIndex] = performance.now() - t0
    Atomics.add(ctrl, RenderProtocol.DONE, 1)
  }
})
