/**
 * Canvas event wiring — mouse, touch (including pinch-zoom on two
 * fingers), and the canvas-area pan handler.
 *
 *   Mouse:
 *     mousedown on canvas    → beginDraw
 *     mousemove on window    → continueDraw (window so a drag can
 *                              continue past the canvas edge)
 *     mouseup on window      → endDraw
 *     mouseenter/mouseleave  → show/hide the brush-cursor overlay
 *     mousedown on canvas-area (NOT on the canvas itself, lasso only)
 *                            → beginDraw (lasso starts outside canvas)
 *
 *   Touch:
 *     touchstart 1 finger    → beginDraw
 *     touchmove  1 finger    → continueDraw
 *     touchend / touchcancel → endDraw
 *     touchstart 2 fingers   → pinch-zoom + 2-finger pan
 *
 *   Pan (any free space around the canvas):
 *     pointerdown / pointermove / pointerup on canvas-area, skipping
 *     the canvas + transform overlay + UI elements above them. Sets
 *     canvasArea.dataset.panX/Y + CSS transform on both canvases.
 *
 *   Exposes `canvasArea._resetPan()` so the zoom/fit reset can clear
 *   the pan offset.
 *
 * @param {{
 *   canvasArea:        HTMLDivElement,
 *   beginDraw:         (e: Event) => void,
 *   continueDraw:      (e: Event) => void,
 *   endDraw:           (e?: Event) => void,
 *   updateBrushCursor: (e: Event) => void,
 *   syncZoomControls?: () => void,
 * }} ctx
 */
import { state } from './state.js';

