/* Circular Gallery — adapted from the provided OGL implementation.
   OGL classes are read from window.__OGL (loaded via an ES-module shim).
   No Title/text overlays are rendered. */

function debounce(func, wait) {
  let timeout;
  return function (...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(this, args), wait);
  };
}

function lerp(p1, p2, t) {
  return p1 + (p2 - p1) * t;
}

function makeMediaClass(OGL) {
  const { Mesh, Program, Texture } = OGL;

  return class Media {
    constructor({ geometry, gl, image, index, length, renderer, scene, screen, viewport, bend, borderRadius = 0 }) {
      this.extra = 0;
      this.geometry = geometry;
      this.gl = gl;
      this.image = image;
      this.index = index;
      this.length = length;
      this.renderer = renderer;
      this.scene = scene;
      this.screen = screen;
      this.viewport = viewport;
      this.bend = bend;
      this.borderRadius = borderRadius;
      this.createShader();
      this.createMesh();
      this.onResize();
    }
    createShader() {
      const texture = new Texture(this.gl, { generateMipmaps: true });
      this.program = new Program(this.gl, {
        depthTest: false,
        depthWrite: false,
        vertex: `
          precision highp float;
          attribute vec3 position;
          attribute vec2 uv;
          uniform mat4 modelViewMatrix;
          uniform mat4 projectionMatrix;
          uniform float uTime;
          uniform float uSpeed;
          varying vec2 vUv;
          void main() {
            vUv = uv;
            vec3 p = position;
            p.z = (sin(p.x * 4.0 + uTime) * 1.5 + cos(p.y * 2.0 + uTime) * 1.5) * (0.1 + uSpeed * 0.5);
            gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 1.0);
          }
        `,
        fragment: `
          precision highp float;
          uniform vec2 uImageSizes;
          uniform vec2 uPlaneSizes;
          uniform sampler2D tMap;
          uniform float uBorderRadius;
          varying vec2 vUv;
          float roundedBoxSDF(vec2 p, vec2 b, float r) {
            vec2 d = abs(p) - b;
            return length(max(d, vec2(0.0))) + min(max(d.x, d.y), 0.0) - r;
          }
          void main() {
            vec2 ratio = vec2(
              min((uPlaneSizes.x / uPlaneSizes.y) / (uImageSizes.x / uImageSizes.y), 1.0),
              min((uPlaneSizes.y / uPlaneSizes.x) / (uImageSizes.y / uImageSizes.x), 1.0)
            );
            vec2 uv = vec2(vUv.x * ratio.x + (1.0 - ratio.x) * 0.5, vUv.y * ratio.y + (1.0 - ratio.y) * 0.5);
            vec4 color = texture2D(tMap, uv);
            float d = roundedBoxSDF(vUv - 0.5, vec2(0.5 - uBorderRadius), uBorderRadius);
            float edgeSmooth = 0.002;
            float alpha = 1.0 - smoothstep(-edgeSmooth, edgeSmooth, d);
            gl_FragColor = vec4(color.rgb, alpha);
          }
        `,
        uniforms: {
          tMap: { value: texture },
          uPlaneSizes: { value: [0, 0] },
          uImageSizes: { value: [0, 0] },
          uSpeed: { value: 0 },
          uTime: { value: 100 * Math.random() },
          uBorderRadius: { value: this.borderRadius }
        },
        transparent: true
      });
      const img = new Image();
      img.crossOrigin = 'anonymous';
      img.src = this.image;
      img.onload = () => {
        texture.image = img;
        this.program.uniforms.uImageSizes.value = [img.naturalWidth, img.naturalHeight];
      };
    }
    createMesh() {
      this.plane = new Mesh(this.gl, { geometry: this.geometry, program: this.program });
      this.plane.setParent(this.scene);
    }
    update(scroll, direction) {
      this.plane.position.x = this.x - scroll.current - this.extra;
      const x = this.plane.position.x;
      const H = this.viewport.width / 2;
      if (this.bend === 0) {
        this.plane.position.y = 0;
        this.plane.rotation.z = 0;
      } else {
        const B_abs = Math.abs(this.bend);
        const R = (H * H + B_abs * B_abs) / (2 * B_abs);
        const effectiveX = Math.min(Math.abs(x), H);
        const arc = R - Math.sqrt(R * R - effectiveX * effectiveX);
        if (this.bend > 0) {
          this.plane.position.y = -arc;
          this.plane.rotation.z = -Math.sign(x) * Math.asin(effectiveX / R);
        } else {
          this.plane.position.y = arc;
          this.plane.rotation.z = Math.sign(x) * Math.asin(effectiveX / R);
        }
      }
      this.speed = scroll.current - scroll.last;
      this.program.uniforms.uTime.value += 0.04;
      this.program.uniforms.uSpeed.value = this.speed;
      const planeOffset = this.plane.scale.x / 2;
      const viewportOffset = this.viewport.width / 2;
      this.isBefore = this.plane.position.x + planeOffset < -viewportOffset;
      this.isAfter = this.plane.position.x - planeOffset > viewportOffset;
      if (direction === 'right' && this.isBefore) {
        this.extra -= this.widthTotal;
        this.isBefore = this.isAfter = false;
      }
      if (direction === 'left' && this.isAfter) {
        this.extra += this.widthTotal;
        this.isBefore = this.isAfter = false;
      }
    }
    onResize({ screen, viewport } = {}) {
      if (screen) this.screen = screen;
      if (viewport) {
        this.viewport = viewport;
        if (this.plane.program.uniforms.uViewportSizes) {
          this.plane.program.uniforms.uViewportSizes.value = [this.viewport.width, this.viewport.height];
        }
      }
      this.scale = this.screen.height / 1500;
      this.plane.scale.y = (this.viewport.height * (900 * this.scale)) / this.screen.height;
      this.plane.scale.x = (this.viewport.width * (700 * this.scale)) / this.screen.width;
      this.plane.program.uniforms.uPlaneSizes.value = [this.plane.scale.x, this.plane.scale.y];
      this.padding = 2;
      this.width = this.plane.scale.x + this.padding;
      this.widthTotal = this.width * this.length;
      this.x = this.width * this.index;
    }
  };
}

