import type { MobState } from "../Mob"
import { STRIDE, type Mesh } from "../../../engine/scene/Mesh"

// Shared building blocks for concrete mob definitions: faceted geometry primitives
// and deterministic wander helpers.

export const TAU = Math.PI * 2

// --- Wander helpers -------------------------------------------------------

/** A new heading: free wander when inside the leash, else biased back toward home
 *  so the mob never drifts off into the peaks (`jitter` = the random cone half-width
 *  in radians layered on top of the homeward bearing). */
export function wanderHeading(
  mob: MobState,
  leash: number,
  jitter: number,
): number {
  const dx = mob.home.x - mob.position.x
  const dz = mob.home.z - mob.position.z
  if (dx * dx + dz * dz > leash * leash) {
    return Math.atan2(dx, dz) + (nextRand(mob) - 0.5) * jitter
  }
  return nextRand(mob) * TAU
}

/** mulberry32 step over the mob's own `seed` (mutated), so a mob's motion is
 *  deterministic and needs no external RNG object to clone. */
export function nextRand(mob: MobState): number {
  const a = (mob.seed + 0x6D2B79F5) | 0
  mob.seed = a
  let t = Math.imul(a ^ (a >>> 15), 1 | a)
  t ^= t + Math.imul(t ^ (t >>> 7), 61 | t)
  return ((t ^ (t >>> 14)) >>> 0) / 4294967296
}

// --- Geometry primitives --------------------------------------------------
// Mobs are drawn double-sided, so winding is not load-bearing --
// these only need to place faceted, flat-shaded surfaces.

/** A UV-rected ellipsoid (pole on Y), faceted like the boulders. */
export function ellipsoid(
  mesh: Mesh,
  cx: number,
  cy: number,
  cz: number,
  rx: number,
  ry: number,
  rz: number,
  seg: number,
  rings: number,
  u0: number,
  u1: number,
  v0: number,
  v1: number,
): void {
  const start = mesh.verts.length / STRIDE
  for (let ir = 0; ir <= rings; ir++) {
    const phi = (ir / rings) * Math.PI
    const cyv = Math.cos(phi)
    const crv = Math.sin(phi)
    const v = v0 + (v1 - v0) * (ir / rings)
    for (let is = 0; is <= seg; is++) {
      const theta = (is / seg) * TAU
      const u = u0 + (u1 - u0) * (is / seg)
      mesh.verts.push(
        cx + crv * Math.cos(theta) * rx,
        cy + cyv * ry,
        cz + crv * Math.sin(theta) * rz,
        u,
        v,
      )
    }
  }
  quadGrid(mesh, start, seg, rings)
}

/** An ovoid whose pole axis is Z (rings step along z, tapering at both ends), so
 *  the mapped `v` runs down the body's length -- used for the bee's stripes. */
export function ovoidZ(
  mesh: Mesh,
  z0: number,
  z1: number,
  r: number,
  seg: number,
  rings: number,
  u0: number,
  u1: number,
  v0: number,
  v1: number,
): void {
  const start = mesh.verts.length / STRIDE
  for (let ir = 0; ir <= rings; ir++) {
    const t = ir / rings
    const z = z0 + (z1 - z0) * t
    const rr = r * (0.15 + 0.85 * Math.sin(t * Math.PI))
    const v = v0 + (v1 - v0) * t
    for (let is = 0; is <= seg; is++) {
      const theta = (is / seg) * TAU
      const u = u0 + (u1 - u0) * (is / seg)
      mesh.verts.push(Math.cos(theta) * rr, Math.sin(theta) * rr, z, u, v)
    }
  }
  quadGrid(mesh, start, seg, rings)
}

/** One flat wing quad on `side` (+1 right / -1 left), swept up and out. */
export function wing(
  mesh: Mesh,
  side: number,
  u0: number,
  u1: number,
  v0: number,
  v1: number,
): void {
  const base = mesh.verts.length / STRIDE
  mesh.verts.push(
    side * 0.06,
    0.12,
    0.14,
    u0,
    v0,
    side * 0.42,
    0.24,
    0.1,
    u1,
    v0,
    side * 0.42,
    0.24,
    -0.12,
    u1,
    v1,
    side * 0.06,
    0.12,
    -0.1,
    u0,
    v1,
  )
  mesh.indices.push(base, base + 1, base + 2, base, base + 2, base + 3)
}

/** Index a (seg x rings) vertex grid (row = seg+1) into two tris per cell. */
function quadGrid(mesh: Mesh, start: number, seg: number, rings: number): void {
  const row = seg + 1
  for (let ir = 0; ir < rings; ir++) {
    for (let is = 0; is < seg; is++) {
      const p = start + ir * row + is
      mesh.indices.push(p, p + 1, p + row + 1, p, p + row + 1, p + row)
    }
  }
}
