// flight/emissive.js (Lane B) — the one material behind every glowing thing B draws.
//
// ART_BIBLE: gameplay-relevant things glow, and the scene has NO lights (walls fake theirs
// with fresnel). So our primitives are unlit: a core colour with a rim that hots up at
// grazing angles, which reads as a volume against the dark tint instead of a flat decal.
// One material per (colour, rim) pair + InstancedMesh = 1 draw call per enemy/projectile
// type, which is how the whole combat layer fits in its 80-draw budget.
//
// Lives in flight/ rather than combat/ to keep the dependency arrow one-way: combat imports
// flight (it needs the player), flight never imports combat.

import * as THREE from 'three';

// ART_BIBLE §Emissive code — the readability law. Never hand-pick a colour outside this.
export const EMISSIVE = {
  hostile:     { core: 0xff5a2a, rim: 0xffb488 },   // hot amber -> red core
  hostileShot: { core: 0xffe9d0, rim: 0xffffff },   // white-hot
  neutral:     { core: 0x39e6ff, rim: 0xdff2ff },   // cyan (player + flora + interactive)
  pickup:      { core: 0x7dffb0, rim: 0xdaffe9 },
  gate:        { core: 0xb06aff, rim: 0xe6d0ff },
  acid:        { core: 0xc8ff3a, rim: 0xeaffb0 },
};

export function emissiveMat({ core, rim }, { additive = false, opacity = 1 } = {}) {
  return new THREE.ShaderMaterial({
    transparent: opacity < 1 || additive,
    depthWrite: !additive,
    blending: additive ? THREE.AdditiveBlending : THREE.NormalBlending,
    uniforms: {
      uCore: { value: new THREE.Color(core) },
      uRim: { value: new THREE.Color(rim) },
      uOpacity: { value: opacity },
      uFlash: { value: 0 },        // driven on hit: 0..1 blows the whole body to rim colour
    },
    vertexShader: /* glsl */`
      varying vec3 vN; varying vec3 vV;
      void main() {
        // three declares the instanceMatrix attribute for us whenever the drawn object is
        // an InstancedMesh (USE_INSTANCING); this same material still works on a plain Mesh.
        // (Careful: no backticks in here — this is a JS template literal.)
        #ifdef USE_INSTANCING
          mat4 m = modelViewMatrix * instanceMatrix;
        #else
          mat4 m = modelViewMatrix;
        #endif
        vec4 mv = m * vec4(position, 1.0);
        // mat3(m) is only a true normal matrix under uniform scale — every instance we
        // emit is uniformly scaled, and normalize() eats the leftover constant.
        vN = normalize(mat3(m) * normal);
        vV = -mv.xyz;
        gl_Position = projectionMatrix * mv;
      }`,
    fragmentShader: /* glsl */`
      varying vec3 vN; varying vec3 vV;
      uniform vec3 uCore; uniform vec3 uRim; uniform float uOpacity; uniform float uFlash;
      void main() {
        float f = pow(1.0 - abs(dot(normalize(vN), normalize(vV))), 2.0);
        vec3 col = mix(uCore, uRim, f) + uRim * f * 0.6;
        col = mix(col, uRim * 1.6, uFlash);
        gl_FragColor = vec4(col, uOpacity);

        // MANDATORY for any custom ShaderMaterial in this project. ColorManagement is on,
        // so THREE.Color(0xff5a2a) is converted to LINEAR working space on the way in, but
        // a hand-written fragment shader gets no automatic conversion on the way out — it
        // would write linear straight into the sRGB target. Measured: hostile amber landed
        // as rgb(255,26,6) blood-red instead of rgb(255,90,42), quietly breaking the
        // ART_BIBLE emissive readability law. This chunk applies linear -> sRGB.
        #include <colorspace_fragment>
      }`,
  });
}
