'use strict';

/* ═══════════════════════════════════════════════
   CrystalSim — 3D crystal lattice (Three.js)
   NaCl, Diamond, BCC metal, FCC metal
   ═══════════════════════════════════════════════ */

class CrystalSim {
  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.5));
    const dir = new THREE.DirectionalLight(0xffffff, 0.8);
    dir.position.set(5, 8, 6);
    this.scene.add(dir);
    const pt = new THREE.PointLight(0x9B5DE5, 0.4, 50);
    pt.position.set(-4, 3, 5);
    this.scene.add(pt);

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

    /* orbit-like manual controls */
    this._drag   = false;
    this._prevX  = 0;
    this._prevY  = 0;
    this._rotY   = 0.6;
    this._rotX   = 0.4;
    this._dist   = 12;
    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(5, Math.min(30, 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._lattice = 'nacl';
    this._group = new THREE.Group();
    this.scene.add(this._group);

    this._buildLattice('nacl');
    this.fit();
    this.play();
  }

  /* ── public ── */
  setLattice(type) {
    this._lattice = type;
    if (window.LabFX) LabFX.sound.play('chime', { pitch: 1.2, volume: 0.3 });
    this._buildLattice(type);
  }

  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; }

  /* ── lattice builders ── */
  _buildLattice(type) {
    // clear
    while (this._group.children.length) {
      const c = this._group.children[0];
      c.geometry?.dispose(); c.material?.dispose();
      this._group.remove(c);
    }

    const builders = {
      nacl:    () => this._buildNaCl(),
      diamond: () => this._buildDiamond(),
      bcc:     () => this._buildBCC(),
      fcc:     () => this._buildFCC(),
    };
    (builders[type] || builders.nacl)();
  }

  _sphere(r, color) {
    const geo = new THREE.SphereGeometry(r, 24, 24);
    const mat = new THREE.MeshPhysicalMaterial({
      color, metalness: 0.1, roughness: 0.3,
      clearcoat: 0.6, clearcoatRoughness: 0.2,
    });
    return new THREE.Mesh(geo, mat);
  }

  _bond(from, to, color = 0x555555) {
    const dir = new THREE.Vector3().subVectors(to, from);
    const len = dir.length();
    const geo = new THREE.CylinderGeometry(0.04, 0.04, len, 8);
    const mat = new THREE.MeshStandardMaterial({ color, opacity: 0.5, transparent: true });
    const mesh = new THREE.Mesh(geo, mat);
    mesh.position.copy(from).add(dir.multiplyScalar(0.5));
    mesh.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), dir.normalize());
    return mesh;
  }

  _buildNaCl() {
    const n = 3; // 3×3×3 unit cells
    const a = 2.0; // lattice constant
    const offset = -(n - 1) * a / 2;
    const positions = [];

    for (let i = 0; i < n; i++)
      for (let j = 0; j < n; j++)
        for (let k = 0; k < n; k++) {
          const x = offset + i * a, y = offset + j * a, z = offset + k * a;
          const isNa = (i + j + k) % 2 === 0;
          const s = this._sphere(isNa ? 0.28 : 0.38, isNa ? 0x9B5DE5 : 0x06D6E0);
          s.position.set(x, y, z);
          this._group.add(s);
          positions.push({ x, y, z });
        }

    // bonds to nearest neighbors
    for (let i = 0; i < positions.length; i++)
      for (let j = i + 1; j < positions.length; j++) {
        const dx = positions[i].x - positions[j].x;
        const dy = positions[i].y - positions[j].y;
        const dz = positions[i].z - positions[j].z;
        const d2 = dx * dx + dy * dy + dz * dz;
        if (Math.abs(d2 - a * a) < 0.01) {
          const b = this._bond(
            new THREE.Vector3(positions[i].x, positions[i].y, positions[i].z),
            new THREE.Vector3(positions[j].x, positions[j].y, positions[j].z),
            0x444466
          );
          this._group.add(b);
        }
      }
  }

  _buildDiamond() {
    const a = 2.5;
    const basis = [
      [0, 0, 0], [0.5, 0.5, 0], [0.5, 0, 0.5], [0, 0.5, 0.5],
      [0.25, 0.25, 0.25], [0.75, 0.75, 0.25], [0.75, 0.25, 0.75], [0.25, 0.75, 0.75],
    ];
    const n = 2;
    const offset = -(n * a) / 2;
    const allPos = [];

    for (let ci = 0; ci < n; ci++)
      for (let cj = 0; cj < n; cj++)
        for (let ck = 0; ck < n; ck++)
          for (const [bx, by, bz] of basis) {
            const x = offset + (ci + bx) * a;
            const y = offset + (cj + by) * a;
            const z = offset + (ck + bz) * a;
            const s = this._sphere(0.22, 0x34d399);
            s.position.set(x, y, z);
            this._group.add(s);
            allPos.push(new THREE.Vector3(x, y, z));
          }

    // bonds
    const bondLen = a * Math.sqrt(3) / 4;
    const tol = bondLen * 0.15;
    for (let i = 0; i < allPos.length; i++)
      for (let j = i + 1; j < allPos.length; j++) {
        const d = allPos[i].distanceTo(allPos[j]);
        if (Math.abs(d - bondLen) < tol) {
          this._group.add(this._bond(allPos[i], allPos[j], 0x228866));
        }
      }
  }

  _buildBCC() {
    const a = 2.2, n = 3;
    const offset = -(n - 1) * a / 2;
    const allPos = [];

    for (let i = 0; i < n; i++)
      for (let j = 0; j < n; j++)
        for (let k = 0; k < n; k++) {
          // corner atoms
          const x1 = offset + i * a, y1 = offset + j * a, z1 = offset + k * a;
          const s1 = this._sphere(0.3, 0xF15BB5);
          s1.position.set(x1, y1, z1);
          this._group.add(s1);
          allPos.push(new THREE.Vector3(x1, y1, z1));

          // body center (except last cell in each dimension)
          if (i < n - 1 && j < n - 1 && k < n - 1) {
            const cx = x1 + a / 2, cy = y1 + a / 2, cz = z1 + a / 2;
            const s2 = this._sphere(0.3, 0xF59E0B);
            s2.position.set(cx, cy, cz);
            this._group.add(s2);
            allPos.push(new THREE.Vector3(cx, cy, cz));
          }
        }

    // bonds
    const bondLen = a * Math.sqrt(3) / 2;
    const tol = bondLen * 0.1;
    for (let i = 0; i < allPos.length; i++)
      for (let j = i + 1; j < allPos.length; j++) {
        const d = allPos[i].distanceTo(allPos[j]);
        if (Math.abs(d - bondLen) < tol) {
          this._group.add(this._bond(allPos[i], allPos[j], 0x664444));
        }
      }
  }

  _buildFCC() {
    const a = 2.4, n = 3;
    const offset = -(n - 1) * a / 2;
    const allPos = [];

    for (let i = 0; i < n; i++)
      for (let j = 0; j < n; j++)
        for (let k = 0; k < n; k++) {
          const x = offset + i * a, y = offset + j * a, z = offset + k * a;
          // corner
          const s = this._sphere(0.25, 0x60a5fa);
          s.position.set(x, y, z);
          this._group.add(s);
          allPos.push(new THREE.Vector3(x, y, z));

          // face centers (only for cell interiors)
          if (i < n - 1 && j < n - 1) {
            const f1 = this._sphere(0.25, 0x60a5fa);
            f1.position.set(x + a / 2, y + a / 2, z);
            this._group.add(f1);
            allPos.push(new THREE.Vector3(x + a / 2, y + a / 2, z));
          }
          if (i < n - 1 && k < n - 1) {
            const f2 = this._sphere(0.25, 0x60a5fa);
            f2.position.set(x + a / 2, y, z + a / 2);
            this._group.add(f2);
            allPos.push(new THREE.Vector3(x + a / 2, y, z + a / 2));
          }
          if (j < n - 1 && k < n - 1) {
            const f3 = this._sphere(0.25, 0x60a5fa);
            f3.position.set(x, y + a / 2, z + a / 2);
            this._group.add(f3);
            allPos.push(new THREE.Vector3(x, y + a / 2, z + a / 2));
          }
        }

    // bonds to nearest neighbors (a/√2)
    const bondLen = a / Math.SQRT2;
    const tol = bondLen * 0.1;
    for (let i = 0; i < allPos.length; i++)
      for (let j = i + 1; j < allPos.length; j++) {
        const d = allPos[i].distanceTo(allPos[j]);
        if (Math.abs(d - bondLen) < tol) {
          this._group.add(this._bond(allPos[i], allPos[j], 0x334466));
        }
      }
  }

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

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

    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 crystalSim = null;
  function _openCrystal() {
    document.getElementById('sim-topbar-title').textContent = 'Кристаллическая решётка';
    _simShow('sim-crystal');
    requestAnimationFrame(() => requestAnimationFrame(() => {
      if (!crystalSim) {
        crystalSim = new CrystalSim(document.getElementById('crystal-container'));
      } else {
        crystalSim.fit();
        crystalSim.play();
      }
    }));
  }
  function setCrystal(type, btn) {
    document.querySelectorAll('.crystal-type-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 (crystalSim) crystalSim.setLattice(type);
  }

  /* ── molecular orbitals (3D) ── */
