'use strict';

/* ═══════════════════════════════════════════════
   OrbitalsSim — 3D molecular orbitals (Three.js)
   s, p, d orbitals + H₂ / H₂O molecular bonding
   ═══════════════════════════════════════════════ */

class OrbitalsSim {
  constructor(container) {
    this.container = container;
    this._running = false;

    /* Three.js */
    this.scene    = new THREE.Scene();
    this.camera   = new THREE.PerspectiveCamera(50, 1, 0.1, 200);
    this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    this.renderer.setClearColor(0x0D0D1A, 1);
    container.appendChild(this.renderer.domElement);

    /* lighting */
    this.scene.add(new THREE.AmbientLight(0xffffff, 0.45));
    const dir = new THREE.DirectionalLight(0xffffff, 0.7);
    dir.position.set(5, 8, 6);
    this.scene.add(dir);
    const pt = new THREE.PointLight(0x9B5DE5, 0.3, 50);
    pt.position.set(-4, 3, 5);
    this.scene.add(pt);

    this.camera.position.set(6, 4, 6);
    this.camera.lookAt(0, 0, 0);

    /* orbit controls (manual) */
    this._drag  = false;
    this._prevX = 0;
    this._prevY = 0;
    this._rotY  = 0.6;
    this._rotX  = 0.3;
    this._dist  = 8;
    this._autoSpin = true;

    const el = this.renderer.domElement;
    el.style.cursor = 'grab';
    el.addEventListener('pointerdown', e => { this._drag = true; this._prevX = e.clientX; this._prevY = e.clientY; this._autoSpin = false; el.style.cursor = 'grabbing'; });
    window.addEventListener('pointerup', () => { this._drag = false; el.style.cursor = 'grab'; });
    window.addEventListener('pointermove', e => {
      if (!this._drag) return;
      this._rotY += (e.clientX - this._prevX) * 0.008;
      this._rotX += (e.clientY - this._prevY) * 0.008;
      this._rotX = Math.max(-1.4, Math.min(1.4, this._rotX));
      this._prevX = e.clientX; this._prevY = e.clientY;
    });
    el.addEventListener('wheel', e => {
      e.preventDefault();
      this._dist = Math.max(3, Math.min(20, this._dist + e.deltaY * 0.02));
    }, { passive: false });

    /* touch */
    el.addEventListener('touchstart', e => {
      if (e.touches.length === 1) {
        this._drag = true; this._prevX = e.touches[0].clientX; this._prevY = e.touches[0].clientY; this._autoSpin = false;
      }
    }, { passive: true });
    el.addEventListener('touchmove', e => {
      if (!this._drag || e.touches.length !== 1) return;
      const t = e.touches[0];
      this._rotY += (t.clientX - this._prevX) * 0.008;
      this._rotX += (t.clientY - this._prevY) * 0.008;
      this._rotX = Math.max(-1.4, Math.min(1.4, this._rotX));
      this._prevX = t.clientX; this._prevY = t.clientY;
    }, { passive: true });
    el.addEventListener('touchend', () => { this._drag = false; });

    /* resize */
    this._ro = new ResizeObserver(() => this.fit());
    this._ro.observe(container);

    /* state */
    this._mode = 's';
    this._group = new THREE.Group();
    this.scene.add(this._group);

    this._buildOrbital('s');
    this.fit();
    this.play();
  }

  /* ── public ── */
  setMode(mode) {
    this._mode = mode;
    if (window.LabFX) LabFX.sound.play('whoosh', { pitch: 1.1, volume: 0.3 });
    this._buildOrbital(mode);
  }

  fit() {
    const w = this.container.clientWidth  || 600;
    const h = this.container.clientHeight || 400;
    this.camera.aspect = w / h;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(w, h);
  }

  play()  { if (!this._running) { this._running = true; this._loop(); } }
  stop()  { this._running = false; }
  pause() { this._running = false; }

  /* ── clear scene group ── */
  _clear() {
    while (this._group.children.length) {
      const c = this._group.children[0];
      if (c.geometry) c.geometry.dispose();
      if (c.material) {
        if (Array.isArray(c.material)) c.material.forEach(m => m.dispose());
        else c.material.dispose();
      }
      this._group.remove(c);
    }
  }