function makeAppClass(OGL) {
  const { Camera, Plane, Renderer, Transform } = OGL;
  const Media = makeMediaClass(OGL);

  return class App {
    constructor(container, { items, bend, borderRadius = 0, scrollSpeed = 2, scrollEase = 0.05 } = {}) {
      document.documentElement.classList.remove('no-js');
      this.container = container;
      this.scrollSpeed = scrollSpeed;
      this.scroll = { ease: scrollEase, current: 0, target: 0, last: 0 };
      this.onCheckDebounce = debounce(this.onCheck, 200);
      this.autoScrollSpeed = 0.04;
      this.userActive = false;
      this.userActiveTimer = null;
      this.createRenderer();
      this.createCamera();
      this.createScene();
      this.onResize();
      this.createGeometry();
      this.createMedias(items, bend, borderRadius);
      this.update();
      this.addEventListeners();
    }
    createRenderer() {
      this.renderer = new Renderer({ alpha: true, antialias: true, dpr: Math.min(window.devicePixelRatio || 1, 2) });
      this.gl = this.renderer.gl;
      this.gl.clearColor(0, 0, 0, 0);
      this.container.appendChild(this.gl.canvas);
    }
    createCamera() {
      this.camera = new Camera(this.gl);
      this.camera.fov = 45;
      this.camera.position.z = 20;
    }
    createScene() { this.scene = new Transform(); }
    createGeometry() { this.planeGeometry = new Plane(this.gl, { heightSegments: 50, widthSegments: 100 }); }
    createMedias(items, bend = 1, borderRadius) {
      this.mediasImages = items.concat(items);
      this.medias = this.mediasImages.map((data, index) => {
        return new Media({
          geometry: this.planeGeometry,
          gl: this.gl,
          image: data.image,
          index,
          length: this.mediasImages.length,
          renderer: this.renderer,
          scene: this.scene,
          screen: this.screen,
          viewport: this.viewport,
          bend,
          borderRadius
        });
      });
    }
    _pauseAutoScroll() {
      this.userActive = true;
      clearTimeout(this.userActiveTimer);
      this.userActiveTimer = setTimeout(() => { this.userActive = false; }, 2500);
    }
    onTouchDown(e) {
      this._pauseAutoScroll();
      this.isDown = true;
      this.scroll.position = this.scroll.current;
      this.startX = e.touches ? e.touches[0].clientX : e.clientX;
      this.startY = e.touches ? e.touches[0].clientY : 0;
      this.start = this.startX;
      this.isHorizontal = null;
    }
    onTouchMove(e) {
      if (!this.isDown) return;
      const x = e.touches ? e.touches[0].clientX : e.clientX;
      const y = e.touches ? e.touches[0].clientY : 0;
      if (this.isHorizontal === null && e.touches) {
        const dx = Math.abs(x - this.startX);
        const dy = Math.abs(y - this.startY);
        if (dx > 4 || dy > 4) this.isHorizontal = dx >= dy;
      }
      if (e.touches && this.isHorizontal === false) return;
      if (e.touches && this.isHorizontal === true) e.preventDefault();
      const distance = (this.startX - x) * (this.scrollSpeed * 0.025);
      this.scroll.target = this.scroll.position + distance;
    }
    onTouchUp() { this.isDown = false; this.isHorizontal = null; this.onCheck(); }
    onWheel(e) {
      this._pauseAutoScroll();
      const delta = e.deltaY || e.wheelDelta || e.detail;
      this.scroll.target += (delta > 0 ? this.scrollSpeed : -this.scrollSpeed) * 0.2;
      this.onCheckDebounce();
    }
    onCheck() {
      if (!this.medias || !this.medias[0]) return;
      const width = this.medias[0].width;
      const itemIndex = Math.round(Math.abs(this.scroll.target) / width);
      const item = width * itemIndex;
      this.scroll.target = this.scroll.target < 0 ? -item : item;
    }
    onResize() {
      this.screen = { width: this.container.clientWidth, height: this.container.clientHeight };
      this.renderer.setSize(this.screen.width, this.screen.height);
      this.camera.perspective({ aspect: this.screen.width / this.screen.height });
      const fov = (this.camera.fov * Math.PI) / 180;
      const height = 2 * Math.tan(fov / 2) * this.camera.position.z;
      this.viewport = { width: height * this.camera.aspect, height };
      if (this.medias) {
        this.medias.forEach(media => media.onResize({ screen: this.screen, viewport: this.viewport }));
      }
    }
    update() {
      if (!this.userActive) this.scroll.target += this.autoScrollSpeed;
      this.scroll.current = lerp(this.scroll.current, this.scroll.target, this.scroll.ease);
      const direction = this.scroll.current > this.scroll.last ? 'right' : 'left';
      if (this.medias) { this.medias.forEach(media => media.update(this.scroll, direction)); }
      this.renderer.render({ scene: this.scene, camera: this.camera });
      this.scroll.last = this.scroll.current;
      this.raf = window.requestAnimationFrame(this.update.bind(this));
    }
    addEventListeners() {
      this.boundOnResize = this.onResize.bind(this);
      this.boundOnWheel = this.onWheel.bind(this);
      this.boundOnTouchDown = this.onTouchDown.bind(this);
      this.boundOnTouchMove = this.onTouchMove.bind(this);
      this.boundOnTouchUp = this.onTouchUp.bind(this);
      // Scope wheel to the gallery container so the page still scrolls normally.
      this.container.addEventListener('wheel', this.boundOnWheel, { passive: true });
      this.container.addEventListener('mousedown', this.boundOnTouchDown);
      window.addEventListener('resize', this.boundOnResize);
      window.addEventListener('mousemove', this.boundOnTouchMove);
      window.addEventListener('mouseup', this.boundOnTouchUp);
      this.container.addEventListener('touchstart', this.boundOnTouchDown, { passive: true });
      this.container.addEventListener('touchmove', this.boundOnTouchMove, { passive: false });
      window.addEventListener('touchend', this.boundOnTouchUp);
    }
    destroy() {
      window.cancelAnimationFrame(this.raf);
      window.removeEventListener('resize', this.boundOnResize);
      this.container.removeEventListener('wheel', this.boundOnWheel);
      this.container.removeEventListener('mousedown', this.boundOnTouchDown);
      window.removeEventListener('mousemove', this.boundOnTouchMove);
      window.removeEventListener('mouseup', this.boundOnTouchUp);
      clearTimeout(this.userActiveTimer);
      this.container.removeEventListener('touchstart', this.boundOnTouchDown);
      this.container.removeEventListener('touchmove', this.boundOnTouchMove);
      window.removeEventListener('touchend', this.boundOnTouchUp);
      if (this.renderer && this.renderer.gl && this.renderer.gl.canvas.parentNode) {
        this.renderer.gl.canvas.parentNode.removeChild(this.renderer.gl.canvas);
      }
    }
  };
}

function CircularGallery({ items, bend = 3, borderRadius = 0.05, scrollSpeed = 2, scrollEase = 0.05 }) {
  const containerRef = React.useRef(null);
  const [ready, setReady] = React.useState(!!window.__OGL);

  React.useEffect(() => {
    if (window.__OGL) { setReady(true); return; }
    const onReady = () => setReady(true);
    window.addEventListener('ogl-ready', onReady);
    return () => window.removeEventListener('ogl-ready', onReady);
  }, []);

  React.useEffect(() => {
    if (!ready || !window.__OGL) return;
    if (!items || items.length === 0) return;
    const App = makeAppClass(window.__OGL);
    const app = new App(containerRef.current, { items, bend, borderRadius, scrollSpeed, scrollEase });
    return () => { app.destroy(); };
  }, [ready, items, bend, borderRadius, scrollSpeed, scrollEase]);

  return React.createElement('div', { className: 'circular-gallery', ref: containerRef });
}

window.CircularGallery = CircularGallery;
