/**
 * LANE-RENDER — remove-mode hover.
 *
 * Two reads at once: a hazard-striped decal on the machine's footprint (this is the
 * ground you are about to clear) and a red wash over its volume (this is the thing that
 * is going away). Stripes because the codex dresses everything horrifying as routine
 * industrial equipment — demolition should look like a permit, not an explosion.
 *
 * Driven by the `__remove` ghost sentinel; see ghost.ts.
 */
import * as THREE from 'three';

const DEMOLITION = 0xff3b30;

const DECAL_VERT = /* glsl */ `
  varying vec2 vUv;
  void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }
`;

const DECAL_FRAG = /* glsl */ `
  precision highp float;
  uniform float uTime;
  uniform vec2 uScale;   // footprint in tiles, so stripes don't stretch on big machines
  uniform vec3 uColor;
  varying vec2 vUv;

  void main() {
    // Border: constant thickness regardless of footprint.
    vec2 d = min(vUv, 1.0 - vUv) * uScale;
    float edge = min(d.x, d.y);
    float border = 1.0 - smoothstep(0.04, 0.10, edge);

    // Hazard stripes, crawling.
    vec2 p = vUv * uScale;
    float s = fract((p.x + p.y) * 1.6 - uTime * 0.5);
    float stripe = step(0.5, s);

    float a = max(border, stripe * 0.28);
    if (a < 0.01) discard;
    gl_FragColor = vec4(uColor, a * 0.85);
  }
`;

export class RemoveHover {
  readonly group = new THREE.Group();
  private decal: THREE.Mesh;
  private wash: THREE.Mesh;
  private uniforms = {
    uTime: { value: 0 },
    uScale: { value: new THREE.Vector2(1, 1) },
    uColor: { value: new THREE.Color(DEMOLITION) },
  };

  constructor() {
    this.group.name = 'removeHover';
    this.group.visible = false;
    this.group.renderOrder = 11;

    this.decal = new THREE.Mesh(
      new THREE.PlaneGeometry(1, 1),
      new THREE.ShaderMaterial({
        uniforms: this.uniforms,
        vertexShader: DECAL_VERT,
        fragmentShader: DECAL_FRAG,
        transparent: true,
        depthWrite: false,
      }),
    );
    this.decal.rotation.x = -Math.PI / 2;
    this.decal.position.y = 0.02; // just clear of the ground plane
    this.group.add(this.decal);

    this.wash = new THREE.Mesh(
      new THREE.BoxGeometry(1, 1, 1),
      new THREE.MeshBasicMaterial({
        color: DEMOLITION,
        transparent: true,
        opacity: 0.3,
        depthWrite: false,
      }),
    );
    this.group.add(this.wash);
  }

  hide(): void {
    this.group.visible = false;
  }

  /**
   * Frame the machine we're about to demolish. `w`/`d` are its ROTATED footprint, so a
   * 2x3 turned east reads as 3x2 — the decal has to match what's actually occupied.
   */
  show(cx: number, cz: number, w: number, d: number, height: number, timeSec: number): void {
    this.group.visible = true;
    this.uniforms.uTime.value = timeSec;
    this.uniforms.uScale.value.set(w, d);

    this.decal.scale.set(w, d, 1);
    this.decal.position.set(cx, 0.02, cz);

    this.wash.scale.set(w * 0.94, height, d * 0.94);
    this.wash.position.set(cx, height / 2, cz);
  }
}