export function wireCanvasEvents({ canvasArea, beginDraw, continueDraw, endDraw, updateBrushCursor, syncZoomControls }) {
  // Mouse — mousedown stays on the canvas; mousemove/up are bound to
  // the WINDOW so a drag can continue (and end) past the canvas edge.
  // Critical for the Resize tool where users overshoot.
  state.mainCanvas.addEventListener('mousedown', beginDraw);
  window.addEventListener('mousemove', continueDraw);
  window.addEventListener('mouseup', endDraw);
  // Lasso can start OUTSIDE the canvas — fallback mousedown on the
  // surrounding canvas-area so the user can begin a lasso path in
  // the empty space around the image. Other tools stay canvas-only.
  canvasArea.addEventListener('mousedown', (e) => {
    if (state.tool !== 'lasso') return;
    if (e.target === state.mainCanvas) return; // already handled
    beginDraw(e);
  });
  state.mainCanvas.addEventListener('mouseenter', (e) => {
    if (['brush', 'eraser', 'inpaint', 'lasso', 'clone'].includes(state.tool)) updateBrushCursor(e);
  });
  state.mainCanvas.addEventListener('mouseleave', () => {
    // Only hide the brush-cursor overlay on leave — DO NOT end the
    // drag, so the user can drag a resize handle past the canvas edge.
    if (state.cursorEl) state.cursorEl.style.display = 'none';
  });

  // Touch — single finger draws; two fingers pan + pinch-zoom.
  let multiActive = false;
  let multiStartDist = 0;
  let multiStartZoom = 1;
  let multiStartCenter = { x: 0, y: 0 };
  let multiStartPan = { x: 0, y: 0 };
  const touchInfo = (e) => {
    const t1 = e.touches[0], t2 = e.touches[1];
    const cx = (t1.clientX + t2.clientX) / 2;
    const cy = (t1.clientY + t2.clientY) / 2;
    const dx = t2.clientX - t1.clientX;
    const dy = t2.clientY - t1.clientY;
    return { cx, cy, dist: Math.hypot(dx, dy) };
  };
  const applyCanvasOffset = (x, y) => {
    canvasArea.dataset.panX = String(x);
    canvasArea.dataset.panY = String(y);
    const t = `translate3d(${x}px, ${y}px, 0)`;
    state.mainCanvas.style.transform = t;
    if (state.transformOverlay) state.transformOverlay.style.transform = t;
  };
  state.mainCanvas.addEventListener('touchstart', (e) => {
    e.preventDefault();
    if (e.touches.length >= 2) {
      // End any in-progress single-finger draw before switching modes.
      if (!multiActive) endDraw();
      multiActive = true;
      const info = touchInfo(e);
      multiStartDist = info.dist;
      multiStartZoom = state.zoom;
      multiStartCenter = { x: info.cx, y: info.cy };
      multiStartPan = {
        x: parseFloat(canvasArea.dataset.panX || '0') || 0,
        y: parseFloat(canvasArea.dataset.panY || '0') || 0,
      };
      return;
    }
    if (multiActive) return;
    beginDraw(e);
  }, { passive: false });
  state.mainCanvas.addEventListener('touchmove', (e) => {
    e.preventDefault();
    if (multiActive && e.touches.length >= 2) {
      const info = touchInfo(e);
      const ratio = info.dist / Math.max(1, multiStartDist);
      const newZoom = Math.max(0.1, Math.min(5, multiStartZoom * ratio));
      if (Math.abs(newZoom - state.zoom) > 0.001) {
        state.zoom = newZoom;
        state.mainCanvas.style.width = (state.imgWidth * state.zoom) + 'px';
        state.mainCanvas.style.height = (state.imgHeight * state.zoom) + 'px';
        const label = state.container.querySelector('.ge-zoom-label');
        if (label) label.textContent = Math.round(state.zoom * 100) + '%';
        syncZoomControls?.();
      }
      const dx = info.cx - multiStartCenter.x;
      const dy = info.cy - multiStartCenter.y;
      applyCanvasOffset(multiStartPan.x + dx, multiStartPan.y + dy);
      return;
    }
    if (multiActive) return;
    continueDraw(e);
  }, { passive: false });
  state.mainCanvas.addEventListener('touchend', (e) => {
    if (multiActive) {
      if (e.touches.length < 2) multiActive = false;
      return;
    }
    endDraw(e);
  });
  state.mainCanvas.addEventListener('touchcancel', () => {
    multiActive = false;
    endDraw();
  });

  // Press-and-drag in the empty space AROUND the canvas pans the
  // canvas + overlay via CSS transform. Works even when the image
  // fits the viewport (no scroll needed). Skips presses on the canvas
  // itself (the canvas owns its own drawing input) or on UI elements
  // above it.
  let panning = false;
  let pid = null;
  let startX = 0, startY = 0;
  const getOffset = () => {
    const v = canvasArea.dataset.panX || '0';
    const u = canvasArea.dataset.panY || '0';
    return { x: parseFloat(v) || 0, y: parseFloat(u) || 0 };
  };
  const applyOffset = (x, y) => {
    canvasArea.dataset.panX = String(x);
    canvasArea.dataset.panY = String(y);
    const t = `translate3d(${x}px, ${y}px, 0)`;
    state.mainCanvas.style.transform = t;
    if (state.transformOverlay) state.transformOverlay.style.transform = t;
  };
  canvasArea.addEventListener('pointerdown', (e) => {
    if (state.tool === 'lasso') return;
    if (e.target === state.mainCanvas || e.target === state.transformOverlay) return;
    if (e.target.closest('button, input, .ge-adj-popup, .ge-transform-popup, .ge-fx-popup, .ge-inpaint-popup, .ge-controls, .ge-right-panel, .ge-fx-menu')) return;
    // During an active transform the corner/rotation handles render
    // OUTSIDE the canvas (over the surrounding area), and the overlay is
    // pointer-events:none — so a grab on an outside handle lands here.
    // Route it to the transform tool (getHandleAt works in image space,
    // even for points beyond the canvas) instead of panning the canvas.
    if (state.transformActive) {
      beginDraw(e);
      // Only swallow the event (skip pan) if a handle was grabbed OR the
      // layer-move fallback engaged; otherwise let the pan logic below
      // run so empty space still pans while the transform tool is open.
      if (state.transformHandle || state.moving) return;
    }
    const off = getOffset();
    panning = true;
    pid = e.pointerId;
    startX = e.clientX - off.x;
    startY = e.clientY - off.y;
    try { canvasArea.setPointerCapture(pid); } catch {}
    canvasArea.style.cursor = 'grabbing';
    e.preventDefault();
  });
  canvasArea.addEventListener('pointermove', (e) => {
    if (!panning || e.pointerId !== pid) return;
    applyOffset(e.clientX - startX, e.clientY - startY);
  });
  const endPan = () => {
    if (!panning) return;
    panning = false;
    try { canvasArea.releasePointerCapture(pid); } catch {}
    pid = null;
    canvasArea.style.cursor = '';
  };
  canvasArea.addEventListener('pointerup', endPan);
  canvasArea.addEventListener('pointercancel', endPan);
  // Reset offset whenever zoom/fit changes the canvas size.
  canvasArea._resetPan = () => applyOffset(0, 0);
}
