/**
 * Layer 3 — world ambience. Post-discovery only.
 *
 * The factory gets a voice once the fossil is dug up: a hum that tracks how hard the plant is
 * working, and a strained whine as decoders approach scram. Both are continuous voices built
 * once and driven by AudioParams — nothing here allocates per frame.
 *
 * Subordinate by design: the hum sits under everything, and ducks further when the radio is
 * carrying a station, because a tuned station is what the player asked to hear.
 */
import { glide, noiseSource } from './graph';

export interface Ambience {
  out: GainNode;
  start(t0: number): void;
  /**
   * @param load    0..1 how hard the plant is running (draw vs gen)
   * @param fever   0..1 aggregate heat — the strained whine near scram
   * @param duck    0..1 how much the radio wants the floor
   */
  update(load: number, fever: number, duck: number): void;
}

export function createAmbience(ctx: BaseAudioContext): Ambience {
  const out = ctx.createGain();
  out.gain.value = 0;

  // ---- the hum: mains-ish fundamental + its octave, plus a broadband machine-room bed.
  const hum = ctx.createOscillator();
  hum.type = 'sawtooth';
  hum.frequency.value = 50;
  const humLp = ctx.createBiquadFilter();
  humLp.type = 'lowpass';
  humLp.frequency.value = 180;
  const humG = ctx.createGain();
  humG.gain.value = 0.0;
  hum.connect(humLp).connect(humG).connect(out);

  const room = noiseSource(ctx, 'brown');
  const roomLp = ctx.createBiquadFilter();
  roomLp.type = 'lowpass';
  roomLp.frequency.value = 600;
  const roomG = ctx.createGain();
  roomG.gain.value = 0.0;
  room.connect(roomLp).connect(roomG).connect(out);

  // ---- the strained whine: only shows up as machines climb toward a scram.
  const whine = ctx.createOscillator();
  whine.type = 'triangle';
  whine.frequency.value = 1900;
  const whineG = ctx.createGain();
  whineG.gain.value = 0;
  whine.connect(whineG).connect(out);

  let started = false;

  return {
    out,
    start(t0) {
      if (started) return;
      started = true;
      hum.start(t0);
      room.start(t0);
      whine.start(t0);
    },
    update(load, fever, duck) {
      if (!started) return;
      const l = Math.min(1, Math.max(0, load));
      const f = Math.min(1, Math.max(0, fever));
      const d = 1 - 0.55 * Math.min(1, Math.max(0, duck)); // radio wins, but never silences

      glide(humG.gain, (0.035 + 0.05 * l) * d, ctx, 0.4);
      glide(roomG.gain, (0.02 + 0.045 * l) * d, ctx, 0.4);
      // a busier plant runs its mains a touch sharp — cheap, and it reads as effort
      glide(hum.frequency, 50 + 6 * l, ctx, 0.5);
      glide(roomLp.frequency, 500 + 900 * l, ctx, 0.5);

      // Heat only becomes audible late; below 0.55 a warm factory is just a factory.
      const strain = Math.max(0, (f - 0.55) / 0.45);
      glide(whineG.gain, 0.03 * strain * strain * d, ctx, 0.3);
      glide(whine.frequency, 1700 + 900 * strain, ctx, 0.3);
    },
  };
}
