import type { Vec3 } from "../../engine/math/Vec3"
import { Mesh } from "../../engine/scene/Mesh"
import type { Material } from "../../engine/render/Material"
import type { Prefab } from "../../engine/scene/Prefab"

const TAU = Math.PI * 2

/** Concrete atlas style referenced directly by flower instances. */
export type FlowerStyle = { bloomUv: [number, number] }

/** A single small flower: a thin crossed-quad stem plus a shallow fan of petals.
 *  Tiny, so it is drawn double-sided (no backface cull) and carries no collider.
 *  `size` is roughly its height; `seed` jitters the petals. */
export type Flower = {
  position: Vec3
  style: FlowerStyle
  size: number
  seed: number
}

/**
 * Low-poly flower geometry. The `flower` texture is a 2x2 color atlas -- green
 * (stem) plus white / red / yellow blooms -- and every vertex samples the flat
 * center of one region, so a flower is solid-colored with no per-flower texture
 * or draw call. `build` appends into one shared flower mesh.
 */
export namespace Flower {
  export const white: FlowerStyle = { bloomUv: [0.75, 0.25] }
  export const red: FlowerStyle = { bloomUv: [0.25, 0.75] }
  export const yellow: FlowerStyle = { bloomUv: [0.75, 0.75] }

  /** uv center of the green stem region. */
  const STEM_U = 0.25
  const STEM_V = 0.25

  export function create(material: Material): Prefab<Flower> {
    return {
      position: (flower) => flower.position,
      bakeNear: (flower, batch) => build(flower, batch.mesh(material)),
    }
  }

  export function build(flower: Flower, mesh: Mesh): void {
    const rand = rng(flower.seed)
    const p = flower.position
    const height = flower.size * (0.8 + rand() * 0.4)
    const bloomY = p.y + height
    const w = flower.size * 0.04
    // Stem: two thin crossed quads so it reads from any angle.
    stem(mesh, p.x, p.y, p.z, bloomY, w, 0)
    stem(mesh, p.x, p.y, p.z, bloomY, 0, w)
    // Bloom: a shallow fan of petals, center raised a touch so it domes.
    const [bu, bv] = flower.style.bloomUv
    const rad = flower.size * 0.38
    const center = Mesh.push(mesh, p.x, bloomY + rad * 0.3, p.z, bu, bv)
    const ring = center + 1
    const petals = 5
    for (let i = 0; i <= petals; i++) {
      const angle = (i / petals) * TAU + rand() * 0.4
      Mesh.push(
        mesh,
        p.x + Math.cos(angle) * rad,
        bloomY,
        p.z + Math.sin(angle) * rad,
        bu,
        bv,
      )
    }
    for (let i = 0; i < petals; i++) {
      mesh.indices.push(center, ring + i, ring + i + 1)
    }
  }

  /** A thin vertical quad from the ground to `y1`, width along (dx, dz). */
  function stem(
    mesh: Mesh,
    x: number,
    y0: number,
    z: number,
    y1: number,
    dx: number,
    dz: number,
  ): void {
    const a = Mesh.push(mesh, x - dx, y0, z - dz, STEM_U, STEM_V)
    const b = Mesh.push(mesh, x + dx, y0, z + dz, STEM_U, STEM_V)
    const c = Mesh.push(mesh, x + dx, y1, z + dz, STEM_U, STEM_V)
    const d = Mesh.push(mesh, x - dx, y1, z - dz, STEM_U, STEM_V)
    mesh.indices.push(a, b, c, a, c, d)
  }

  /** Deterministic 0..1 generator (mulberry32) seeded per flower. */
  function rng(seed: number): () => number {
    let a = seed >>> 0
    return () => {
      a = (a + 0x6D2B79F5) | 0
      let t = Math.imul(a ^ (a >>> 15), 1 | a)
      t ^= t + Math.imul(t ^ (t >>> 7), 61 | t)
      return ((t ^ (t >>> 14)) >>> 0) / 4294967296
    }
  }
}
