import React from "react";
import { restrictToBoundingRect } from "./restrict-to-bounding-rect.js";
const ACTIVATION_DISTANCE = 5;
const useSortableGrid = ({ itemCount, onSortEnd, getContainerRect }) => {
  const itemNodes = React.useRef([]);
  const [activeIndex, setActiveIndex] = React.useState(null);
  const [overIndex, setOverIndex] = React.useState(null);
  const [transform, setTransform] = React.useState(null);
  const drag = React.useRef({
    start: { x: 0, y: 0 },
    activated: false,
    moved: false,
    index: -1,
    over: null,
    rects: [],
    cleanup: () => {
    }
  });
  const reset = React.useCallback(() => {
    drag.current.cleanup();
    drag.current.cleanup = () => {
    };
    drag.current.activated = false;
    drag.current.index = -1;
    drag.current.over = null;
    setActiveIndex(null);
    setOverIndex(null);
    setTransform(null);
  }, []);
  const onPointerDown = React.useCallback(
    (index) => (event) => {
      if (event.button !== 0) {
        return;
      }
      drag.current.cleanup();
      drag.current.start = { x: event.clientX, y: event.clientY };
      drag.current.activated = false;
      drag.current.moved = false;
      drag.current.index = index;
      const handleUp = () => {
        if (drag.current.activated) {
          const oldIndex = drag.current.index;
          const newIndex = drag.current.over ?? oldIndex;
          if (oldIndex !== newIndex) {
            onSortEnd({ oldIndex, newIndex });
          }
        }
        reset();
      };
      const handleMove = (moveEvent) => {
        if (moveEvent.buttons === 0) {
          handleUp();
          return;
        }
        const pointer = { x: moveEvent.clientX, y: moveEvent.clientY };
        if (!drag.current.activated) {
          const dx = pointer.x - drag.current.start.x;
          const dy = pointer.y - drag.current.start.y;
          if (Math.hypot(dx, dy) < ACTIVATION_DISTANCE) {
            return;
          }
          drag.current.activated = true;
          drag.current.moved = true;
          drag.current.rects = itemNodes.current.slice(0, itemCount).map((itemNode) => itemNode?.getBoundingClientRect() ?? null);
          setActiveIndex(index);
        }
        const containerRect = getContainerRect();
        const draggingRect = drag.current.rects[index];
        const offset = {
          x: pointer.x - drag.current.start.x,
          y: pointer.y - drag.current.start.y,
          scaleX: 1,
          scaleY: 1
        };
        setTransform(
          containerRect && draggingRect ? restrictToBoundingRect(offset, draggingRect, containerRect) : offset
        );
        const over = getOverIndex(pointer, drag.current.rects, index);
        drag.current.over = over;
        setOverIndex(over);
      };
      document.addEventListener("pointermove", handleMove);
      document.addEventListener("pointerup", handleUp);
      document.addEventListener("pointercancel", reset);
      drag.current.cleanup = () => {
        document.removeEventListener("pointermove", handleMove);
        document.removeEventListener("pointerup", handleUp);
        document.removeEventListener("pointercancel", reset);
      };
    },
    [itemCount, onSortEnd, getContainerRect, reset]
  );
  React.useEffect(() => {
    if (activeIndex !== null) {
      reset();
    }
  }, [itemCount]);
  React.useEffect(() => reset, [reset]);
  const getItemProps = (index) => {
    const style = { touchAction: "none" };
    if (activeIndex === index && transform) {
      style.transform = `translate3d(${transform.x}px, ${transform.y}px, 0)`;
      style.transition = "none";
      style.zIndex = 1e4;
      style.cursor = "grabbing";
    } else if (activeIndex !== null && overIndex !== null) {
      const shift = shiftFor(index, activeIndex, overIndex, drag.current.rects);
      style.transform = `translate3d(${shift.x}px, ${shift.y}px, 0)`;
      style.transition = "transform 200ms ease";
    }
    return {
      ref: (node) => {
        itemNodes.current[index] = node;
      },
      onPointerDown: onPointerDown(index),
      style
    };
  };
  return {
    getItemProps,
    isDragging: activeIndex !== null,
    didDrag: () => drag.current.moved
  };
};
const getOverIndex = (pointer, rects, activeIndex) => {
  let nearest = activeIndex;
  let minDistance = Infinity;
  rects.forEach((rect, index) => {
    if (!rect) {
      return;
    }
    const centerX = rect.left + rect.width / 2;
    const centerY = rect.top + rect.height / 2;
    const distance = (pointer.x - centerX) ** 2 + (pointer.y - centerY) ** 2;
    if (distance < minDistance) {
      minDistance = distance;
      nearest = index;
    }
  });
  return nearest;
};
const shiftFor = (index, activeIndex, overIndex, rects) => {
  const movingDown = overIndex > activeIndex;
  const inRange = movingDown ? index > activeIndex && index <= overIndex : index < activeIndex && index >= overIndex;
  if (!inRange) {
    return { x: 0, y: 0 };
  }
  const from = rects[index];
  const to = movingDown ? rects[index - 1] : rects[index + 1];
  if (!from || !to) {
    return { x: 0, y: 0 };
  }
  return { x: to.left - from.left, y: to.top - from.top };
};
export {
  useSortableGrid
};
//# sourceMappingURL=use-sortable-grid.js.map
