import React, { useState, useCallback, useRef, useEffect } from "react";

const FlexibleGroupedCarousel = ({
  maxVisibleItems = 5,
  gapPx = 180,
  evenlySpaced = true,
}) => {
  const itemGroups = [
    { id: 1, items: ["🍎", "🍌", "🍇"], name: "Summer Fruits" },
    { id: 2, items: ["🍊", "🍓", "🍍"], name: "Tropical Delights" },
    { id: 3, items: ["🥝", "🥭", "🍈"], name: "Exotic Mix" },
    { id: 4, items: ["🍉", "🍐", "🍑"], name: "Juicy Picks" },
  ];

  const [offset, setOffset] = useState(0);
  const [visibleGroups, setVisibleGroups] = useState(new Set());
  const containerRef = useRef(null);

  const pathD = "M20,50 Q90,10 180,50 T360,50";
  const pathLength = 340; // Approximate path length in pixels

  const handleWheel = useCallback((event: React.WheelEvent<HTMLDivElement>) => {
    event.preventDefault();
    setOffset((prevOffset) => {
      const newOffset = prevOffset + event.deltaY * 0.001;
      return newOffset - Math.floor(newOffset);
    });
  }, []);

  const totalItems = itemGroups.reduce(
    (sum, group) => sum + group.items.length,
    0,
  );
  const totalGapLength = (maxVisibleItems - 1) * gapPx;
  const itemSpacing = (pathLength - totalGapLength) / maxVisibleItems;

  const getItemOffset = (groupIndex: number, itemIndex: number) => {
    if (evenlySpaced) {
      const itemCount =
        itemGroups
          .slice(0, groupIndex)
          .reduce((sum, g) => sum + g.items.length, 0) + itemIndex;
      return (itemCount * (pathLength / totalItems)) / pathLength;
    } else {
      const groupStartOffset = itemGroups
        .slice(0, groupIndex)
        .reduce((sum, g) => sum + g.items.length * itemSpacing + gapPx, 0);
      return (groupStartOffset + itemIndex * itemSpacing) / pathLength;
    }
  };

  const isVisible = (itemOffset: number) => {
    const visibleRange = evenlySpaced
      ? maxVisibleItems / totalItems
      : (maxVisibleItems * (itemSpacing + gapPx)) / pathLength;
    return (
      itemOffset > 0.5 - visibleRange / 2 && itemOffset < 0.5 + visibleRange / 2
    );
  };

  useEffect(() => {
    const visibleGroupsSet = new Set();
    itemGroups.forEach((group, groupIndex) => {
      const groupVisible = group.items.some((_, itemIndex) => {
        const itemOffset = (getItemOffset(groupIndex, itemIndex) + offset) % 1;
        return isVisible(itemOffset);
      });
      if (groupVisible) visibleGroupsSet.add(group.id);
    });
    setVisibleGroups(visibleGroupsSet);
  }, [
    offset,
    itemGroups,
    maxVisibleItems,
    evenlySpaced,
    gapPx,
    itemSpacing,
    totalItems,
  ]);

  return (
    <div className="w-[400px]">
      <div
        ref={containerRef}
        className="relative mb-4 h-[200px] overflow-hidden rounded-lg bg-gray-100"
        onWheel={handleWheel}
      >
        <svg width="400" height="200" className="absolute left-0 top-0">
          <path d={pathD} fill="none" stroke="lightgray" />
        </svg>

        {itemGroups.flatMap((group, groupIndex) =>
          group.items.map((item, itemIndex) => {
            const itemOffset =
              (getItemOffset(groupIndex, itemIndex) + offset) % 1;
            const visible = isVisible(itemOffset);
            return (
              <div
                key={`${group.id}-${itemIndex}`}
                className={`absolute -translate-x-1/2 -translate-y-1/2 transform text-4xl transition-opacity duration-300 ${visible ? "opacity-100" : "opacity-0"}`}
                style={{
                  offsetPath: `path("${pathD}")`,
                  offsetDistance: `${itemOffset * 100}%`,
                }}
              >
                {item}
              </div>
            );
          }),
        )}
      </div>
      <div className="mb-2 flex justify-center space-x-2">
        {itemGroups.map((group) => (
          <div
            key={group.id}
            className={`h-3 w-3 rounded-full ${visibleGroups.has(group.id) ? "bg-blue-500" : "bg-gray-300"}`}
            title={group.name}
          />
        ))}
      </div>
      <div className="text-center text-sm">
        Visible Groups:{" "}
        {Array.from(visibleGroups)
          .map((id) => itemGroups.find((g) => g.id === id)?.name)
          .join(", ")}
      </div>
    </div>
  );
};

export default FlexibleGroupedCarousel;


the visible items should be distributed on path with similar to justify between or the gap px. if add a parameter. In current code the gap is only working between groups not the individual items.