  /* ── orbital builders ── */
  _buildOrbital(mode) {
    this._clear();
    const b = {
      s:    () => this._buildS(),
      p:    () => this._buildP(),
      d:    () => this._buildD(),
      h2:   () => this._buildH2(),
      h2o:  () => this._buildH2O(),
    };
    (b[mode] || b.s)();
  }

  /* nucleus dot */
  _nucleus(pos, color = 0xffffff) {
    const geo = new THREE.SphereGeometry(0.12, 16, 16);
    const mat = new THREE.MeshStandardMaterial({ color, emissive: color, emissiveIntensity: 0.5 });
    const m = new THREE.Mesh(geo, mat);
    m.position.copy(pos);
    this._group.add(m);
    return m;
  }

  /* cloud lobe (ellipsoid) */
  _lobe(pos, scale, color, opacity = 0.3) {
    const geo = new THREE.SphereGeometry(1, 32, 32);
    const mat = new THREE.MeshPhysicalMaterial({
      color, transparent: true, opacity,
      metalness: 0, roughness: 0.6,
      clearcoat: 0.3, side: THREE.DoubleSide,
      depthWrite: false,
    });
    const mesh = new THREE.Mesh(geo, mat);
    mesh.position.copy(pos);
    mesh.scale.set(scale.x, scale.y, scale.z);
    this._group.add(mesh);
    return mesh;
  }

  /* particle cloud (points) */
  _cloud(center, radius, count, color) {
    const positions = new Float32Array(count * 3);
    for (let i = 0; i < count; i++) {
      // gaussian distribution
      const r = radius * Math.pow(Math.random(), 0.33);
      const theta = Math.random() * Math.PI * 2;
      const phi = Math.acos(2 * Math.random() - 1);
      positions[i * 3]     = center.x + r * Math.sin(phi) * Math.cos(theta);
      positions[i * 3 + 1] = center.y + r * Math.sin(phi) * Math.sin(theta);
      positions[i * 3 + 2] = center.z + r * Math.cos(phi);
    }
    const geo = new THREE.BufferGeometry();
    geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
    const mat = new THREE.PointsMaterial({ color, size: 0.04, transparent: true, opacity: 0.6, depthWrite: false });
    const pts = new THREE.Points(geo, mat);
    this._group.add(pts);
    return pts;
  }

  /* ── s orbital: spherical ── */
  _buildS() {
    this._nucleus(new THREE.Vector3(0, 0, 0));
    this._lobe(new THREE.Vector3(0, 0, 0), new THREE.Vector3(1.5, 1.5, 1.5), 0x9B5DE5, 0.18);
    this._cloud(new THREE.Vector3(0, 0, 0), 1.5, 2000, 0x9B5DE5);
  }

  /* ── p orbitals: 3 dumbbell shapes ── */
  _buildP() {
    this._nucleus(new THREE.Vector3(0, 0, 0));

    // px (red)
    this._lobe(new THREE.Vector3(1.2, 0, 0),  new THREE.Vector3(0.7, 0.5, 0.5), 0xF15BB5, 0.25);
    this._lobe(new THREE.Vector3(-1.2, 0, 0), new THREE.Vector3(0.7, 0.5, 0.5), 0xF15BB5, 0.25);

    // py (green)
    this._lobe(new THREE.Vector3(0, 1.2, 0),  new THREE.Vector3(0.5, 0.7, 0.5), 0x34d399, 0.25);
    this._lobe(new THREE.Vector3(0, -1.2, 0), new THREE.Vector3(0.5, 0.7, 0.5), 0x34d399, 0.25);

    // pz (blue)
    this._lobe(new THREE.Vector3(0, 0, 1.2),  new THREE.Vector3(0.5, 0.5, 0.7), 0x60a5fa, 0.25);
    this._lobe(new THREE.Vector3(0, 0, -1.2), new THREE.Vector3(0.5, 0.5, 0.7), 0x60a5fa, 0.25);

    // axis labels
    this._addLabel('px', 2, 0, 0, 0xF15BB5);
    this._addLabel('py', 0, 2, 0, 0x34d399);
    this._addLabel('pz', 0, 0, 2, 0x60a5fa);
  }

