import { Vec3 } from "engine/math/Vec3"
import type { Mesh } from "engine/scene/Mesh"
import type { Material } from "engine/render/Material"
import type { Prefab } from "engine/scene/Prefab"
import type { Tree } from "../Tree"
import { blob, lerp, limb, TAU, rng } from "./treekit"

// Oak: short tapered trunk, a couple of branches, a broad cluster of rounded canopy
// blobs (bushy, wider than tall). Trunk = brown bark, foliage = oak leaf.

export type Oak = Prefab<Tree>

export namespace Oak {
  export function create(trunk: Material, foliage: Material): Oak {
    return {
      position: (tree) => tree.position,
      bakeNear: (tree, batch) =>
        build(tree, batch.mesh(trunk), batch.mesh(foliage), "full"),
      bakeFar: (tree, batch) =>
        build(tree, batch.mesh(trunk), batch.mesh(foliage), "impostor"),
      collider(tree) {
        if (tree.growth <= 0.35) {
          return null
        }
        const radius = tree.growth * 0.3 + 0.15
        return {
          shape: "box",
          minX: tree.position.x - radius,
          maxX: tree.position.x + radius,
          minZ: tree.position.z - radius,
          maxZ: tree.position.z + radius,
          top: tree.position.y + 3,
          standable: false,
        }
      },
    }
  }
}

function build(
  tree: Tree,
  trunk: Mesh,
  leaves: Mesh,
  lod: "full" | "impostor",
): void {
  const base = tree.position
  const g = tree.growth
  const rand = rng(tree.seed)
  const h = lerp(0.8, 7, g)
  const rTrunk = lerp(0.04, 0.32, g)
  const forkY = base.y + h * 0.5
  const canopyY = base.y + h * 0.72
  const blobR = h * 0.3
  if (lod === "impostor") {
    // One low-poly blob on a stubby trunk -- reads as an oak at distance.
    limb(
      trunk,
      base,
      { x: base.x, y: forkY, z: base.z },
      rTrunk,
      rTrunk * 0.6,
      3,
    )
    blob(leaves, { x: base.x, y: canopyY, z: base.z }, blobR * 1.15, rand, 4, 2)
    return
  }
  limb(trunk, base, { x: base.x, y: forkY, z: base.z }, rTrunk, rTrunk * 0.6, 5)

  const spread = h * 0.32
  // Central blob plus, as it grows, a couple offset ones -> broad bushy crown.
  const blobs = 1 + Math.round(g * 2)
  for (let i = 0; i < blobs; i++) {
    const angle = rand() * TAU
    const rad = i === 0 ? 0 : spread * (0.5 + rand() * 0.5)
    const center = {
      x: base.x + Math.cos(angle) * rad,
      y: canopyY + (rand() - 0.4) * spread,
      z: base.z + Math.sin(angle) * rad,
    }
    blob(leaves, center, blobR * (0.7 + rand() * 0.4), rand)
  }
  // Grown oaks throw out a few branches, each tipped with a leaf tuft.
  if (g > 0.55) {
    const branches = 2 + Math.round(rand())
    for (let i = 0; i < branches; i++) {
      const angle = rand() * TAU
      const dir = Vec3.normalize({
        x: Math.cos(angle),
        y: 1.2,
        z: Math.sin(angle),
      })
      const start = { x: base.x, y: base.y + h * 0.42, z: base.z }
      const end = Vec3.add(start, Vec3.scale(dir, h * 0.3))
      limb(trunk, start, end, rTrunk * 0.4, rTrunk * 0.2, 4)
      blob(leaves, end, blobR * 0.6, rand)
    }
  }
}
