// world-atlas.jsx — Atlas in space: stars, drifting smoke/nebula, GLB model.

function initAtlas(container) {
  const scene = new THREE.Scene();
  scene.background = new THREE.Color(0x000000);

  const camera = new THREE.PerspectiveCamera(34, 1, 0.05, 4000);
  camera.position.set(0, 2.6, 12);

  const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
  renderer.setClearColor(0x000000, 1);
  container.appendChild(renderer.domElement);

  const sunDir = new THREE.Vector3(-0.5, 0.7, 0.35).normalize();

  // ══════════════════════════════════════════════════════════════════════════
  // STARFIELD
  // ══════════════════════════════════════════════════════════════════════════
  const starGeo = new THREE.BufferGeometry();
  const STAR_N = 15000;
  const sPos = new Float32Array(STAR_N * 3);
  const sSize = new Float32Array(STAR_N);
  const sBri = new Float32Array(STAR_N);
  const sCol = new Float32Array(STAR_N * 3);
  for (let i = 0; i < STAR_N; i++) {
    let x, y, z, r2;
    do {
      x = Math.random()*2-1; y = Math.random()*2-1; z = Math.random()*2-1;
      r2 = x*x + y*y + z*z;
    } while (r2 > 1 || r2 < 0.001);
    const r = 1600 * (0.5 + Math.random()*0.6);
    const s = Math.sqrt(r2);
    sPos[i*3] = (x/s)*r; sPos[i*3+1] = (y/s)*r; sPos[i*3+2] = (z/s)*r;
    const p = Math.random();
    sSize[i] = p < 0.80 ? 0.4 + Math.random()*0.7 : p < 0.98 ? 1.2 + Math.random()*1.8 : 3.0 + Math.random()*4.5;
    sBri[i] = 0.4 + Math.pow(Math.random(), 2.2) * 1.0;
    const ct = Math.random();
    if (ct < 0.55)      { sCol[i*3]=1.0; sCol[i*3+1]=0.97; sCol[i*3+2]=0.92; }
    else if (ct < 0.78) { sCol[i*3]=0.74; sCol[i*3+1]=0.84; sCol[i*3+2]=1.0; }
    else if (ct < 0.92) { sCol[i*3]=1.0; sCol[i*3+1]=0.72; sCol[i*3+2]=0.55; }
    else                { sCol[i*3]=1.0; sCol[i*3+1]=0.55; sCol[i*3+2]=0.50; }
  }
  starGeo.setAttribute('position', new THREE.BufferAttribute(sPos, 3));
  starGeo.setAttribute('size', new THREE.BufferAttribute(sSize, 1));
  starGeo.setAttribute('aBri', new THREE.BufferAttribute(sBri, 1));
  starGeo.setAttribute('aCol', new THREE.BufferAttribute(sCol, 3));
  const starMat = new THREE.ShaderMaterial({
    uniforms: { uTime: { value: 0 } },
    transparent: true, depthWrite: false, blending: THREE.AdditiveBlending,
    vertexShader: `
      attribute float size; attribute float aBri; attribute vec3 aCol;
      varying float vBri; varying vec3 vCol;
      uniform float uTime;
      void main(){
        vBri = aBri; vCol = aCol;
        vec4 mv = modelViewMatrix * vec4(position,1.0);
        float tw = 0.80 + 0.15*sin(uTime*1.8 + aBri*52.0) + 0.05*sin(uTime*4.7 + aBri*113.0);
        gl_PointSize = size * (320.0 / -mv.z) * tw;
        gl_Position = projectionMatrix * mv;
      }
    `,
    fragmentShader: `
      varying float vBri; varying vec3 vCol;
      void main(){
        vec2 c = gl_PointCoord - 0.5;
        float d = length(c);
        float core = smoothstep(0.42, 0.0, d);
        float halo = smoothstep(0.50, 0.10, d) * 0.4;
        float a = (core + halo) * vBri;
        gl_FragColor = vec4(vCol * a, a);
      }
    `,
  });
  const stars = new THREE.Points(starGeo, starMat);
  scene.add(stars);

  // ══════════════════════════════════════════════════════════════════════════
  // SMOKE / NEBULA — drifting transparent particles around the atlas
  // ══════════════════════════════════════════════════════════════════════════
  const smokeGeo = new THREE.BufferGeometry();
  const SMOKE_N = 600;
  const smPos = new Float32Array(SMOKE_N * 3);
  const smSize = new Float32Array(SMOKE_N);
  const smAlpha = new Float32Array(SMOKE_N);
  for (let i = 0; i < SMOKE_N; i++) {
    // Spread around the atlas position (0, 0.55, 3.4)
    smPos[i*3]   = (Math.random()-0.5) * 30;
    smPos[i*3+1] = (Math.random()-0.5) * 15 + 1;
    smPos[i*3+2] = 3.4 + (Math.random()-0.5) * 30;
    smSize[i]    = 200 + Math.random() * 500;
    smAlpha[i]   = 0.03 + Math.random() * 0.08;
  }
  smokeGeo.setAttribute('position', new THREE.BufferAttribute(smPos, 3));
  smokeGeo.setAttribute('size', new THREE.BufferAttribute(smSize, 1));
  smokeGeo.setAttribute('aAlpha', new THREE.BufferAttribute(smAlpha, 1));
  const smokeMat = new THREE.ShaderMaterial({
    transparent: true, depthWrite: false, blending: THREE.AdditiveBlending,
    uniforms: { uTime: { value: 0 } },
    vertexShader: `
      attribute float size; attribute float aAlpha;
      varying float vA;
      uniform float uTime;
      void main(){
        vA = aAlpha;
        vec3 p = position;
        p.x += sin(uTime*0.04 + position.z*0.08) * 1.5;
        p.y += sin(uTime*0.06 + position.x*0.05) * 0.4;
        vec4 mv = modelViewMatrix * vec4(p, 1.0);
        gl_PointSize = size * (300.0 / -mv.z);
        gl_Position = projectionMatrix * mv;
      }
    `,
    fragmentShader: `
      varying float vA;
      void main(){
        vec2 c = gl_PointCoord - 0.5;
        float d = length(c);
        float a = smoothstep(0.5, 0.0, d) * vA;
        // Warm smoke tint
        vec3 col = vec3(0.7, 0.65, 0.55);
        gl_FragColor = vec4(col * a, a);
      }
    `,
  });
  const smoke = new THREE.Points(smokeGeo, smokeMat);
  scene.add(smoke);

  // ══════════════════════════════════════════════════════════════════════════
  // ATLAS GLB MODEL
  // ══════════════════════════════════════════════════════════════════════════
  const atlasGroup = new THREE.Group();
  atlasGroup.position.set(0, 0.55, 3.4);
  atlasGroup.rotation.y = -0.12;
  scene.add(atlasGroup);

  // Lights for the GLB PBR materials
  const keyLight = new THREE.DirectionalLight(0xfff0e0, 1.2);
  keyLight.position.copy(sunDir).multiplyScalar(10);
  scene.add(keyLight);
  const fillLight = new THREE.AmbientLight(0x808088, 2.5);
  scene.add(fillLight);

  let atlasModel = null;
  const atlasDraco = new THREE.DRACOLoader();
  atlasDraco.setDecoderPath('https://unpkg.com/three@0.147.0/examples/js/libs/draco/');
  const atlasLoader = new THREE.GLTFLoader();
  atlasLoader.setDRACOLoader(atlasDraco);
  atlasLoader.load('atlas_holding_the_earth.glb', (gltf) => {
    atlasModel = gltf.scene;
    const box = new THREE.Box3().setFromObject(atlasModel);
    const size = new THREE.Vector3();
    box.getSize(size);
    const targetHeight = 1.6;
    const s = targetHeight / Math.max(size.y, 1e-3);
    atlasModel.scale.setScalar(s);
    const box2 = new THREE.Box3().setFromObject(atlasModel);
    const center = new THREE.Vector3();
    box2.getCenter(center);
    atlasModel.position.x -= center.x;
    atlasModel.position.y -= center.y;
    atlasModel.position.z -= center.z;
    atlasGroup.add(atlasModel);
  }, undefined, (err) => {
    console.error('Failed to load atlas_holding_the_earth.glb', err);
  });

  // ══════════════════════════════════════════════════════════════════════════
  // Camera: slow dolly toward atlas
  // ══════════════════════════════════════════════════════════════════════════
  const update = (p, t) => {
    p = clamp01(p);
    const e = ease(p);
    const eo = easeOut(p);
    // Phase 1 (0-60%): dolly in from far to medium distance
    // Phase 2 (60-100%): descend below atlas and look up at face (contre-plongée)
    const phase2 = clamp01((p - 0.6) / 0.4);
    const e2 = ease(phase2);
    camera.position.x = Math.sin(t*0.02)*0.35 + lerp(-0.7, 0.0, eo);
    // Y: starts high (3.6), goes to eye level (1.05), then drops below atlas (-0.3)
    camera.position.y = lerp(lerp(2.0, 1.05, e), -0.3, e2);
    // Z: starts closer, then gets very close for the face shot
    camera.position.z = lerp(lerp(8.0, 5.4, eo), 4.2, e2);
    // Look-at: starts at center, then tilts up to face (above the atlas)
    const lookY = lerp(lerp(0.9, 0.55, e), 1.2, e2);
    camera.lookAt(0, lookY, 3.4);
    camera.fov = lerp(34, 40, e2) + Math.sin(t*0.10)*0.25;
    camera.updateProjectionMatrix();
    starMat.uniforms.uTime.value = t;
    smokeMat.uniforms.uTime.value = t;
    stars.rotation.y = t * 0.002;
  };

  const setStyle = () => {};

  const resize = () => {
    const r = container.getBoundingClientRect();
    camera.aspect = r.width / r.height;
    camera.updateProjectionMatrix();
    renderer.setSize(r.width, r.height, false);
  };
  resize();

  const dispose = () => {
    renderer.dispose();
    if (renderer.domElement.parentNode) renderer.domElement.parentNode.removeChild(renderer.domElement);
    if (atlasModel) atlasModel.traverse((o) => {
      if (o.isMesh) {
        o.geometry?.dispose?.();
        if (Array.isArray(o.material)) o.material.forEach((m) => m.dispose?.());
        else o.material?.dispose?.();
      }
    });
    starGeo.dispose(); starMat.dispose();
    smokeGeo.dispose(); smokeMat.dispose();
  };

  return { scene, camera, renderer, update, resize, dispose, setStyle };
}

window.initAtlas = initAtlas;