  /* ── d orbital: dxy cloverleaf ── */
  _buildD() {
    this._nucleus(new THREE.Vector3(0, 0, 0));

    // four lobes in xy plane
    const r = 1.1, s = 0.45;
    const angles = [Math.PI / 4, 3 * Math.PI / 4, 5 * Math.PI / 4, 7 * Math.PI / 4];
    const colors = [0xF59E0B, 0x9B5DE5, 0xF59E0B, 0x9B5DE5]; // alternating sign

    for (let i = 0; i < 4; i++) {
      const x = r * Math.cos(angles[i]);
      const y = r * Math.sin(angles[i]);
      this._lobe(new THREE.Vector3(x, y, 0), new THREE.Vector3(s, s, s * 0.6), colors[i], 0.28);
    }

    // dz² torus + lobes
    this._lobe(new THREE.Vector3(0, 0, 1.3),  new THREE.Vector3(0.35, 0.35, 0.6), 0x06D6E0, 0.2);
    this._lobe(new THREE.Vector3(0, 0, -1.3), new THREE.Vector3(0.35, 0.35, 0.6), 0x06D6E0, 0.2);
    // torus ring
    const tGeo = new THREE.TorusGeometry(0.7, 0.18, 16, 32);
    const tMat = new THREE.MeshPhysicalMaterial({ color: 0x06D6E0, transparent: true, opacity: 0.15, side: THREE.DoubleSide, depthWrite: false });
    const torus = new THREE.Mesh(tGeo, tMat);
    this._group.add(torus);

    this._addLabel('d_{xy}', 1.8, 1.8, 0, 0xF59E0B);
    this._addLabel('d_{z²}', 0, 0, 2.2, 0x06D6E0);
  }

  /* ── H₂ sigma bond ── */
  _buildH2() {
    const sep = 1.5;
    this._nucleus(new THREE.Vector3(-sep / 2, 0, 0), 0xffffff);
    this._nucleus(new THREE.Vector3(sep / 2, 0, 0), 0xffffff);

    // individual 1s orbitals (faded)
    this._lobe(new THREE.Vector3(-sep / 2, 0, 0), new THREE.Vector3(0.8, 0.8, 0.8), 0x9B5DE5, 0.1);
    this._lobe(new THREE.Vector3(sep / 2, 0, 0),  new THREE.Vector3(0.8, 0.8, 0.8), 0x9B5DE5, 0.1);

    // bonding σ (overlap region — elongated ellipsoid)
    this._lobe(new THREE.Vector3(0, 0, 0), new THREE.Vector3(1.4, 0.6, 0.6), 0x06D6E0, 0.22);
    this._cloud(new THREE.Vector3(0, 0, 0), 1.0, 3000, 0x06D6E0);

    // bond line
    const bGeo = new THREE.CylinderGeometry(0.03, 0.03, sep, 8);
    const bMat = new THREE.MeshStandardMaterial({ color: 0xffffff, emissive: 0xffffff, emissiveIntensity: 0.3 });
    const bond = new THREE.Mesh(bGeo, bMat);
    bond.rotation.z = Math.PI / 2;
    this._group.add(bond);

    this._addLabel('H', -sep / 2 - 0.5, 0.6, 0, 0xffffff);
    this._addLabel('H', sep / 2 + 0.3, 0.6, 0, 0xffffff);
    this._addLabel('σ', 0, -0.9, 0, 0x06D6E0);
  }

