import type * as THREE from 'three';
// Small vertex and fragment studies. Deformed normals are an art-direction approximation;
// for close-up production work derive corrected normals or deform a subdivided source mesh.
const vertexBodies: Record<string, string> = {
  wave: 'transformed.x += sin(position.y * 4.0 + webcraftTime * 1.6) * 0.16; transformed.z += cos(position.x * 3.0 + webcraftTime) * 0.1;',
  twist:
    'float a = sin(webcraftTime * 0.6) * position.y * 0.7; mat2 rot = mat2(cos(a), -sin(a), sin(a), cos(a)); transformed.xz = rot * transformed.xz;',
  breathe:
    'transformed += normal * sin(position.y * 5.0 + position.x * 3.0 + webcraftTime * 1.7) * 0.1;',
  'noise-displace':
    'float noise = sin(position.x*5.0+webcraftTime)*cos(position.y*6.0-webcraftTime*.7)*sin(position.z*4.0+webcraftTime*.4); transformed += normal*noise*.22;',
  'ripple-surface':
    'float radial = length(position.xz); transformed += normal*sin(radial*12.0-webcraftTime*2.4)*.1;',
  bend: 'float a=position.y*sin(webcraftTime*.7)*.55; float c=cos(a);float s=sin(a);transformed.xy=mat2(c,-s,s,c)*transformed.xy;',
  inflate:
    'float a=pow(sin(webcraftTime*.65)*.5+.5,2.0); transformed += normal*a*.28;',
};
const fragmentBodies: Record<string, string> = {
  hologram:
    'float bands=pow(sin(webcraftPosition.y*70.0-webcraftTime*3.0)*.5+.5,5.0); float rim=pow(1.0-abs(dot(normal,normalize(vViewPosition))),2.0); outgoingLight=mix(outgoingLight*.2,vec3(.22,.85,.72),clamp(bands*.55+rim*.8,0.0,1.0));',
  dissolve:
    'float n=fract(sin(dot(floor(webcraftPosition*22.0),vec3(12.9898,78.233,37.719)))*43758.5453);float threshold=(sin(webcraftTime*.7)*.5+.5)*.8; if(n<threshold)discard;outgoingLight+=vec3(1.0,.32,.07)*(1.0-smoothstep(0.0,.07,n-threshold));',
  fresnel:
    'float rim=pow(1.0-abs(dot(normal,normalize(vViewPosition))),2.6);outgoingLight+=vec3(.35,.65,.95)*rim*1.5;',
  'scan-band':
    'float scanY=sin(webcraftTime*.65)*1.5;float band=1.0-smoothstep(.0,.13,abs(webcraftPosition.y-scanY));outgoingLight+=vec3(.4,.95,.55)*band*1.8;',
};
export function attachEffect(
  material: THREE.MeshPhysicalMaterial,
  effect: string,
) {
  const time = { value: 0 };
  if (!vertexBodies[effect] && !fragmentBodies[effect]) return time;
  material.onBeforeCompile = (shader) => {
    shader.uniforms.webcraftTime = time;
    shader.vertexShader =
      'uniform float webcraftTime;\nvarying vec3 webcraftPosition;\n' +
      shader.vertexShader;
    shader.vertexShader = shader.vertexShader.replace(
      '#include <begin_vertex>',
      '#include <begin_vertex>\n' +
        (vertexBodies[effect] || '') +
        '\nwebcraftPosition = transformed;',
    );
    if (fragmentBodies[effect]) {
      shader.fragmentShader =
        'uniform float webcraftTime;\nvarying vec3 webcraftPosition;\n' +
        shader.fragmentShader;
      shader.fragmentShader = shader.fragmentShader.replace(
        '#include <opaque_fragment>',
        fragmentBodies[effect] + '\n#include <opaque_fragment>',
      );
    }
  };
  material.customProgramCacheKey = () => `webcraft-2-${effect}`;
  material.needsUpdate = true;
  return time;
}
export const shaderEffectIds = [
  ...Object.keys(vertexBodies),
  ...Object.keys(fragmentBodies),
];
