import { Terrain, type Terrain as Ground } from "../../../engine/world/Terrain"
import { Mesh, type Mesh as Geometry } from "../../../engine/scene/Mesh"
import type { Material } from "../../../engine/render/Material"
import type { ActorDefinition } from "../../../engine/scene/Actor"
import { Mob, type MobState } from "../Mob"
import { ellipsoid, nextRand, wanderHeading } from "./mobkit"

// Everything about the frog: squat, ground-bound, sits then springs a ballistic hop.

export type Frog = ActorDefinition<MobState, Ground>

export namespace Frog {
  export function create(material: Material): Frog {
    const mesh = Mesh.create()
    build(mesh)
    return {
      prototype: {
        groups: [{ mesh, material }],
        radius: 0.7,
        minY: -0.7,
        maxY: 1.3,
      },
      update,
      transform: Mob.transform,
      collider: (state) => Mob.collider(state, 0.7, 0.6, state.grounded),
    }
  }
}

const LEASH = 5
const REST_MIN = 0.7
const REST_SPAN = 1.8
const HOP_SPEED = 1.6
const HOP_IMPULSE = 3.2
const GRAVITY = 14

function build(mesh: Geometry): void {
  // Wide squat body, two eye bumps on the top-front, two hind haunches. UVs:
  // the frog texture is green skin on the left, a dark eye tone on the right.
  ellipsoid(mesh, 0, 0.26, 0, 0.5, 0.28, 0.52, 6, 4, 0, 0.68, 0, 1)
  ellipsoid(mesh, 0.24, 0.5, 0.26, 0.13, 0.13, 0.13, 4, 3, 0.75, 0.98, 0, 1)
  ellipsoid(mesh, -0.24, 0.5, 0.26, 0.13, 0.13, 0.13, 4, 3, 0.75, 0.98, 0, 1)
  ellipsoid(mesh, 0.3, 0.2, -0.26, 0.2, 0.2, 0.26, 4, 3, 0, 0.68, 0, 1)
  ellipsoid(mesh, -0.3, 0.2, -0.26, 0.2, 0.2, 0.26, 4, 3, 0, 0.68, 0, 1)
}

function update(mob: MobState, dt: number, terrain: Ground): void {
  if (mob.grounded) {
    mob.timer -= dt
    mob.position.y = Terrain.height(terrain, mob.position.x, mob.position.z)
    if (mob.timer > 0) {
      return
    }
    // Launch a hop: pick a heading (pulled homeward past the leash), then convert
    // it into a forward+upward ballistic velocity.
    mob.heading = wanderHeading(mob, LEASH, 0.9)
    mob.vx = Math.sin(mob.heading) * HOP_SPEED
    mob.vz = Math.cos(mob.heading) * HOP_SPEED
    mob.vy = HOP_IMPULSE
    mob.grounded = false
    return
  }
  mob.vy -= GRAVITY * dt
  mob.position.x += mob.vx * dt
  mob.position.y += mob.vy * dt
  mob.position.z += mob.vz * dt
  const ground = Terrain.height(terrain, mob.position.x, mob.position.z)
  if (mob.position.y <= ground && mob.vy < 0) {
    mob.position.y = ground
    mob.vx = 0
    mob.vy = 0
    mob.vz = 0
    mob.grounded = true
    mob.timer = REST_MIN + nextRand(mob) * REST_SPAN
  }
}
