/**
 * LANE-RENDER — heat shimmer.
 *
 * A real refraction shimmer means a screen-space pass, and post-processing is
 * LANE-SCREEN's territory — so this is a cheap stand-in: a soft additive column above
 * the machine whose bands wobble and rise. At iso distance it reads as rising heat, it
 * costs one transparent quad per hot machine, and it never touches the render pipeline.
 *
 * Created lazily (only once a machine actually heats) and one material per machine,
 * because opacity has to vary per entity.
 */
import * as THREE from 'three';

const VERT = /* glsl */ `
  varying vec2 vUv;
  void main() {
    vUv = uv;
    // Billboard on Y: face the camera horizontally so the column never edges out.
    vec3 p = position;
    vec4 mv = modelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0);
    mv.xy += p.xy * vec2(1.0, 1.0);
    gl_Position = projectionMatrix * mv;
  }
`;

const FRAG = /* glsl */ `
  precision highp float;
  uniform float uTime;
  uniform float uHeat;
  varying vec2 vUv;

  void main() {
    if (uHeat <= 0.001) discard;
    // Wobbling vertical bands, rising and fading out with height.
    float w = sin((vUv.y * 9.0 - uTime * 2.4) + sin(vUv.x * 7.0 + uTime * 1.7) * 1.6);
    float band = smoothstep(0.35, 1.0, w);
    float rise = smoothstep(0.0, 0.25, vUv.y) * (1.0 - smoothstep(0.35, 1.0, vUv.y));
    float edge = 1.0 - smoothstep(0.25, 0.5, abs(vUv.x - 0.5));
    float a = band * rise * edge * uHeat * 0.5;
    gl_FragColor = vec4(vec3(1.0, 0.45, 0.2) * a, a);
  }
`;

export interface HeatHaze {
  mesh: THREE.Mesh;
  set(heat: number, timeSec: number): void;
}

const GEO = new THREE.PlaneGeometry(1, 1);

export function createHeatHaze(width: number, height: number): HeatHaze {
  const uniforms = { uTime: { value: 0 }, uHeat: { value: 0 } };
  const mat = new THREE.ShaderMaterial({
    uniforms,
    vertexShader: VERT,
    fragmentShader: FRAG,
    transparent: true,
    depthWrite: false,
    blending: THREE.AdditiveBlending,
  });
  const mesh = new THREE.Mesh(GEO, mat);
  mesh.scale.set(width * 1.3, height * 2.2, 1);
  mesh.position.y = height * 1.1;
  mesh.renderOrder = 5;
  mesh.frustumCulled = false;
  return {
    mesh,
    set(heat: number, timeSec: number) {
      uniforms.uHeat.value = heat;
      uniforms.uTime.value = timeSec;
      mesh.visible = heat > 0.001;
    },
  };
}