  /* ── H₂O bent molecule ── */
  _buildH2O() {
    const angle = 104.5 * Math.PI / 180;
    const bondLen = 1.6;

    const oPos = new THREE.Vector3(0, 0, 0);
    const h1 = new THREE.Vector3(-bondLen * Math.sin(angle / 2), -bondLen * Math.cos(angle / 2), 0);
    const h2 = new THREE.Vector3(bondLen * Math.sin(angle / 2), -bondLen * Math.cos(angle / 2), 0);

    // nuclei
    const oNuc = this._nucleus(oPos, 0xF15BB5);
    oNuc.scale.set(1.5, 1.5, 1.5);
    this._nucleus(h1, 0xffffff);
    this._nucleus(h2, 0xffffff);

    // O lone pairs (above)
    this._lobe(new THREE.Vector3(-0.5, 0.8, 0), new THREE.Vector3(0.4, 0.5, 0.35), 0xF59E0B, 0.2);
    this._lobe(new THREE.Vector3(0.5, 0.8, 0),  new THREE.Vector3(0.4, 0.5, 0.35), 0xF59E0B, 0.2);

    // O-H σ bonds (electron density)
    const mid1 = new THREE.Vector3().addVectors(oPos, h1).multiplyScalar(0.5);
    const mid2 = new THREE.Vector3().addVectors(oPos, h2).multiplyScalar(0.5);
    this._lobe(mid1, new THREE.Vector3(0.4, 0.7, 0.35), 0x06D6E0, 0.2);
    this._lobe(mid2, new THREE.Vector3(0.4, 0.7, 0.35), 0x06D6E0, 0.2);

    // bond lines
    for (const hPos of [h1, h2]) {
      const d = new THREE.Vector3().subVectors(hPos, oPos);
      const len = d.length();
      const bGeo = new THREE.CylinderGeometry(0.04, 0.04, len, 8);
      const bMat = new THREE.MeshStandardMaterial({ color: 0xaaaaaa });
      const bond = new THREE.Mesh(bGeo, bMat);
      bond.position.copy(oPos).add(d.clone().multiplyScalar(0.5));
      bond.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), d.normalize());
      this._group.add(bond);
    }

    // electron cloud
    this._cloud(oPos, 1.2, 1500, 0xF15BB5);

    this._addLabel('O', 0.3, 0.3, 0, 0xF15BB5);
    this._addLabel('H', h1.x - 0.4, h1.y - 0.3, 0, 0xffffff);
    this._addLabel('H', h2.x + 0.2, h2.y - 0.3, 0, 0xffffff);
    this._addLabel('104.5°', 0, -0.5, 0, 0x888888);
  }

  /* ── text label (using sprite) ── */
  _addLabel(text, x, y, z, color) {
    const canvas = document.createElement('canvas');
    canvas.width = 128; canvas.height = 48;
    const ctx = canvas.getContext('2d');
    ctx.font = 'bold 28px Manrope, sans-serif';
    ctx.fillStyle = '#' + color.toString(16).padStart(6, '0');
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(text, 64, 24);

    const tex = new THREE.CanvasTexture(canvas);
    const mat = new THREE.SpriteMaterial({ map: tex, transparent: true, depthWrite: false });
    const sprite = new THREE.Sprite(mat);
    sprite.position.set(x, y, z);
    sprite.scale.set(1, 0.4, 1);
    this._group.add(sprite);
  }

  /* ── animation ── */
  _loop() {
    if (!this._running) return;
    requestAnimationFrame(() => this._loop());

    if (this._autoSpin) this._rotY += 0.004;

    this.camera.position.set(
      this._dist * Math.sin(this._rotY) * Math.cos(this._rotX),
      this._dist * Math.sin(this._rotX),
      this._dist * Math.cos(this._rotY) * Math.cos(this._rotX)
    );
    this.camera.lookAt(0, 0, 0);

    this.renderer.render(this.scene, this.camera);
  }
}

/* ─── lab UI init ─────────────────────────────────── */
  var orbitalsSim = null;
  function _openOrbitals() {
    document.getElementById('sim-topbar-title').textContent = 'Молекулярные орбитали';
    _simShow('sim-orbitals');
    requestAnimationFrame(() => requestAnimationFrame(() => {
      if (!orbitalsSim) {
        orbitalsSim = new OrbitalsSim(document.getElementById('orbitals-container'));
      } else {
        orbitalsSim.fit();
        orbitalsSim.play();
      }
    }));
  }
  function setOrbital(mode, btn) {
    document.querySelectorAll('.orbital-mode-btn').forEach(b => { b.classList.remove('active'); b.style.borderColor = ''; b.style.color = ''; });
    btn.classList.add('active');
    btn.style.borderColor = '#9B5DE5'; btn.style.color = '#9B5DE5';
    if (orbitalsSim) orbitalsSim.setMode(mode);
  }

  /* ── stereometry 3D ── */
