/**
 * Synchron zum 2D⇄3D-Kamera-Tween: beleuchtete Standard-Materials (u. a. MeshPhong,
 * MeshPhysical) mischen outgoingLight per Uniform mit diffuser Albedo (diffuseColor.rgb) —
 * wirkt in 2D wie flaches/unlit ohne Material-Wechsel.
 *
 * Pro Viewer-Instanz ein Registry `createProjectionFlatRegistry()` — nie global teilen,
 * sonst setzt das zuletzt initialisierte Diagramm die Flatness aller anderen (Race).
 */

/** @returns {{ materials: Set<import('three').Material> }} */
export function createProjectionFlatRegistry() {
  return { materials: new Set() };
}

/**
 * @param {import('three').Material} material
 * @param {{ materials: Set<import('three').Material> }} registry
 */
export function enableProjectionFlatMorph(material, registry) {
  if (!material || material.userData._ceProjFlatMorph || !registry?.materials) return;
  material.userData._ceProjFlatMorph = true;
  material.userData.ceProjectionFlat = { value: 0 };
  registry.materials.add(material);

  const flatU = material.userData.ceProjectionFlat;
  const prev = material.onBeforeCompile;
  material.onBeforeCompile = (shader, renderer) => {
    if (prev) prev(shader, renderer);
    shader.uniforms.ceProjectionFlat = flatU;
    shader.fragmentShader = shader.fragmentShader
      .replace('#include <common>', '#include <common>\nuniform float ceProjectionFlat;')
      .replace(
        '#include <opaque_fragment>',
        'outgoingLight = mix(outgoingLight, diffuseColor.rgb, ceProjectionFlat);\n\t#include <opaque_fragment>',
      );
  };
  material.needsUpdate = true;
}

/**
 * @param {number} value 0 = voll beleuchtet (3D), 1 = flach / ohne Lichteffekt
 * @param {{ materials: Set<import('three').Material> }} registry
 */
export function setProjectionFlatnessAll(value, registry) {
  if (!registry?.materials) return;
  const v = Math.max(0, Math.min(1, Number(value) || 0));
  for (const m of registry.materials) {
    const u = m.userData.ceProjectionFlat;
    if (u) u.value = v;
  }
}

/**
 * @param {number} tEased Kamera-Tween 0…1
 * @param {'2d' | '3d'} targetProjection
 * @param {{ materials: Set<import('three').Material> }} registry
 */
export function projectionTweenToFlatness(tEased, targetProjection, registry) {
  const t = Math.max(0, Math.min(1, Number(tEased) || 0));
  const flat = targetProjection === '2d' ? t : 1 - t;
  setProjectionFlatnessAll(flat, registry);
}
