/**
 * LANE-RENDER — wildlife: dust piles and mosquito swarms (§2/§5, M2's living hazards).
 *
 * Piles are HF-dust glitter heaps (codex: quantizer sweepings) — they grow with `size`.
 * Swarms are pixel-gnat clouds that cluster over the machine they're degrading (`target`);
 * the machine's own flicker is EntityLayer's job, driven off the same `target` ids.
 *
 * Both are instanced from day one: the perf bar is 50 piles + 10 swarms, and a swarm is
 * itself a cloud of gnats. One InstancedMesh for every pile, one for every gnat across
 * every swarm — two draw calls for the whole ecosystem.
 */
import * as THREE from 'three';
import type { SimSnapshot, WildlifeState } from '../contracts';

const GNATS_PER_SWARM = 24;

/** HF dust: pale, faintly iridescent glitter. Emissive so heaps sparkle at iso distance. */
function pileMaterial(): THREE.MeshStandardMaterial {
  return new THREE.MeshStandardMaterial({
    color: 0xe8e0ff, emissive: 0xb0a0ff, emissiveIntensity: 0.5,
    roughness: 0.3, metalness: 0.6, flatShading: true,
  });
}

export class WildlifeLayer {
  readonly group = new THREE.Group();
  private piles: THREE.InstancedMesh;
  private gnats: THREE.InstancedMesh;
  private pileCap = 64;
  private gnatCap = 64 * GNATS_PER_SWARM;
  private dummy = new THREE.Object3D();
  /** Entity ids currently harassed by a swarm — EntityLayer reads this to flicker them. */
  readonly harassed = new Set<number>();

  constructor() {
    this.group.name = 'wildlife';
    // A low, faceted heap; instance scale grows it with `size`.
    const pileGeo = new THREE.IcosahedronGeometry(0.5, 0);
    pileGeo.scale(1, 0.5, 1);
    this.piles = new THREE.InstancedMesh(pileGeo, pileMaterial(), this.pileCap);
    this.piles.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
    this.piles.castShadow = true;
    this.piles.frustumCulled = false;
    this.piles.count = 0;

    const gnatGeo = new THREE.PlaneGeometry(0.08, 0.08);
    const gnatMat = new THREE.MeshBasicMaterial({
      color: 0x101014, transparent: true, opacity: 0.85, side: THREE.DoubleSide,
    });
    this.gnats = new THREE.InstancedMesh(gnatGeo, gnatMat, this.gnatCap);
    this.gnats.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
    this.gnats.frustumCulled = false;
    this.gnats.count = 0;

    this.group.add(this.piles, this.gnats);
  }

  sync(wildlife: ReadonlyArray<WildlifeState> | undefined, timeSec: number): void {
    this.harassed.clear();
    const list = wildlife ?? [];
    let pileN = 0;
    let gnatN = 0;

    for (const w of list) {
      if (w.kind === 'dust-pile') {
        if (pileN >= this.pileCap) continue;
        const s = 0.4 + THREE.MathUtils.clamp(w.size, 0, 1) * 1.3;
        this.dummy.position.set(w.pos.x + 0.5, 0.05 * s, w.pos.y + 0.5);
        this.dummy.rotation.set(0, w.id * 1.7, 0); // desync facets between heaps
        this.dummy.scale.set(s, s, s);
        this.dummy.updateMatrix();
        this.piles.setMatrixAt(pileN++, this.dummy.matrix);
      } else {
        if (w.target !== undefined) this.harassed.add(w.target);
        const cx = w.pos.x + 0.5;
        const cz = w.pos.y + 0.5;
        const density = Math.round(GNATS_PER_SWARM * (0.4 + w.size * 0.6));
        for (let i = 0; i < density && gnatN < this.gnatCap; i++) {
          // Each gnat orbits the swarm centre on its own little chaotic path.
          const ph = w.id * 3.1 + i * 0.7;
          const rad = 0.5 + 0.5 * Math.sin(timeSec * 3 + ph * 1.3);
          const a = timeSec * (1.5 + (i % 3) * 0.4) + ph;
          this.dummy.position.set(
            cx + Math.cos(a) * rad,
            0.5 + 0.4 * Math.sin(timeSec * 4 + ph) + w.size * 0.3,
            cz + Math.sin(a * 1.1) * rad,
          );
          this.dummy.rotation.set(0, -a, 0); // face roughly along travel
          this.dummy.scale.setScalar(1);
          this.dummy.updateMatrix();
          this.gnats.setMatrixAt(gnatN++, this.dummy.matrix);
        }
      }
    }

    this.piles.count = pileN;
    this.piles.instanceMatrix.needsUpdate = true;
    this.gnats.count = gnatN;
    this.gnats.instanceMatrix.needsUpdate = true;
  }
}

/** Convenience: which entities a snapshot's swarms are harassing (for EntityLayer). */
export function harassedIn(snap: SimSnapshot): Set<number> {
  const out = new Set<number>();
  for (const w of snap.wildlife ?? []) {
    if (w.kind === 'mosquito-swarm' && w.target !== undefined) out.add(w.target);
  }
  return out;
}
