import { useEffect, useRef, useCallback, useState } from "react"
import {
  RenderingEngine,
  Enums,
} from "@cornerstonejs/core"
import { init as csRenderInit, utilities as csUtilities } from "@cornerstonejs/core"
import { init as csToolsInit, addTool, ToolGroupManager, Enums as csToolsEnums, PanTool, WindowLevelTool, StackScrollTool, ZoomTool, PlanarRotateTool, utilities as csToolsUtilities } from "@cornerstonejs/tools"
import { LengthTool, ProbeTool, ArrowAnnotateTool, RectangleROITool, EllipticalROITool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, SculptorTool, CrosshairsTool, ReferenceLinesTool, ReferenceCursors } from "@cornerstonejs/tools"
import { init as dicomImageLoaderInit } from "@cornerstonejs/dicom-image-loader"
import * as cornerstoneTools from '@cornerstonejs/tools';
import { segmentation, Enums as csEnums } from '@cornerstonejs/tools';
import { voi } from "@cornerstonejs/tools/utilities"
import { readDicomElementExplicit } from "dicom-parser"
import { volumeLoader } from '@cornerstonejs/core';
import { metaData } from '@cornerstonejs/core';
import * as dicomParser from "dicom-parser";
import cornerstoneDICOMImageLoader from "@cornerstonejs/dicom-image-loader"

import { NoLabelArrowAnnotateTool } from "./NoLabelArrowAnnotateTool";
import { render } from "@cornerstonejs/tools/tools/displayTools/Labelmap/labelmapDisplay"

// Extend the Window interface to include cornerstoneTools
declare global {
  interface Window {
    cornerstone3dTools: typeof cornerstoneTools;
  }
}
function pixelToWorld(imageId: string, point: { x: number, y: number }) {
  const plane = metaData.get("imagePlaneModule", imageId);
  if (!plane) return undefined;
  const { rowCosines, columnCosines, imagePositionPatient, rowPixelSpacing, columnPixelSpacing } = plane;
  if (!rowCosines || !columnCosines || !imagePositionPatient) return undefined;
  // Default to 1 if spacing is missing
  const dx = columnPixelSpacing || 1;
  const dy = rowPixelSpacing || 1;
  // Convert cosines to vectors
  const rc = rowCosines;
  const cc = columnCosines;
  const ipp = imagePositionPatient;
  // World = IPP + x*col*cos*spacing + y*row*cos*spacing
  return [
    ipp[0] + point.x * cc[0] * dx + point.y * rc[0] * dy,
    ipp[1] + point.x * cc[1] * dx + point.y * rc[1] * dy,
    ipp[2] + point.x * cc[2] * dx + point.y * rc[2] * dy,
  ];
}

// Add to global types:
declare global {
  interface Window {
    exportViewerState?: () => string;
    importViewerState?: (json: string) => void;
    exportAnnotations?: () => string;
    importAnnotations?: (json: string) => void;
    importLegacyAnnotations?: (json: string) => void;
    importLegacyViewport?: (json: string) => void;
  }
}

type StackEntry = { imageIds: string[], studyInstanceUID?: string };

// Add this type for clarity
declare global {
  interface Window {
    loadDicomStackFromFiles?: (files: FileList | File[]) => void;
  }
}

declare global {
  interface Window {
    loadDicomStackFromWadouriList?: (wadouriImageIds: string[]) => void;
  }
}

window.cornerstone3dTools = cornerstoneTools;


type ViewportDiv = HTMLDivElement & {
  _cornerstoneEnabled?: boolean;
  _lastViewportType?: any;
  _resizeObserver?: ResizeObserver;
  _overlayListener?: EventListener;
};

const { MouseBindings, KeyboardBindings } = csToolsEnums;

const { IMAGE_RENDERED } = Enums.Events;


// Common CT presets (add more as needed)
const WINDOW_LEVEL_PRESETS = [
  { name: "Soft Tissue", center: 40, width: 400 },
  { name: "Lung", center: -600, width: 1500 },
  { name: "Bone", center: 300, width: 1500 },
  { name: "Brain", center: 40, width: 80 },
  { name: "Abdomen", center: 60, width: 400 },
]

// Tool options for dropdowns
const TOOL_OPTIONS = [
  { label: "Window/Level", value: WindowLevelTool.toolName },
  { label: "Pan", value: PanTool.toolName },
  { label: "Zoom", value: ZoomTool.toolName },
  { label: "Stack Scroll", value: StackScrollTool.toolName },
  { label: "Rotate", value: PlanarRotateTool.toolName },
  { label: "Length", value: LengthTool.toolName },
  { label: "Probe", value: ProbeTool.toolName },
  { label: "Arrow (No Label)", value: NoLabelArrowAnnotateTool.toolName },
  { label: "Arrow Annotate", value: ArrowAnnotateTool.toolName },
  { label: "Rectangle ROI", value: RectangleROITool.toolName },
  { label: "Ellipse ROI", value: EllipticalROITool.toolName },
  { label: "Freehand ROI", value: PlanarFreehandROITool.toolName },
  { label: "Freehand Contour Segmentation", value: PlanarFreehandContourSegmentationTool.toolName },
  { label: "Sculptor", value: SculptorTool.toolName },
  { label: "Reference Cursors", value: ReferenceCursors.toolName },
]

// Mouse button labels
const MOUSE_BUTTONS = [
  { label: "Left", value: "Primary" },
  { label: "Middle", value: "Auxiliary" },
  { label: "Right", value: "Secondary" },
]
const ALL_MOUSE_BINDINGS = [
  { label: "Left", value: "Primary", mask: 1 },
  { label: "Middle", value: "Auxiliary", mask: 4 },
  { label: "Right", value: "Secondary", mask: 2 },
  { label: "Left + Right", value: "Primary_and_Secondary", mask: 3 },
  { label: "Left + Middle", value: "Primary_and_Auxiliary", mask: 5 },
  { label: "Right + Middle", value: "Secondary_and_Auxiliary", mask: 6 },
  { label: "Left + Right + Middle", value: "Primary_and_Secondary_and_Auxiliary", mask: 7 },
  { label: "Fourth Button", value: "Fourth_Button", mask: 8 },
  { label: "Fifth Button", value: "Fifth_Button", mask: 16 },
  { label: "Mouse Wheel", value: "Wheel", mask: 524288 },
  { label: "Mouse Wheel + Left", value: "Wheel_Primary", mask: 524289 },
];

type AppProps = {
  container_id?: string;
  imageStacks: () => Promise<string[][]>;
  autoCacheStack?: boolean; // <-- new prop
  // ...other props if needed
};

// App definintion
function App({ container_id, imageStacks, autoCacheStack, ...props }: AppProps) {
  const elementRef = useRef<HTMLDivElement>(null)
  const running = useRef(false)

  // State to control preset menu open/close
  const [presetsOpen, setPresetsOpen] = useState(false);

  const [settingsOpen, setSettingsOpen] = useState(false);

  const [fullscreenHoverIdx, setFullscreenHoverIdx] = useState<number | null>(null);
  const [openDropdownIdx, setOpenDropdownIdx] = useState<number | null>(null);

  const MAX_VIEWPORTS = 9;
  const renderingEngineId = "renderingEngine_" + container_id;
  const MAIN_TOOL_GROUP_ID = "TOOL_GROUP_" + container_id;
  const renderingEngineRef = useRef<any>(null);

  const restoringStateRef = useRef(false);


  const [availableStacks, setAvailableStacks] = useState<StackEntry[]>([]);
  const [activeViewport, setActiveViewport] = useState<number | null>(0);
  const [viewportImageIds, setViewportImageIds] = useState<string[][]>(
    () => Array(MAX_VIEWPORTS).fill([])
  );
  const [selectedStackIdx, setSelectedStackIdx] = useState<number[]>(
    () => Array(MAX_VIEWPORTS).fill(0)
  );


  // Load available stacks on mount
  useEffect(() => {
    imageStacks().then(async stacks => {
      // Try to extract StudyInstanceUID for each stack
      const stackEntries: StackEntry[] = await Promise.all(
        stacks.map(async imageIds => {
          let studyInstanceUID: string | undefined = undefined;
          try {
            // Try to fetch the first image as a blob and parse it
            const firstId = imageIds[0];
            if (firstId && firstId.startsWith("wadouri:")) {
              const url = firstId.slice(8);
              const response = await fetch(url);
              const arrayBuffer = await response.arrayBuffer();
              const dataSet = dicomParser.parseDicom(new Uint8Array(arrayBuffer));
              studyInstanceUID = dataSet.string('x0020000d');
            }
          } catch (e) {
            // Ignore errors, fallback to undefined
          }
          return { imageIds, studyInstanceUID };
        })
      );
      setAvailableStacks(stackEntries);
      //setAvailableStacks(stacks.map(imageIds => ({ imageIds, studyInstanceUID: undefined })));
      await setLoadedImageIdsAndCacheMeta(stacks[0]);
      setViewportImageIds(Array(MAX_VIEWPORTS).fill(stacks[0] || []));
    });
  }, []);

  const handleLoadStack = async (viewportIdx: number, stackIdx: number) => {
    const stackObj = availableStacks[stackIdx];
    if (!stackObj) return;
    const stack = stackObj.imageIds;

    // Always revert to stack mode when loading a new stack
    setViewportModes(prev => {
      const next = [...prev];
      next[viewportIdx] = 'stack';
      return next;
    });

    setViewportImageIds(prev => {
      const next = [...prev];
      next[viewportIdx] = stack;
      return next;
    });
    setSelectedStackIdx(prev => {
      const next = [...prev];
      next[viewportIdx] = stackIdx;
      return next;
    });

    // Cache imageIds and load metadata for this stack
    await setLoadedImageIdsAndCacheMeta(stack);
  };

  const [crossHairsEnabled, setCrossHairsEnabled] = useState(false);
  const [referenceLinesEnabled, setReferenceLinesEnabled] = useState(true);

  const [showAdvancedMouseBindings, setShowAdvancedMouseBindings] = useState(false);
  const [showAdvancedCtrlMouseBindings, setShowAdvancedCtrlMouseBindings] = useState(false);

  const sortViewportImageIdsBySliceLocation = () => {
    console.log("Sorting viewport imageIds by slice location");
    setViewportImageIds(prev => {
      const next = prev.map((imageIds, idx) => {
        if (!imageIds || imageIds.length === 0) return imageIds;

        const withSortKey = imageIds.map(id => {
          const plane = metaData.get('imagePlaneModule', id);
          let sortKey = null;
          if (plane) {
            if (plane.sliceLocation !== undefined) {
              sortKey = Number(plane.sliceLocation);
            } else if (
              plane.imagePositionPatient &&
              Array.isArray(plane.imagePositionPatient) &&
              plane.imagePositionPatient.length === 3
            ) {
              sortKey = Number(plane.imagePositionPatient[2]);
            }
          }
          return { imageId: id, sortKey };
        });

        const sorted = [...withSortKey].sort((a, b) => {
          if (a.sortKey !== null && b.sortKey !== null) {
            return a.sortKey - b.sortKey;
          }
          return a.imageId.localeCompare(b.imageId);
        });

        const sortedIds = sorted.map(obj => obj.imageId);

        if (JSON.stringify(sortedIds) === JSON.stringify(imageIds)) {
          return imageIds;
        }
        return sortedIds;
      });
      return next;
    });
  };

  // Wrap setLoadedImageIds to also cache DICOM metadata
  const setLoadedImageIdsAndCacheMeta = async (imageIds: string[]) => {
    console.log("Setting loaded imageIds:", imageIds);
    console.log("autoCacheStack:", autoCacheStack);
    const loadPromises: Promise<any>[] = [];

    if (autoCacheStack) {
      for (const imageId of imageIds) {
        if (imageId.startsWith("wadouri:")) {
          console.log("Caching metadata for imageId:", imageId);
          try {
            const url = imageId.slice(8);
            if (cornerstoneDICOMImageLoader.wadouri.dataSetCacheManager) {
              // @ts-ignore
              const result = cornerstoneDICOMImageLoader.wadouri.dataSetCacheManager.load(url);
              if (result && typeof result.then === "function") {
                loadPromises.push(result);
              }
            } else {
              console.warn("MetaDataManager not found. Unable to cache metadata.");
            }
          } catch (e) {
            console.warn(`Failed to cache metadata for ${imageId}:`, e);
          }
        }
      }

      // Wait for all metadata loads to finish
      await Promise.all(loadPromises);

      // Trigger sort if enabled
      if (orderBySliceLocation) {
        console.log("Sorting viewport imageIds by slice location");
        sortViewportImageIdsBySliceLocation();
      }
    }
  };


  const [viewportGrid, setViewportGrid] = useState({ rows: 1, cols: 1 })
  const [viewportMenuOpen, setViewportMenuOpen] = useState(false)

  // Store refs for each viewport
  const viewportRefs = useRef<Array<ViewportDiv | null>>([]);

  // Store the properties and scroll position of existing viewports before grid change
  const prevViewportProps = useRef<Record<number, any>>({});
  const prevScrollIndices = useRef<Record<number, number>>({});

  const [orderBySliceLocation, setOrderBySliceLocation] = useState(true);

  const [altEnablesReferenceCursors, setAltEnablesReferenceCursors] = useState(true);

  // Helper to generate grid options
  const gridOptions = []
  for (let rows = 1; rows <= 3; rows++) {
    for (let cols = 1; cols <= 3; cols++) {
      gridOptions.push({ rows, cols })
    }
  }

  // State for overlay info per viewport
  const [imageInfos, setImageInfos] = useState<Array<{
    index: number,
    total: number,
    windowCenter: string,
    windowWidth: string,
  }>>(() =>
    Array(viewportGrid.rows * viewportGrid.cols).fill({
      index: 0,
      total: 0,
      windowCenter: "",
      windowWidth: "",
    })
  );

  const [viewportModes, setViewportModes] = useState<Array<'stack' | 'volume'>>(
    () => Array(MAX_VIEWPORTS).fill('stack')
  );

  // State for preset menu open/close per viewport
  const [presetsOpenArr, setPresetsOpenArr] = useState<boolean[]>(
    () => Array(viewportGrid.rows * viewportGrid.cols).fill(false)
  );

  // State for selected tool per mouse button
  const [mouseToolBindings, setMouseToolBindings] = useState({
    Primary: StackScrollTool.toolName,
    Auxiliary: PanTool.toolName,
    Secondary: WindowLevelTool.toolName,
    Primary_and_Secondary: ZoomTool.toolName, // 1 (left) + 2 (right) = 3 
    Fourth_Button: PlanarRotateTool.toolName, // 16
    Wheel: StackScrollTool.toolName, // 524288

  })
  const [ctrlMouseToolBindings, setCtrlMouseToolBindings] = useState({
    Primary: LengthTool.toolName,
    Auxiliary: ProbeTool.toolName,
    Secondary: ArrowAnnotateTool.toolName,
  })

  // Track if Ctrl is pressed
  const [ctrlPressed, setCtrlPressed] = useState(false)
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.ctrlKey) setCtrlPressed(true)
    }
    const handleKeyUp = (e: KeyboardEvent) => {
      if (!e.ctrlKey) setCtrlPressed(false)
    }
    window.addEventListener("keydown", handleKeyDown)
    window.addEventListener("keyup", handleKeyUp)
    return () => {
      window.removeEventListener("keydown", handleKeyDown)
      window.removeEventListener("keyup", handleKeyUp)
    }
  }, [])

  const [metaModalOpen, setMetaModalOpen] = useState(false);
  const [metaModalContent, setMetaModalContent] = useState<any>(null);

  const handleSelectAnnotation = (viewportIdx: number, uid: string | null) => {
    // Use the official selection API for Cornerstone3D Tools
    if (cornerstoneTools?.annotation?.selection?.setAnnotationSelected) {
      cornerstoneTools.annotation.selection.setAnnotationSelected(uid);
    }

    // Optionally, update your own UI state
    setSelectedAnnUIDs(prev => {
      const next = [...prev];
      next[viewportIdx] = uid;
      return next;
    });
  };

  const handleDeleteAnnotation = (viewportIdx: number, uid: string, currentAnnotations: any[]) => {
    const allAnnotations = cornerstoneTools.annotation.state.getAllAnnotations() || [];
    allAnnotations.forEach(ann => {
      if (ann.annotationUID === uid) ann.isSelected = false;
    });
    cornerstoneTools.annotation.state.removeAnnotation(uid);
    renderingEngineRef.current.render();
    //}
    // Auto-select another annotation if available
    const remaining = currentAnnotations.filter(ann => ann.annotationUID !== uid);
    setSelectedAnnUIDs(prev => {
      const next = [...prev];
      const newSelected = remaining.length > 0 ? remaining[0].annotationUID : null;
      next[viewportIdx] = newSelected;
      allAnnotations.forEach(ann => {
        ann.isSelected = ann.annotationUID === newSelected;
      });
      cornerstoneTools.annotation.selection.setAnnotationSelected(newSelected);
      renderingEngineRef.current.render();
      return next;
    });
  };

  // Add this function to gather and format metadata:
  const handleShowMetadata = () => {
    // Find the first visible viewport with images
    const firstIdx = viewportImageIds.findIndex(ids => ids && ids.length > 0);
    if (firstIdx === -1) {
      setMetaModalContent("No images loaded.");
      setMetaModalOpen(true);
      return;
    }
    const imageId = viewportImageIds[firstIdx][0];

    // Gather all available metadata modules for this imageId
    const modules = [
      "imagePlaneModule",
      "generalSeriesModule",
      "generalStudyModule",
      "patientModule",
      "imagePixelModule",
      "voiLutModule",
      "modalityLutModule",
      "sopCommonModule",
      "petIsotopeModule",
      "multiframeModule",
      "cineModule",
      "overlayPlaneModule",
      "generalImageModule",
      "stackModule",
      "voiLutModule",
      "modalityLutModule",
      "petSeriesModule",
      "petImageModule",
      "petIsotopeModule",
      "petSeriesModule",
      "petImageModule",
      "petIsotopeModule",
      "petSeriesModule",
      "petImageModule",
      "petIsotopeModule",
      "petSeriesModule",
      "petImageModule",
      "petIsotopeModule",
      // Add more if your app or cornerstone supports them
    ];

    const allMeta: Record<string, any> = {};
    modules.forEach((mod) => {
      const val = metaData.get(mod, imageId);
      if (val !== undefined) allMeta[mod] = val;
    });

    setMetaModalContent(
      <pre style={{ maxHeight: 600, overflow: "auto", fontSize: 13, color: "#fff" }}>
        {JSON.stringify(allMeta, null, 2)}
      </pre>
    );
    setMetaModalOpen(true);
  };

  // Inside your App component:
  useEffect(() => {
    window.loadDicomStackFromFiles = (files: FileList | File[]) => {
      // Convert FileList to array if needed
      const fileArray = Array.from(files);
      // Your logic to handle the files and load them as a stack
      // For example, call your existing handler:
      handleFilesSelected({ target: { files: fileArray } } as any);
    };
    // Cleanup on unmount
    return () => {
      window.loadDicomStackFromFiles = undefined;
    };
  }, []);

  const [altPressed, setAltPressed] = useState(false);

  useEffect(() => {
    const mainToolGroup = ToolGroupManager.getToolGroup(MAIN_TOOL_GROUP_ID);
    if (!mainToolGroup) return;

    // List all annotation tools you use
    [
      'ArrowAnnotate',
      'NoLabelArrowAnnotate',
      'Length',
      'RectangleROI',
      'EllipticalROI',
      'PlanarFreehandROI',
      'PlanarFreehandContourSegmentation',
      // Add more if needed
    ].forEach(toolName => {
      mainToolGroup.setToolConfiguration(toolName, {
        color: '#00e676', // normal color
        selectedColor: '#ffeb3b', // yellow for selected
      });
    });
  }, [MAIN_TOOL_GROUP_ID]);

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.altKey) setAltPressed(true);
    };
    const handleKeyUp = (e: KeyboardEvent) => {
      if (!e.altKey) setAltPressed(false);
    };
    window.addEventListener("keydown", handleKeyDown);
    window.addEventListener("keyup", handleKeyUp);
    return () => {
      window.removeEventListener("keydown", handleKeyDown);
      window.removeEventListener("keyup", handleKeyUp);
    };
  }, []);

  useEffect(() => {
    if (!altEnablesReferenceCursors) return;
    const mainToolGroup = ToolGroupManager.getToolGroup(MAIN_TOOL_GROUP_ID);
    if (!mainToolGroup) return;
    if (altPressed) {
      if (!mainToolGroup.hasTool(ReferenceCursors.toolName)) {
        mainToolGroup.addTool(ReferenceCursors.toolName);
      }
      mainToolGroup.setToolEnabled(ReferenceCursors.toolName);
      mainToolGroup.setToolActive(ReferenceCursors.toolName);
    } else {
      if (mainToolGroup.hasTool(ReferenceCursors.toolName)) {
        mainToolGroup.setToolDisabled(ReferenceCursors.toolName);
      }
    }
  }, [altPressed, altEnablesReferenceCursors]);

  // Helper to apply all mouse tool bindings (normal or ctrl)
  const applyMouseToolBindings = useCallback(() => {
    // Apply to both stack and volume tool groups
    const mainToolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(MAIN_TOOL_GROUP_ID);

    [mainToolGroup].forEach(toolGroup => {
      if (toolGroup) {
        // Set all tools to passive first, removing all bindings
        TOOL_OPTIONS.forEach(opt => {
          toolGroup.setToolPassive(opt.value, { removeAllBindings: true });
        });

        toolGroup.setToolDisabled(ReferenceCursors.toolName);

        // Collect all bindings for each tool
        const toolBindings: Record<string, Array<any>> = {};

        // Normal bindings
        Object.entries(mouseToolBindings).forEach(([btn, tool]) => {
          if (!toolBindings[tool]) toolBindings[tool] = [];
          // Find the mask for this binding
          const binding = ALL_MOUSE_BINDINGS.find(b => b.value === btn);
          if (binding) {
            toolBindings[tool].push({ mouseButton: binding.mask });
          } else if (MouseBindings[btn]) {
            // Fallback for legacy bindings
            toolBindings[tool].push({ mouseButton: MouseBindings[btn] });
          }
        });
        // Ctrl bindings (now supports advanced mouse bindings)
        Object.entries(ctrlMouseToolBindings).forEach(([btn, tool]) => {
          if (!tool) return;
          if (!toolBindings[tool]) toolBindings[tool] = [];
          const binding = ALL_MOUSE_BINDINGS.find(b => b.value === btn);
          if (binding) {
            toolBindings[tool].push({
              mouseButton: binding.mask,
              modifierKey: KeyboardBindings.Ctrl,
            });
          } else if (MouseBindings[btn]) {
            toolBindings[tool].push({
              mouseButton: MouseBindings[btn],
              modifierKey: KeyboardBindings.Ctrl,
            });
          }
        });

        // Apply all bindings for each tool in a single call
        Object.entries(toolBindings).forEach(([tool, bindings]) => {
          toolGroup.setToolActive(tool, { bindings });
        });

        //// Always re-apply the mouse wheel binding for StackScrollTool
        //toolGroup.setToolActive(StackScrollTool.toolName, {
        //  bindings: [
        //    {
        //      mouseButton: MouseBindings.Wheel, // Wheel Mouse
        //    },
        //  ],
        //});

      }
    });
  }, [mouseToolBindings, ctrlMouseToolBindings]);

  const [sideMenuOpen, setSideMenuOpen] = useState(false);

  // Add a ref to store selected files
  const fileInputRef = useRef<HTMLInputElement | null>(null);

  // Handler for file input change
  const handleFilesSelected = async (event: React.ChangeEvent<HTMLInputElement> | { target: { files: File[] | FileList } }) => {
    const files = event.target.files;
    if (!files || files.length === 0) return;

    setOrderBySliceLocation(false);

    const fileArray = Array.from(files).sort((a, b) => a.name.localeCompare(b.name));
    let studyInstanceUID: string | undefined = undefined;
    const arrayBuffer = await fileArray[0].arrayBuffer();
    const dataSet = dicomParser.parseDicom(new Uint8Array(arrayBuffer));
    studyInstanceUID = dataSet.string('x0020000d');
    console.log("StudyInstanceUID:", studyInstanceUID);

    const imageIds = fileArray.map(
      file => `wadouri:${URL.createObjectURL(file)}`
    );

    setAvailableStacks(prev => {
      const next = [...prev, { imageIds, studyInstanceUID }];
      setViewportImageIds(vpPrev => {
        const vpNext = [...vpPrev];
        vpNext[0] = imageIds;
        return vpNext;
      });
      setSelectedStackIdx(selPrev => {
        const selNext = [...selPrev];
        selNext[0] = next.length - 1;
        return selNext;
      });
      setLoadedImageIdsAndCacheMeta(imageIds);
      return next;
    });
  };


  const handleToolChange = (mouseButton: string, toolName: string) => {
    setMouseToolBindings(prev => ({ ...prev, [mouseButton]: toolName }))
  }

  // Update overlay info for a specific viewport
  const updateOverlay = useCallback((viewportIdx: number, viewport: any) => {
    if (!viewport) return;
    const currentIndex = typeof viewport.getCurrentImageIdIndex === "function"
      ? viewport.getCurrentImageIdIndex()
      : 0;
    const imageIds = typeof viewport.getImageIds === "function"
      ? viewport.getImageIds()
      : [];
    const total = imageIds.length;

    let windowCenter = "";
    let windowWidth = "";
    if (typeof viewport.getProperties === "function") {
      const props = viewport.getProperties();
      if (props.voiRange) {
        const { lower, upper } = props.voiRange;
        windowCenter = ((upper + lower) / 2).toFixed(0);
        windowWidth = (upper - lower).toFixed(0);
      }
    }

    setImageInfos(prev => {
      const next = [...prev];
      next[viewportIdx] = {
        index: currentIndex + 1,
        total,
        windowCenter,
        windowWidth,
      };
      return next;
    });
  }, []);

  const [gridBtnActive, setGridBtnActive] = useState(false);

  function saveVisibleViewportState() {
    for (let i = 0; i < viewportGrid.rows * viewportGrid.cols; i++) {
      const viewportId = `CT_${i}`;
      const renderingEngine = renderingEngineRef.current;
      if (renderingEngine) {
        const viewport = renderingEngine.getViewport(viewportId);
        if (viewport && typeof viewport.getProperties === "function") {
          prevViewportProps.current[i] = viewport.getProperties();
        }
        if (viewport && typeof viewport.getCurrentImageIdIndex === "function") {
          prevScrollIndices.current[i] = viewport.getCurrentImageIdIndex();
        }
      }
    }
  }

  useEffect(() => {
    const setup = async () => {
      console.log("setup")

      // 1. Initialize Cornerstone3D and tools ONCE
      if (!running.current) {
        running.current = true;
        await csRenderInit();
        await csToolsInit(
          { showSVGCursors: true, }
        );
        dicomImageLoaderInit({ maxWebWorkers: 1 });
      }


      // 3. Get or create the rendering engine
      let renderingEngine = renderingEngineRef.current;
      if (!renderingEngine) {
        renderingEngine = new RenderingEngine(renderingEngineId);
        renderingEngineRef.current = renderingEngine;
      }

      // --- In your setup function, use the correct tool group per viewport ---
      let mainToolGroup = ToolGroupManager.getToolGroup(MAIN_TOOL_GROUP_ID);
      if (!mainToolGroup) {
        mainToolGroup = setupTools(MAIN_TOOL_GROUP_ID)
      }


      const visibleVolumeViewports = viewportModes
        .slice(0, viewportGrid.rows * viewportGrid.cols)
        .filter(mode => mode === 'volume').length;

      // --- In your setup function, after tool group creation, enable/disable ReferenceLinesTool for each tool group ---
      updateActiveReferenceLineViewport(mainToolGroup)

      // 6. Enable and configure each viewport
      for (let i = 0; i < MAX_VIEWPORTS; i++) {

        let imageIds = viewportImageIds[i];
        if (!imageIds || imageIds.length === 0) continue;

        const viewportId = `CT_${i}`;
        const element = viewportRefs.current[i];
        if (!element) continue;

        const viewportType =
          viewportModes[i] === 'volume'
            ? Enums.ViewportType.ORTHOGRAPHIC // or Enums.ViewportType.VOLUME if supported
            : Enums.ViewportType.STACK;

        // Choose tool group based on viewport type
        //const toolGroup = viewportModes[i] === 'volume' ? volumeToolGroup : stackToolGroup;



        if (!element._cornerstoneEnabled || element._lastViewportType !== viewportType) {
          if (renderingEngine.getViewport(viewportId)) {
            renderingEngine.disableElement(viewportId);
          }
          renderingEngine.enableElement({
            viewportId,
            type: viewportType,
            element,
          });
          element._cornerstoneEnabled = true;
          element._lastViewportType = viewportType;
          //mainToolGroup.addViewport(viewportId, renderingEngineId);
          //toolGroup.addViewport(viewportId, renderingEngineId);
        }


        const viewport = renderingEngine.getViewport(viewportId);
        if (!viewport) continue;

        if (!element._resizeObserver) {
          const resizeObserver = new window.ResizeObserver(() => {
            if (element.offsetWidth > 0 && element.offsetHeight > 0) {
              renderingEngine.resize();
              // Always get the latest viewport instance
              const vp = renderingEngine.getViewport(viewportId);
              if (vp && typeof vp.resize === "function") {
                vp.resize();
              }
              if (vp && typeof vp.render === "function") {
                vp.render();
              }
            }
          });
          resizeObserver.observe(element);
          element._resizeObserver = resizeObserver;
        }

        if (viewportModes[i] === 'stack') {
          if (imageIds && !restoringStateRef.current) {
            viewport.setStack(imageIds);
          }
          // CrosshairsTool is not applicable for stack viewports
          if (!crossHairsEnabled) {
            mainToolGroup.addViewport(viewportId, renderingEngineId);
          } else {
            mainToolGroup.removeViewports(renderingEngineId, viewportId);
          }

          // Restore scroll, properties, etc.
        } else if (viewportModes[i] === 'volume') {
          mainToolGroup.addViewport(viewportId, renderingEngineId);

          //let sortedImageIds = imageIds;
          //if (imageIds && imageIds.length > 1) {
          //  sortedImageIds = [...imageIds].sort((a, b) => {
          //    const metaA = metaData.get('imagePlaneModule', a);
          //    const metaB = metaData.get('imagePlaneModule', b);
          //    const sliceA = metaA && metaA.sliceLocation !== undefined ? Number(metaA.sliceLocation) : 0;
          //    const sliceB = metaB && metaB.sliceLocation !== undefined ? Number(metaB.sliceLocation) : 0;
          //    return sliceA - sliceB;
          //  });
          //}

          // Use a unique volumeId per viewport
          const volumeId = `myVolumeId_${i}`;
          const volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: imageIds });
          volume.load();
          viewport.setVolumes([{ volumeId }]);
          viewport.render();
          continue
        }
        viewport.render();

        if (!restoringStateRef.current) {
          // Restore previous scroll position for existing viewports only
          if (
            prevScrollIndices.current[i] !== undefined &&
            typeof viewport.setImageIdIndex === "function"
          ) {
            //viewport.setImageIdIndex(prevScrollIndices.current[i]);
            csUtilities.jumpToSlice(viewport.element, { imageIndex: prevScrollIndices.current[i] })
          }


          // Restore previous state for existing viewports only
          const prevProps = prevViewportProps.current[i];
          if (prevProps && typeof viewport.setProperties === "function") {
            // Only restore safe properties
            const { voiRange, invert, interpolationType, VOILUTFunction, colormap } = prevProps;
            const propsToRestore: any = {};
            if (voiRange && voiRange.lower !== undefined && voiRange.upper !== undefined) propsToRestore.voiRange = voiRange;
            if (invert !== undefined) propsToRestore.invert = invert;
            if (interpolationType !== undefined) propsToRestore.interpolationType = interpolationType;
            if (VOILUTFunction) propsToRestore.VOILUTFunction = VOILUTFunction;
            if (
              colormap !== undefined &&
              colormap !== null &&
              (typeof colormap === "string" || (typeof colormap === "object" && !("actor" in colormap)))
            ) {
              propsToRestore.colormap = colormap;
            }
            setTimeout(() => {
              console.log("Restoring properties (timeout) for viewport", i, propsToRestore);
              viewport.setProperties(propsToRestore);
              viewport.render()
            }, 100);
          }

        }


        // Always (re-)attach overlay update for each viewport
        if ((element)._overlayListener) {
          // Remove previous listener if present
          viewport.element.removeEventListener(IMAGE_RENDERED, (element)._overlayListener);
        }
        const overlayListener = () => updateOverlay(i, viewport);
        viewport.element.addEventListener(IMAGE_RENDERED, overlayListener);
        (element)._overlayListener = overlayListener;
        updateOverlay(i, viewport);
      }

      const activeVolumeViewports = [];
      for (let i = 0; i < viewportGrid.rows * viewportGrid.cols; i++) {
        const viewportId = `CT_${i}`;
        const viewport = renderingEngineRef.current?.getViewport(viewportId);
        if (viewport && viewportModes[i] === 'volume') {
          activeVolumeViewports.push(viewport);
        }
      }
      // Find all active volume viewports
      const activeVolumeViewportIds = [];
      for (let i = 0; i < viewportGrid.rows * viewportGrid.cols; i++) {
        if (viewportModes[i] === 'volume') {
          activeVolumeViewportIds.push(`CT_${i}`);
        }
      }

      if (mainToolGroup && crossHairsEnabled) {
        // Only enable CrosshairsTool if at least two volume viewports are active
        if (activeVolumeViewportIds.length >= 2) {
          // Make sure CrosshairsTool is added to the tool group
          if (!mainToolGroup.hasTool(CrosshairsTool.toolName)) {
            mainToolGroup.addTool(CrosshairsTool.toolName);
          }
          mainToolGroup.setToolEnabled(CrosshairsTool.toolName);
          mainToolGroup.setToolConfiguration(CrosshairsTool.toolName, {
            // Example: color: '#ffeb3b', lineWidth: 2
          });
          mainToolGroup.setToolActive(CrosshairsTool.toolName, {
            bindings: [{ mouseButton: MouseBindings.Primary }],
          });

        } else {
          // Disable CrosshairsTool if not enough volume viewports
          if (mainToolGroup.hasTool(CrosshairsTool.toolName)) {
            mainToolGroup.setToolDisabled(CrosshairsTool.toolName);
          }
        }
      }

      // 7. Apply mouse tool bindings
      applyMouseToolBindings();
    };

    // Use requestAnimationFrame to ensure DOM is painted before setup
    const raf = requestAnimationFrame(() => {
      setup();
    });

    // Cleanup: disconnect ResizeObservers
    return () => {
      viewportRefs.current.forEach(el => {
        if (el && (el)._resizeObserver) {
          (el)._resizeObserver.disconnect();
          delete (el)._resizeObserver;
        }
      });
      cancelAnimationFrame(raf);
    };
  }, [elementRef, updateOverlay, viewportGrid, viewportModes, viewportImageIds]);


  // Handler to reset the view (camera and window/level)
  const handleDoubleClick = useCallback((viewportIdx: number) => {
    const viewportId = `CT_${viewportIdx}`;
    const renderingEngine = renderingEngineRef.current;
    const viewport = renderingEngine.getViewport(viewportId);
    if (viewport && typeof viewport.resetCamera === "function") {
      viewport.resetCamera();

      console.log("Restoring viewport properties for viewport", viewportIdx);
      viewport.resetProperties(); // Resets window/level and other properties
      viewport.render();
    }
  }, [])

  // Handler to apply a preset for a specific viewport
  const handleApplyPreset = useCallback((viewportIdx: number, center: number, width: number) => {
    const viewportId = `CT_${viewportIdx}`;
    try {
      const renderingEngine = renderingEngineRef.current;
      if (renderingEngine) {
        const viewport = renderingEngine.getViewport(viewportId);
        if (viewport && typeof viewport.setProperties === "function") {
          console.log(`Applying preset to viewport ${viewportIdx}: center=${center}, width=${width}`);
          viewport.setProperties({
            voiRange: {
              lower: center - width / 2,
              upper: center + width / 2,
            },
          });
          viewport.render();
          updateOverlay(viewportIdx, viewport);
        }
      }
    } catch (e) {
      // Ignore if not found
    }
  }, [updateOverlay]);

  // Update overlay and preset state arrays when grid changes
  useEffect(() => {
    setImageInfos(Array(viewportGrid.rows * viewportGrid.cols).fill({
      index: 0,
      total: 0,
      windowCenter: "",
      windowWidth: "",
    }));
    setPresetsOpenArr(Array(viewportGrid.rows * viewportGrid.cols).fill(false));
  }, [viewportGrid]);

  // Re-apply bindings when tool sets or ctrl changes
  useEffect(() => {
    applyMouseToolBindings()
  }, [mouseToolBindings, ctrlMouseToolBindings, ctrlPressed, applyMouseToolBindings])

  // Close the menu when clicking outside
  useEffect(() => {
    if (!presetsOpen) return;
    const handleClick = (e: MouseEvent) => {
      const menu = document.querySelector(".preset-menu");
      if (menu && !menu.contains(e.target as Node)) {
        setPresetsOpen(false);
      }
    };
    document.addEventListener("mousedown", handleClick);
    return () => document.removeEventListener("mousedown", handleClick);
  }, [presetsOpen]);

  useEffect(() => {
    const handleContextMenu = (e: MouseEvent) => {
      e.preventDefault();
    };
    document.addEventListener("contextmenu", handleContextMenu);
    return () => {
      document.removeEventListener("contextmenu", handleContextMenu);
    };
  }, []);

  useEffect(() => {
    if (!referenceLinesEnabled) return;
    const mainToolGroup = ToolGroupManager.getToolGroup(MAIN_TOOL_GROUP_ID);
    updateActiveReferenceLineViewport(mainToolGroup)
  }, [activeViewport, referenceLinesEnabled]);

  useEffect(() => {
    if (fileInputRef.current) {
      fileInputRef.current.setAttribute("webkitdirectory", "true");
      fileInputRef.current.setAttribute("directory", "true");
    }
  }, []);


  useEffect(() => {
    // API: Load a stack from a list of wadouri imageIds (strings)
    window.loadDicomStackFromWadouriList = async (wadouriImageIds: string[]) => {
      if (!wadouriImageIds || wadouriImageIds.length === 0) return;

      // Try to extract StudyInstanceUID from the first image if possible
      let studyInstanceUID: string | undefined = undefined;
      try {
        const firstId = wadouriImageIds[0];
        if (firstId && firstId.startsWith("wadouri:")) {
          const url = firstId.slice(8);
          const response = await fetch(url);
          const arrayBuffer = await response.arrayBuffer();
          const dataSet = dicomParser.parseDicom(new Uint8Array(arrayBuffer));
          studyInstanceUID = dataSet.string('x0020000d');
        }
      } catch (e) {
        // Ignore errors, fallback to undefined
      }

      setAvailableStacks(prev => {
        const next = [...prev, { imageIds: wadouriImageIds, studyInstanceUID }];
        setViewportImageIds(vpPrev => {
          const vpNext = [...vpPrev];
          vpNext[0] = wadouriImageIds;
          return vpNext;
        });
        setSelectedStackIdx(selPrev => {
          const selNext = [...selPrev];
          selNext[0] = next.length - 1;
          return selNext;
        });
        setLoadedImageIdsAndCacheMeta(wadouriImageIds);
        return next;
      });
    };

    // Cleanup
    return () => {
      window.loadDicomStackFromWadouriList = undefined;
    };
  }, [setAvailableStacks, setViewportImageIds, setSelectedStackIdx, setLoadedImageIdsAndCacheMeta]);

  useEffect(() => {
    if (!orderBySliceLocation) return;

    // Only sort if all metadata is loaded for all viewports
    const allLoaded = viewportImageIds.every(imageIds =>
      !imageIds || imageIds.length === 0 ||
      imageIds.every(id => !!metaData.get("imagePlaneModule", id))
    );
    if (!allLoaded) return;

    setViewportImageIds(prev => {
      const next = prev.map(imageIds => {
        if (!imageIds || imageIds.length === 0) return imageIds;

        const withSliceLoc = imageIds.map(id => ({
          imageId: id,
          sliceLoc: (() => {
            const meta = metaData.get('imagePlaneModule', id);
            return meta && meta.sliceLocation !== undefined ? Number(meta.sliceLocation) : null;
          })(),
        }));

        const sorted = [...withSliceLoc].sort((a, b) => {
          if (a.sliceLoc !== null && b.sliceLoc !== null) {
            return a.sliceLoc - b.sliceLoc;
          }
          return a.imageId.localeCompare(b.imageId);
        });

        const sortedIds = sorted.map(obj => obj.imageId);
        if (JSON.stringify(sortedIds) === JSON.stringify(imageIds)) {
          return imageIds;
        }
        return sortedIds;
      });
      return next;
    });
  }, [orderBySliceLocation, availableStacks]);


  function updateActiveReferenceLineViewport(mainToolGroup: cornerstoneTools.Types.IToolGroup) {
    // Ensure the main tool group is defined (it won't be during setup)
    if (!mainToolGroup) return;
    const sourceViewportId = `CT_${activeViewport}`
    if (referenceLinesEnabled) {
      mainToolGroup.setToolEnabled(ReferenceLinesTool.toolName)
      mainToolGroup.setToolConfiguration(ReferenceLinesTool.toolName, {
        sourceViewportId,
      })
    } else {
      mainToolGroup.setToolDisabled(ReferenceLinesTool.toolName)
    }
  }
  useEffect(() => {
    const apiKey = container_id || "default";

  function jumpToSliceByImageId(viewportIdx: number, imageId: string) {
    const renderingEngine = renderingEngineRef.current;
    if (!renderingEngine) return;
    const viewport = renderingEngine.getViewport(`CT_${viewportIdx}`);
    if (!viewport || typeof viewport.getImageIds !== "function") return;
    const imageIds = viewport.getImageIds();
    // Some stacks may have imageIds with or without "wadouri:" prefix, so normalize for comparison
    const normImageIds = imageIds.map(id => id.replace(/^wadouri:/, ""));
    const normTarget = imageId.replace(/^wadouri:/, "");
    const idx = normImageIds.indexOf(normTarget);
    if (idx !== -1 && typeof viewport.setImageIdIndex === "function") {
      csUtilities.jumpToSlice(viewport.element, { imageIndex: idx });
      updateOverlay(viewportIdx, viewport);
    }
  }
    // Attach to window with a unique name
    window[`jumpToSliceByImageId_${apiKey}`] = jumpToSliceByImageId;


    // Export the current viewer state as JSON
    window[`exportViewerState_${apiKey}`] = () => {
      const numActive = viewportGrid.rows * viewportGrid.cols;
      const modes = viewportModes.slice(0, numActive);
      const stackIdx = selectedStackIdx.slice(0, numActive);
      const stacks = availableStacks.map(s => ({
        i: s.imageIds, // imageIds
        u: s.studyInstanceUID // studyInstanceUID
      }));

      // Only keep minimal properties
      const renderingEngine = renderingEngineRef.current;
      const props: any[] = [];
      const stackPos: number[] = [];
      const cam: any[] = [];
      for (let i = 0; i < numActive; i++) {
        let p = null, s = null, c = null;
        if (renderingEngine) {
          const viewportId = `CT_${i}`;
          const viewport = renderingEngine.getViewport(viewportId);
          if (viewport) {
            if (typeof viewport.getProperties === "function") {
              const { voiRange, invert, interpolationType, VOILUTFunction, colormap } = viewport.getProperties();
              p = { voiRange, invert, interpolationType, VOILUTFunction, colormap };
            }
            if (typeof viewport.getCurrentImageIdIndex === "function") {
              s = viewport.getCurrentImageIdIndex();
            }
            if (typeof viewport.getCamera === "function") {
              c = viewport.getCamera();
            }
          }
        }
        props.push(p);
        stackPos.push(s);
        cam.push(c);
      }

      // Use short keys for compactness
      const state = {
        grid: viewportGrid,
        modes,
        stacks,
        stackIdx,
        stackPos,
        props,
        cam,
      };
      return JSON.stringify(state);
    };

    // Import and restore a viewer state from JSON (using stack indices)
    window[`importViewerState_${apiKey}`] = (json: string) => {
      try {
        restoringStateRef.current = true;
        const state = typeof json === "string" ? JSON.parse(json) : json;
        if (state.grid) setViewportGrid(state.grid);
        if (state.modes) setViewportModes(state.modes);
        if (state.stacks) setAvailableStacks(state.stacks.map((s: any) => ({
          imageIds: s.i,
          studyInstanceUID: s.u,
        })));
        if (state.stackIdx && state.stacks) {
          const reconstructed = state.stackIdx.map(
            (idx: number) => state.stacks[idx]?.i || []
          );
          setViewportImageIds(reconstructed);
          setSelectedStackIdx(state.stackIdx);
        }

        setTimeout(() => {
          const renderingEngine = renderingEngineRef.current;
          if (!renderingEngine) {
            restoringStateRef.current = false;
            return;
          }
          const numActive = state.grid?.rows * state.grid?.cols || 1;
          for (let i = 0; i < numActive; i++) {
            const viewportId = `CT_${i}`;
            const viewport = renderingEngine.getViewport(viewportId);
            if (!viewport) continue;

            // Restore stack position
            if (
              state.stackPos &&
              typeof state.stackPos[i] === "number" &&
              typeof viewport.setImageIdIndex === "function"
            ) {
              if (csUtilities && csUtilities.jumpToSlice) {
                csUtilities.jumpToSlice(viewport.element, { imageIndex: state.stackPos[i] });
              } else {
                viewport.setImageIdIndex(state.stackPos[i]);
              }
            }

            // Restore properties
            if (
              state.props &&
              state.props[i] &&
              typeof viewport.setProperties === "function"
            ) {
              viewport.setProperties(state.props[i]);
              viewport.render();
            }

            // Restore camera
            if (
              state.cam &&
              state.cam[i] &&
              typeof viewport.setCamera === "function"
            ) {
              viewport.setCamera(state.cam[i]);
              viewport.render();
            }
          }
          restoringStateRef.current = false;
        }, 300);
      } catch (e) {
        restoringStateRef.current = false;
        alert("Failed to import viewer state: " + e);
      }
    };

    // Export all annotations using cornerstoneTools.annotation.state.getAllAnnotations()
    window[`exportAnnotations_${apiKey}`] = () => {
      try {
        let allAnnotations = cornerstoneTools.annotation.state.getAllAnnotations();
        console.log("Exporting annotations:", allAnnotations);
        // Remove ReferenceLines annotations (toolName may be 'ReferenceLines')
        if (Array.isArray(allAnnotations)) {
          // Only keep annotations with toolName in the allowed list
          const allowedTools = [
            LengthTool.toolName,
            NoLabelArrowAnnotateTool.toolName,
            ArrowAnnotateTool.toolName,
            RectangleROITool.toolName,
            EllipticalROITool.toolName,
            PlanarFreehandROITool.toolName,
            PlanarFreehandContourSegmentationTool.toolName,
            ProbeTool.toolName,
            SculptorTool.toolName,
          ];
          allAnnotations = allAnnotations.filter(
            ann => allowedTools.includes(ann.metadata.toolName)
          );
        } else {
          return "{}"; // If no annotations found, return empty object
        }
        return JSON.stringify(allAnnotations, null, 2);
      } catch (e) {
        alert("Failed to export annotations: " + e);
        return "{}";
      }
    };

    // Import and restore all annotations using cornerstoneTools.annotation.state.restoreAnnotations()
    window[`importAnnotations_${apiKey}`] = (json: string) => {
      try {
        const annotationData = typeof json === "string" ? JSON.parse(json) : json;
        // Remove all existing annotations if needed
        if (cornerstoneTools.annotation.state.removeAllAnnotations) {
          cornerstoneTools.annotation.state.removeAllAnnotations();
        }
        // Add each annotation from the imported data
        if (Array.isArray(annotationData)) {
          // For now just add them to a new group
          //const group = new cornerstoneTools.annotation.AnnotationGroup();
          annotationData.forEach(ann => {
            cornerstoneTools.annotation.state.addAnnotation(ann, ann.annotationUID);
          });
        }
        if (renderingEngineRef.current) {
          renderingEngineRef.current.render();
        }
      } catch (e) {
        alert("Failed to import annotations: " + e);
      }
    };

    window[`importLegacyAnnotations_${apiKey}`] = (json: string) => {
      // Remove all existing annotations
      if (cornerstoneTools.annotation.state.removeAllAnnotations) {
        cornerstoneTools.annotation.state.removeAllAnnotations();
      }

      let legacyData: any;
      try {
        legacyData = typeof json === "string" ? JSON.parse(json) : json;
      } catch (e) {
        alert("Invalid legacy annotation JSON");
        return;
      }

      const toolNameMap: Record<string, string> = {
        ArrowAnnotate: "ArrowAnnotate",
        // Add more mappings if needed
      };

      Object.entries(legacyData).forEach(([imageId, tools]) => {
        console.log("Importing legacy annotations for imageId:", imageId);
        Object.entries(tools as any).forEach(([legacyTool, toolData]: [string, any]) => {
          const cs3Tool = toolNameMap[legacyTool] || legacyTool;
          if (toolData.data && Array.isArray(toolData.data)) {
            toolData.data.forEach((ann: any) => {
              // Convert pixel handles to world coordinates
              const startWorld = pixelToWorld(imageId, { x: ann.handles.start.y, y: ann.handles.start.x });
              const endWorld = pixelToWorld(imageId, { x: ann.handles.end.y, y: ann.handles.end.x });

              console.log("Importing legacy annotation:")
              console.log("  Image ID:", imageId);
              console.log("  Start World:", startWorld);
              console.log("  End World:", endWorld);

              // Get spatial metadata
              const plane = metaData.get("imagePlaneModule", imageId);
              console.log(metaData);
              let FrameOfReferenceUID = metaData.get("frameOfReferenceUID", imageId);
              if (!FrameOfReferenceUID && plane && plane.frameOfReferenceUID) {
                FrameOfReferenceUID = plane.frameOfReferenceUID;
              }
              const viewPlaneNormal =
                plane?.rowCosines && plane?.columnCosines
                  ? [
                    plane.rowCosines[1] * plane.columnCosines[2] - plane.rowCosines[2] * plane.columnCosines[1],
                    plane.rowCosines[2] * plane.columnCosines[0] - plane.rowCosines[0] * plane.columnCosines[2],
                    plane.rowCosines[0] * plane.columnCosines[1] - plane.rowCosines[1] * plane.columnCosines[0],
                  ]
                  : undefined;

              // Only include viewPlaneNormal if it's a valid 3-element array
              const metadata: any = {
                toolName: cs3Tool,
                referencedImageId: imageId,
                FrameOfReferenceUID,
              };
              if (Array.isArray(viewPlaneNormal) && viewPlaneNormal.length === 3) {
                metadata.viewPlaneNormal = viewPlaneNormal as [number, number, number];
              }


              // --- Add rotation if present in legacy annotation ---
              // Does this actually exist?
              if (typeof ann.rotation === "number") {
                metadata.rotation = ann.rotation;
              }

              const annotation = {
                annotationUID: ann.uuid, // || cornerstoneTools.utilities.uuidv4(),
                metadata: {
                  toolName: cs3Tool,
                  referencedImageId: imageId,
                  FrameOfReferenceUID,
                  viewPlaneNormal,
                  // Optionally add viewUp, cameraFocalPoint, etc.
                },
                data: {
                  handles: {
                    points: [startWorld, endWorld],
                    textBox: ann.handles.textBox,
                  },
                  text: ann.text || "",
                  // Add more fields as needed
                },
                visibility: ann.visible !== false,
                active: ann.active || false,
              };
              // @ts-expect-error legacy
              cornerstoneTools.annotation.state.addAnnotation(annotation, annotation.annotationUID);
            });
          }
        });
      });

      if (renderingEngineRef.current) {
        renderingEngineRef.current.render();
      }
    };

    // Import and apply a legacy viewport state to the active viewport
    window[`importLegacyViewport_${apiKey}`] = (json: string) => {
      let legacy: any;
      try {
        legacy = typeof json === "string" ? JSON.parse(json) : json;
      } catch (e) {
        alert("Invalid legacy viewport JSON");
        return;
      }

      const renderingEngine = renderingEngineRef.current;
      if (!renderingEngine) return;
      const viewportId = `CT_${activeViewport ?? 0}`;
      const viewport = renderingEngine.getViewport(viewportId);
      if (!viewport) return;

      // Map legacy fields to Cornerstone3D properties
      const props: any = {};
      if (legacy.voi) {
        props.voiRange = {
          lower: legacy.voi.windowCenter - legacy.voi.windowWidth / 2,
          upper: legacy.voi.windowCenter + legacy.voi.windowWidth / 2,
        };
      }
      if (typeof legacy.invert === "boolean") props.invert = legacy.invert;
      if (typeof legacy.pixelReplication === "boolean") props.interpolationType = legacy.pixelReplication ? 0 : 1;
      if (typeof legacy.hflip === "boolean") props.hflip = legacy.hflip;
      if (typeof legacy.vflip === "boolean") props.vflip = legacy.vflip;

      if (typeof viewport.setProperties === "function") {
        viewport.setProperties(props);
      }

      // Apply translation and scale (pan/zoom)
      if (legacy.translation || legacy.scale) {
        const camera = viewport.getCamera ? viewport.getCamera() : {};
        if (legacy.translation) {
          camera.translation = { ...camera.translation, ...legacy.translation };
        }
        if (legacy.scale) {
          camera.parallelScale = 200 / legacy.scale;
        }
        console.log("Applying legacy camera properties:", camera);
        if (typeof viewport.setCamera === "function") {
          viewport.setCamera(camera);
        }
      }
      // --- Fix: Import rotation from legacy viewport ---
      if (typeof legacy.rotation === "number") {
        viewport.setRotation(legacy.rotation);
      }

      viewport.render();
      updateOverlay(activeViewport ?? 0, viewport);
    };



    // Cleanup
    return () => {
      window[`exportViewerState_${apiKey}`] = undefined;
      window[`importViewerState_${apiKey}`] = undefined;
      window[`exportAnnotations_${apiKey}`] = undefined;
      window[`importAnnotations_${apiKey}`] = undefined;
      window[`importLegacyAnnotations_${apiKey}`] = undefined;
      window[`importLegacyViewport_${apiKey}`] = undefined;
      window[`jumpToSliceByImageId_${apiKey}`] = undefined;
    };
  }, [
    viewportGrid,
    viewportModes,
    selectedStackIdx,
    availableStacks,
    viewportImageIds,
    setViewportGrid,
    setViewportModes,
    setSelectedStackIdx,
    setAvailableStacks,
    setViewportImageIds,
  ]);



  // Reference stack and UID for study comparison (use active viewport)
  const referenceStackIdx = activeViewport !== null ? selectedStackIdx[activeViewport] : 0;
  const referenceStackObj = availableStacks[referenceStackIdx];
  const referenceUID = referenceStackObj?.studyInstanceUID;

  const [selectedAnnUIDs, setSelectedAnnUIDs] = useState<(string | null)[]>(() =>
    Array(MAX_VIEWPORTS).fill(null)
  );

  const numVisible = viewportGrid.rows * viewportGrid.cols;
  const viewports = [];
  //for (let i = 0; i < MAX_VIEWPORTS; i++) {
  // Viewports rendering loop
  for (let i = 0; i < numVisible; i++) {
    // Determine if this viewport should be visible in the current grid
    const isVisible = i < viewportGrid.rows * viewportGrid.cols;

    viewports.push(
      <div
        key={i}
        ref={el => (viewportRefs.current[i] = el)}
        style={{
          flex: 1,
          border: isVisible ? "1px solid #222" : "none", // Only show border if visible
          background: "#000",
          minWidth: 0,
          minHeight: 0,
          position: "relative",
          overflow: "hidden",
          flexDirection: "column",
          gridRow: Math.floor(i / viewportGrid.cols) + 1,
          gridColumn: (i % viewportGrid.cols) + 1,
          height: "100%",
          //visibility: isVisible ? "visible" : "hidden",
          //pointerEvents: isVisible ? "auto" : "none",
          // Remove outline, use only boxShadow for indicator
          boxShadow: activeViewport === i
            ? "0 0 0 4px #1976d2, 0 0 16px 4px #1976d2cc"
            : undefined,
          // cursor: "pointer",
          zIndex: activeViewport === i ? 10 : 1,
        }}
        onClick={() => setActiveViewport(i)}
        onDoubleClick={() => handleDoubleClick(i)}
      >
        <button
          style={{
            position: "absolute",
            top: 8,
            left: 8,
            zIndex: 20,
            background: "#333",
            color: "#fff",
            border: "none",
            borderRadius: "4px",
            padding: "4px 8px",
            cursor: "pointer",
            fontSize: "12px",
            opacity: 0.85,
          }}
          onClick={() => {
            setViewportModes(prev => {
              const next = [...prev];
              next[i] = prev[i] === 'stack' ? 'volume' : 'stack';
              return next;
            });
          }}
        >
          {viewportModes[i] === 'stack' ? 'Volume' : 'Stack'}
        </button>
        {viewportModes[i] === 'volume' && (
          <div
            style={{
              position: "absolute",
              top: 8,
              left: 70,
              zIndex: 21,
              display: "flex",
              gap: "6px",
              background: "rgba(34,34,34,0.85)",
              borderRadius: "4px",
              padding: "2px 6px",
              alignItems: "center",
            }}
          >
            <button
              style={{
                background: "#444",
                color: "#fff",
                border: "none",
                borderRadius: "3px",
                padding: "2px 8px",
                cursor: "pointer",
                fontSize: "12px",
                marginRight: "2px",
              }}
              onClick={() => {
                const renderingEngine = renderingEngineRef.current;
                const viewport = renderingEngine?.getViewport(`CT_${i}`);
                if (viewport && viewport.setOrientation) {
                  viewport.setOrientation("axial");
                  viewport.render();
                }
              }}
            >
              Axial
            </button>
            <button
              style={{
                background: "#444",
                color: "#fff",
                border: "none",
                borderRadius: "3px",
                padding: "2px 8px",
                cursor: "pointer",
                fontSize: "12px",
                marginRight: "2px",
              }}
              onClick={() => {
                const renderingEngine = renderingEngineRef.current;
                const viewport = renderingEngine?.getViewport(`CT_${i}`);
                if (viewport && viewport.setOrientation) {
                  viewport.setOrientation("sagittal");
                  viewport.render();
                }
              }}
            >
              Sagittal
            </button>
            <button
              style={{
                background: "#444",
                color: "#fff",
                border: "none",
                borderRadius: "3px",
                padding: "2px 8px",
                cursor: "pointer",
                fontSize: "12px",
              }}
              onClick={() => {
                const renderingEngine = renderingEngineRef.current;
                const viewport = renderingEngine?.getViewport(`CT_${i}`);
                if (viewport && viewport.setOrientation) {
                  viewport.setOrientation("coronal");
                  viewport.render();
                }
              }}
            >
              Coronal
            </button>
          </div>
        )}
        {/* Fullscreen button below the Volume/Stack toggle */}
        <button
          style={{
            position: "absolute",
            top: 40,
            left: 8,
            zIndex: 20,
            background: "#111",
            color: "#fff",
            border: "none",
            borderRadius: "4px",
            padding: "4px 8px",
            cursor: "pointer",
            fontSize: "13px",
            opacity: 0.85,
            marginTop: "4px",
            display: "flex",
            alignItems: "center",
            gap: "6px",
          }}
          title="Toggle Fullscreen"
          onClick={e => {
            e.stopPropagation();
            const el = viewportRefs.current[i];
            if (document.fullscreenElement === el) {
              document.exitFullscreen();
            } else if (el?.requestFullscreen) {
              el.requestFullscreen();
            } else if ((el as any)?.webkitRequestFullscreen) {
              (el as any).webkitRequestFullscreen();
            } else if ((el as any)?.msRequestFullscreen) {
              (el as any).msRequestFullscreen();
            }
          }}
          onMouseEnter={() => setFullscreenHoverIdx(i)}
          onMouseLeave={() => setFullscreenHoverIdx(null)}
        >
          <span style={{ fontSize: "18px", lineHeight: 1 }}>⛶</span>
          {fullscreenHoverIdx === i && (
            <span style={{ marginLeft: 6 }}>
              {document.fullscreenElement === viewportRefs.current[i]
                ? "Exit Fullscreen"
                : "Fullscreen"}
            </span>
          )}
        </button>
        // Annotations buttons
        {viewportModes[i] === "stack" && (() => {
          // Get all annotations for this stack
          const imageIds = viewportImageIds[i];
          const allAnnotations = cornerstoneTools.annotation.state.getAllAnnotations() || [];
          const stackAnnotations = allAnnotations.filter(
            ann =>
              ann.metadata &&
              ann.metadata.referencedImageId &&
              imageIds.includes(ann.metadata.referencedImageId) &&
              ann.metadata.toolName !== ReferenceCursors.toolName // Exclude ReferenceCursors
          );
          if (!stackAnnotations.length) return null;
          // Find the annotation(s) on the current image
          const renderingEngine = renderingEngineRef.current;
          const viewport = renderingEngine?.getViewport(`CT_${i}`);
          const currentIdx = viewport?.getCurrentImageIdIndex?.() ?? 0;
          const currentImageId = imageIds[currentIdx];
          const currentAnnotations = stackAnnotations.filter(
            ann => ann.metadata.referencedImageId === currentImageId
          );

          // Use per-viewport selection state
          const selectedAnnUID = selectedAnnUIDs[i];





          return (
            <div
              style={{
                position: "absolute",
                left: "50%",
                bottom: 60,
                transform: "translateX(-50%)",
                zIndex: 30,
                display: "flex",
                gap: 4,
                pointerEvents: "auto",
                alignItems: "center",
              }}
            >
              {/* Prev/Next buttons ... */}
              <button
                style={{
                  background: "rgba(30,30,30,0.7)",
                  color: "#fff",
                  border: "none",
                  borderRadius: "50%",
                  width: 28,
                  height: 28,
                  fontSize: "16px",
                  fontWeight: "bold",
                  cursor: "pointer",
                  opacity: 0.7,
                  display: "flex",
                  alignItems: "center",
                  justifyContent: "center",
                  boxShadow: "0 1px 4px rgba(0,0,0,0.18)",
                  transition: "opacity 0.2s",
                }}
                title="Previous Annotation"
                aria-label="Previous Annotation"
                onClick={() => {
                  const renderingEngine = renderingEngineRef.current;
                  const viewport = renderingEngine?.getViewport(`CT_${i}`);
                  if (!viewport || typeof viewport.getCurrentImageIdIndex !== "function") return;
                  const currentIdx = viewport.getCurrentImageIdIndex();
                  const prevIdx = findNextAnnotationIdx(currentIdx, imageIds, stackAnnotations, -1);
                  if (prevIdx !== null && typeof viewport.setImageIdIndex === "function") {
                    csUtilities.jumpToSlice(viewport.element, { imageIndex: prevIdx });
                    updateOverlay(i, viewport);
                    // Select the first annotation on the new image
                    const newImageId = imageIds[prevIdx];
                    const annsOnImage = stackAnnotations.filter(
                      ann => ann.metadata.referencedImageId === newImageId
                    );
                    handleSelectAnnotation(i, annsOnImage.length > 0 ? annsOnImage[0].annotationUID : null);
                  }
                }}
              >
                <span aria-hidden="true">◀</span>
              </button>
              <button
                style={{
                  background: "rgba(30,30,30,0.7)",
                  color: "#fff",
                  border: "none",
                  borderRadius: "50%",
                  width: 28,
                  height: 28,
                  fontSize: "16px",
                  fontWeight: "bold",
                  cursor: "pointer",
                  opacity: 0.7,
                  display: "flex",
                  alignItems: "center",
                  justifyContent: "center",
                  boxShadow: "0 1px 4px rgba(0,0,0,0.18)",
                  transition: "opacity 0.2s",
                }}
                title="Next Annotation"
                aria-label="Next Annotation"
                onClick={() => {
                  const renderingEngine = renderingEngineRef.current;
                  const viewport = renderingEngine?.getViewport(`CT_${i}`);
                  if (!viewport || typeof viewport.getCurrentImageIdIndex !== "function") return;
                  const currentIdx = viewport.getCurrentImageIdIndex();
                  const nextIdx = findNextAnnotationIdx(currentIdx, imageIds, stackAnnotations, 1);
                  if (nextIdx !== null && typeof viewport.setImageIdIndex === "function") {
                    csUtilities.jumpToSlice(viewport.element, { imageIndex: nextIdx });
                    updateOverlay(i, viewport);
                    // Select the first annotation on the new image
                    const newImageId = imageIds[nextIdx];
                    const annsOnImage = stackAnnotations.filter(
                      ann => ann.metadata.referencedImageId === newImageId
                    );
                    handleSelectAnnotation(i, annsOnImage.length > 0 ? annsOnImage[0].annotationUID : null);
                  }
                }}
              >
                <span aria-hidden="true">▶</span>
              </button>
              {/* List and select/delete individual annotations */}
              {currentAnnotations.length > 0 && (
                <div style={{ display: "flex", gap: 2, alignItems: "center" }}>
                  {currentAnnotations.map(ann => (
                    <div
                      key={ann.annotationUID}
                      style={{
                        display: "flex",
                        alignItems: "center",
                        background: selectedAnnUID === ann.annotationUID ? "rgb(4, 225, 0)" : "#444",
                        color: selectedAnnUID === ann.annotationUID ? "#222" : "#fff",
                        borderRadius: "4px",
                        padding: "2px 6px",
                        marginRight: 2,
                        cursor: "pointer",
                        border: selectedAnnUID === ann.annotationUID ? "2px solid #1976d2" : "none",
                        fontWeight: selectedAnnUID === ann.annotationUID ? "bold" : "normal",
                      }}
                      onClick={() => handleSelectAnnotation(i, ann.annotationUID)}
                    >
                      <span style={{ marginRight: 4 }}>
                        {selectedAnnUID === ann.annotationUID ? "★ " : ""}
                        {ann.metadata.toolName || "Annotation"}
                      </span>
                      <button
                        style={{
                          background: "#b71c1c",
                          color: "#fff",
                          border: "none",
                          borderRadius: "50%",
                          width: 20,
                          height: 20,
                          fontSize: "13px",
                          fontWeight: "bold",
                          cursor: "pointer",
                          marginLeft: 2,
                          display: "flex",
                          alignItems: "center",
                          justifyContent: "center",
                          opacity: 0.85,
                        }}
                        title="Delete this annotation"
                        aria-label="Delete annotation"
                        onClick={e => {
                          e.stopPropagation();
                          handleDeleteAnnotation(i, ann.annotationUID, currentAnnotations);
                        }}
                      >
                        🗑️
                      </button>
                    </div>
                  ))}
                </div>
              )}
            </div>
          );
        })()}
// ...existing code...
        {/* Overlay info (bottom left of viewport) */}
        <div
          style={{
            position: "absolute",
            left: 8,
            bottom: 8,
            background: "rgba(0,0,0,0.7)",
            color: "#fff",
            padding: "6px 12px",
            borderRadius: "4px",
            fontSize: "14px",
            zIndex: 10,
            pointerEvents: "none",
            minWidth: "120px",
            userSelect: "none",
          }}
        >
          {viewportModes[i] === "stack" ? (
            <>
              <div>
                Image: {imageInfos[i]?.index} / {imageInfos[i]?.total}
              </div>
              <div>
                WC: {imageInfos[i]?.windowCenter} &nbsp; WW: {imageInfos[i]?.windowWidth}
              </div>
            </>
          ) : (
            // Volume mode: do not show image position
            <div>
              WC: {imageInfos[i]?.windowCenter} &nbsp; WW: {imageInfos[i]?.windowWidth}
            </div>
          )}
        </div>

        {/* Preset menu (bottom right of viewport) */}
        <div
          style={{
            position: "absolute",
            right: 8,
            bottom: 8,
            zIndex: 10,
          }}
        >
          <div
            className="preset-menu"
            style={{
              background: "rgba(0,0,0,0.7)",
              color: "#fff",
              borderRadius: "4px",
              fontSize: "14px",
              minWidth: "120px",
              overflow: "hidden",
              cursor: "pointer",
              position: "relative",
              boxShadow: presetsOpenArr[i] ? "0 2px 8px rgba(0,0,0,0.3)" : undefined,
            }}
          >
            <div
              style={{
                padding: "6px 12px",
                fontWeight: "bold",
                userSelect: "none",
              }}
              onClick={() =>
                setPresetsOpenArr(prev => {
                  const next = [...prev];
                  next[i] = !next[i];
                  return next;
                })
              }
            >
              Presets
              <span style={{ float: "right", fontWeight: "normal" }}>
                {presetsOpenArr[i] ? "▲" : "▼"}
              </span>
            </div>
            {presetsOpenArr[i] && (
              <div
                className="preset-menu-content"
                style={{
                  background: "rgba(0,0,0,0.95)",
                  borderRadius: "0 0 4px 4px",
                  padding: "6px 0 6px 0",
                  display: "flex",
                  flexDirection: "column",
                  zIndex: 11,
                }}
              >
                {WINDOW_LEVEL_PRESETS.map((preset) => (
                  <button
                    key={preset.name}
                    style={{
                      display: "block",
                      width: "100%",
                      margin: "2px 0",
                      background: "#333",
                      color: "#fff",
                      border: "none",
                      borderRadius: "3px",
                      cursor: "pointer",
                      padding: "4px 0",
                      fontSize: "13px",
                      opacity: 0.9,
                    }}
                    onClick={() => {
                      handleApplyPreset(i, preset.center, preset.width);
                      setPresetsOpenArr(prev => {
                        const next = [...prev];
                        next[i] = false;
                        return next;
                      });
                    }}
                  >
                    {preset.name}
                  </button>
                ))}
              </div>
            )}
          </div>
        </div>
        {/* Position bar inside each viewport */}
        {viewportModes[i] === "stack" && viewportImageIds[i] && (
          <div
            style={{
              position: "absolute",
              top: "10%",
              right: 8,
              width: "12px",
              height: "80%",
              maxHeight: "100%",
              minHeight: 0,
              zIndex: 30,
              display: viewportImageIds[i].length > 0 ? "flex" : "none",
              alignItems: "flex-start",
              justifyContent: "center",
              pointerEvents: "none",
              flexDirection: "column",
              padding: 0,
            }}
          >
            <div
              style={{
                width: "8px",
                height: "100%",
                maxHeight: "100%",
                minHeight: 0,
                borderRadius: "6px",
                background: "#222",
                position: "relative",
                display: "flex",
                flexDirection: "column",
                justifyContent: "flex-start",
                boxShadow: "0 0 8px #2196f3cc",
                overflow: "hidden",
              }}
            >
              {viewportImageIds[i].map((id, idx) => {
                const loaded = !!metaData.get("imagePlaneModule", id);
                const renderingEngine = renderingEngineRef.current;
                let currentIdx = -1;
                if (
                  renderingEngine &&
                  typeof renderingEngine.getViewport === "function"
                ) {
                  const viewport = renderingEngine.getViewport(`CT_${i}`);
                  if (viewport && typeof viewport.getCurrentImageIdIndex === "function") {
                    currentIdx = viewport.getCurrentImageIdIndex();
                  }
                }
                const isCurrent = idx === currentIdx;
                return (
                  <div
                    key={id}
                    style={{
                      width: "100%",
                      height: `${100 / viewportImageIds[i].length}%`,
                      background: isCurrent
                        ? "linear-gradient(to right, #ffeb3b 60%, #ff9800 100%)"
                        : loaded
                          ? "linear-gradient(to right, #4caf50 60%, #2196f3 100%)"
                          : "#444",
                      opacity: loaded ? 0.95 : 0.4,
                      transition: "background 0.3s, opacity 0.3s",
                      borderBottom:
                        idx < viewportImageIds[i].length - 1
                          ? "1px solid #222"
                          : "none",
                      pointerEvents: "auto",
                      cursor: "pointer",
                    }}
                    title={
                      isCurrent
                        ? `Current image #${idx + 1}`
                        : loaded
                          ? `Go to image #${idx + 1}`
                          : `Loading...`
                    }
                    onClick={() => {
                      const renderingEngine = renderingEngineRef.current;
                      const viewport = renderingEngine?.getViewport(`CT_${i}`);
                      if (
                        viewport &&
                        typeof viewport.setImageIdIndex === "function" &&
                        viewportModes[i] === "stack"
                      ) {

                        //viewport.setImageIdIndex(idx);
                        csUtilities.jumpToSlice(viewport.element, { imageIndex: idx })
                        //viewport.render();
                        updateOverlay(i, viewport);

                      }
                    }}
                  />
                );
              })}
            </div>
          </div>
        )}
        {isVisible && availableStacks.length > 1 && (
          <div
            style={{
              position: "absolute",
              left: 0,
              right: 0,
              bottom: 8,
              zIndex: 20,
              display: "flex",
              alignItems: "center",
              justifyContent: "center", // Center horizontally
              pointerEvents: "auto",
              width: "100%",
              // Remove background/gap if you want only the selector to be visible
              background: "none",
              borderRadius: "0",
              padding: 0,
            }}
          >
            {/* Custom Stack Dropdown */}
            <div style={{ position: "relative" }}>
              <button
                style={{
                  background: "#222",
                  color: "#fff",
                  border: "1px solid #444",
                  borderRadius: "4px",
                  padding: "2px 8px",
                  fontSize: "13px",
                  marginRight: "6px",
                  minWidth: 120,
                  textAlign: "left",
                  cursor: "pointer",
                }}
                onClick={() => setOpenDropdownIdx(openDropdownIdx === i ? null : i)}
                //onBlur={() => setTimeout(() => setOpenDropdownIdx(null), 150)}
                aria-haspopup="listbox"
                aria-expanded={openDropdownIdx === i}
              >
                {/* Show current stack with indicator */}
                {(() => {
                  const stackObj = availableStacks[selectedStackIdx[i] || 0];
                  const thisUID = stackObj?.studyInstanceUID;
                  const sameStudy = referenceUID && thisUID && referenceUID === thisUID;
                  const dotColor = sameStudy ? "#4caf50" : "#888";
                  return (
                    <span>
                      <span style={{
                        color: dotColor,
                        fontWeight: "bold",
                        marginRight: 6,
                        fontSize: "16px",
                        verticalAlign: "middle",
                      }}>●</span>
                      Stack {selectedStackIdx[i] + 1} ({stackObj?.imageIds.length || 0} images)
                    </span>
                  );
                })()}
                <span style={{ float: "right", fontSize: "12px" }}>▼</span>
              </button>
              {openDropdownIdx === i && (
                <div
                  ref={el => {
                    if (el) el.focus();
                  }}
                  tabIndex={0}
                  style={{
                    position: "absolute",
                    left: 0,
                    bottom: "110%",
                    background: "#222",
                    border: "1px solid #444",
                    borderRadius: "4px",
                    boxShadow: "0 2px 8px rgba(0,0,0,0.3)",
                    zIndex: 100,
                    minWidth: 180,
                    maxHeight: 220,
                    overflowY: "auto",
                    overscrollBehavior: "contain",
                    scrollbarWidth: "thin",
                  }}
                  role="listbox"
                  onMouseDown={e => e.stopPropagation()}
                  onWheel={e => {
                    // Trap scroll inside the dropdown
                    const target = e.currentTarget as HTMLDivElement;
                    const { scrollTop, scrollHeight, clientHeight } = target;
                    const atTop = scrollTop === 0;
                    const atBottom = scrollTop + clientHeight >= scrollHeight - 1;
                    if (
                      (e.deltaY < 0 && atTop) ||
                      (e.deltaY > 0 && atBottom)
                    ) {
                      // Let the event bubble to parent (viewport)
                      return;
                    }
                    // Otherwise, prevent scrolling the viewport
                    e.stopPropagation();
                    e.preventDefault();
                    // Manually scroll the menu
                    target.scrollTop += e.deltaY;
                  }}
                  onBlur={e => {
                    const related = e.relatedTarget as Node;
                    const parent = e.currentTarget.parentElement;
                    if (!parent?.contains(related)) {
                      setTimeout(() => setOpenDropdownIdx(null), 100);
                    }
                  }}
                >
                  {availableStacks.map((stackObj, idx) => {
                    const thisUID = stackObj.studyInstanceUID;
                    const sameStudy = referenceUID && thisUID && referenceUID === thisUID;
                    const dotColor = sameStudy ? "#4caf50" : "#888";
                    const isEmpty = stackObj.imageIds.length === 0;
                    return (
                      <div
                        key={idx}
                        role="option"
                        aria-selected={selectedStackIdx[i] === idx}
                        style={{
                          padding: "6px 12px",
                          background: selectedStackIdx[i] === idx ? "#444" : "#222",
                          color: isEmpty ? "#aaa" : "#fff",
                          cursor: isEmpty ? "not-allowed" : "pointer",
                          display: "flex",
                          alignItems: "center",
                          fontWeight: selectedStackIdx[i] === idx ? "bold" : "normal",
                          borderBottom: "1px solid #333",
                          userSelect: "none",
                          opacity: isEmpty ? 0.6 : 1,
                        }}
                        tabIndex={-1}
                        onMouseDown={e => {
                          e.preventDefault();
                          if (!isEmpty) {
                            handleLoadStack(i, idx);
                            setOpenDropdownIdx(null);
                          }
                        }}
                      >
                        <span style={{
                          color: dotColor,
                          fontWeight: "bold",
                          marginRight: 8,
                          fontSize: "16px",
                          verticalAlign: "middle",
                        }}>●</span>
                        Stack {idx + 1} ({stackObj.imageIds.length} images
                        {isEmpty && <span style={{ color: "#f44336", marginLeft: 6 }}>(empty)</span>})
                      </div>
                    );
                  })}
                </div>
              )}
            </div>
          </div>
        )}
      </div>
    );
  }




  return (
    // App root
    <div style={{
      position: "relative",
      inset: 0,
      width: "100%",
      height: "100%",
      boxSizing: "border-box",
      background: "#222",
      padding: "12px",
      overflow: "hidden",
    }}>
      <div>
        {/* Side menu toggle button */}
        <button
          style={{
            position: "absolute",
            top: "50%",
            transform: "translateY(-50%)",
            left: sideMenuOpen ? 250 : -20, // Slide right when open
            zIndex: 200,
            width: 40,
            height: 40,
            background: "#222",
            color: "#fff",
            border: "none",
            borderRadius: "50%",
            fontWeight: "bold",
            cursor: "pointer",
            fontSize: "22px",
            boxShadow: "0 2px 8px rgba(0,0,0,0.2)",
            transition: "left 0.25s cubic-bezier(.4,2,.6,1), background 0.2s",
          }}
          onClick={() => setSideMenuOpen(open => !open)}
          aria-label="Open menu"
        >
          ☰
        </button>

        {/* Side menu drawer */}
        {sideMenuOpen && (
          <div
            style={{
              position: "absolute",
              top: 0,
              left: 0,
              width: 220,
              height: "100%",
              background: "#222",
              color: "#fff",
              zIndex: 199,
              boxShadow: "2px 0 12px rgba(0,0,0,0.3)",
              flexDirection: "column",
              padding: "24px 16px 16px 16px",
              display: "flex",
              overflow: "hidden",
            }}
          >
            <div
              style={{
                flex: 1,
                minHeight: 0,
                overflowY: "auto", // <-- enables scrolling for menu content
                display: "flex",
                flexDirection: "column",
              }}
            >
              <div style={{ fontWeight: "bold", fontSize: 18, marginBottom: 24 }}>
                Menu
                <button
                  style={{
                    float: "right",
                    background: "transparent",
                    color: "#fff",
                    border: "none",
                    fontSize: "22px",
                    cursor: "pointer",
                  }}
                  onClick={() => setSideMenuOpen(false)}
                  aria-label="Close menu"
                >
                  ×
                </button>
              </div>
              <button
                style={{
                  padding: "10px 18px",
                  background: "#333",
                  color: "#fff",
                  border: "none",
                  borderRadius: "4px",
                  fontWeight: "bold",
                  cursor: "pointer",
                  fontSize: "15px",
                  marginBottom: "18px",
                  width: "100%",
                }}
                onClick={() => fileInputRef.current?.click()}
              >
                Load DICOM Folder
              </button>
              <input
                ref={fileInputRef}
                type="file"
                style={{ display: "none" }}
                multiple
                onChange={handleFilesSelected}
                accept=".dcm,application/dicom"
              />
              <div
                style={{
                  marginBottom: "18px",
                  display: "flex",
                  alignItems: "center",
                  gap: "8px",
                }}
              >
                <input
                  type="checkbox"
                  id="orderBySliceLocation"
                  checked={orderBySliceLocation}
                  onChange={e => setOrderBySliceLocation(e.target.checked)}
                  style={{ accentColor: "#666" }}
                />
                <label htmlFor="orderBySliceLocation" style={{ fontSize: "15px", cursor: "pointer" }}>
                  Order by Slice Location
                </label>
              </div>
              <button
                style={{
                  padding: "10px 18px",
                  background: "#333",
                  color: "#fff",
                  border: "none",
                  borderRadius: "4px",
                  fontWeight: "bold",
                  cursor: "pointer",
                  fontSize: "15px",
                  marginBottom: "18px",
                  width: "100%",
                }}
                onClick={handleShowMetadata}
              >
                Show DICOM Metadata
              </button>
              <button
                style={{
                  padding: "10px 18px",
                  background: crossHairsEnabled ? "#1976d2" : "#333",
                  color: "#fff",
                  border: "none",
                  borderRadius: "4px",
                  fontWeight: "bold",
                  cursor: "pointer",
                  fontSize: "15px",
                  marginBottom: "18px",
                  width: "100%",
                }}
                onClick={() => setCrossHairsEnabled((prev) => !prev)}
              >
                {crossHairsEnabled ? "Disable" : "Enable"} Cross Reference Lines
              </button>
              <button
                style={{
                  padding: "10px 18px",
                  background: referenceLinesEnabled ? "#1976d2" : "#333",
                  color: "#fff",
                  border: "none",
                  borderRadius: "4px",
                  fontWeight: "bold",
                  cursor: "pointer",
                  fontSize: "15px",
                  marginBottom: "18px",
                  width: "100%",
                }}
                onClick={() => setReferenceLinesEnabled((prev) => !prev)}
              >
                {referenceLinesEnabled ? "Disable" : "Enable"} Reference Lines
              </button>
              <button
                onClick={sortViewportImageIdsBySliceLocation}
                style={{ margin: 8, padding: 8 }}
              >
                Sort All Viewports by Slice Location
              </button>
              <button
                style={{
                  padding: "10px 18px",
                  background: altEnablesReferenceCursors ? "#1976d2" : "#333",
                  color: "#fff",
                  border: "none",
                  borderRadius: "4px",
                  fontWeight: "bold",
                  cursor: "pointer",
                  fontSize: "15px",
                  marginBottom: "18px",
                  width: "100%",
                }}
                onClick={() => setAltEnablesReferenceCursors((prev) => !prev)}
              >
                {altEnablesReferenceCursors
                  ? "Disable Alt Key for Reference Cursors"
                  : "Enable Alt Key for Reference Cursors"}
              </button>
              {/* Add more menu items here if needed */}

            </div>
          </div>
        )}
      </div>
      {/* Viewport grid menu at top center */}


      <div
        className="grid-menu-hover-container"
        style={{
          position: "absolute",
          top: 0,
          left: "50%",
          transform: "translateX(-50%)",
          zIndex: 100,
          display: "flex",
          flexDirection: "column",
          alignItems: "center",
          width: "auto",
          height: "48px",
          pointerEvents: "none", // Only the hit area and button will be interactive
        }}
      >
        {/* Transparent hit area to catch hover/focus */}
        <div
          style={{
            position: "absolute",
            top: 0,
            left: "50%",
            transform: "translateX(-50%)",
            width: 80,
            height: 14,
            zIndex: 1,
            pointerEvents: "auto",
            background: "transparent",
          }}
          onMouseEnter={() => setGridBtnActive(true)}
          onFocus={() => setGridBtnActive(true)}
          tabIndex={-1}
        />
        <button
          className="grid-menu-btn"
          style={{
            position: "relative",
            top: gridBtnActive ? "0" : "-22px",
            opacity: gridBtnActive ? 1 : 0.5,
            transition: "top 0.25s cubic-bezier(.4,2,.6,1), opacity 0.2s",
            padding: "6px 16px",
            background: "#222",
            color: "#fff",
            border: "none",
            borderRadius: "4px",
            fontWeight: "bold",
            cursor: "pointer",
            fontSize: "15px",
            marginRight: "8px",
            boxShadow: "0 2px 8px rgba(0,0,0,0.2)",
            pointerEvents: gridBtnActive ? "auto" : "none", // Only clickable when active
            zIndex: 2,
            display: gridBtnActive ? "block" : "none",
          }}
          onMouseLeave={() => setGridBtnActive(false)}
          onBlur={() => setGridBtnActive(false)}
          onClick={() => setViewportMenuOpen(open => !open)}
          tabIndex={0}
        >
          Grid: {viewportGrid.rows}x{viewportGrid.cols} ▼
        </button>
        {viewportMenuOpen && (
          <div
            style={{
              position: "absolute",
              top: "110%",
              left: "50%",
              transform: "translateX(-50%)",
              background: "#222",
              color: "#fff",
              borderRadius: "6px",
              boxShadow: "0 2px 12px rgba(0,0,0,0.4)",
              padding: "12px",
              display: "grid",
              gridTemplateColumns: "repeat(3, 40px)",
              gap: "8px",
              zIndex: 200,
              pointerEvents: "auto",
            }}
          >
            {gridOptions.map(opt => (
              <button
                key={`${opt.rows}x${opt.cols}`}
                style={{
                  width: "40px",
                  height: "40px",
                  background: (opt.rows === viewportGrid.rows && opt.cols === viewportGrid.cols) ? "#444" : "#333",
                  color: "#fff",
                  border: "none",
                  borderRadius: "4px",
                  fontWeight: "bold",
                  cursor: "pointer",
                  fontSize: "15px",
                }}
                onClick={() => {
                  saveVisibleViewportState();
                  setViewportGrid({ rows: opt.rows, cols: opt.cols })
                  setViewportMenuOpen(false)
                }}
              >
                {opt.rows}x{opt.cols}
              </button>
            ))}
          </div>
        )}
      </div>


      {/* Viewport grid (inside main div) */}
      <div
        style={{
          position: "absolute",
          top: 0,
          left: 0,
          width: "100%",
          height: "100%",
          display: "grid",
          margin: 3,
          gridTemplateRows: `repeat(${viewportGrid.rows}, 1fr)`,
          gridTemplateColumns: `repeat(${viewportGrid.cols}, 1fr)`,
          gap: "5px", // Increase gap for better visibility
          zIndex: 1,
        }}
      >
        {viewports}


      </div>


      {/* Settings button at top right */}
      <button
        style={{
          position: "absolute",
          top: 8,
          right: 8,
          zIndex: 20,
          padding: "6px 12px",
          background: "#222",
          color: "#fff",
          border: "none",
          borderRadius: "4px",
          cursor: "pointer",
          opacity: 0.85,
          userSelect: "none",
        }}
        onClick={() => setSettingsOpen(true)}
      >
        Settings
      </button>

      {/* Settings Modal (viewport-local, not fixed to page) */}
      {
        settingsOpen && (
          <div
            style={{
              position: "absolute",
              top: 0,
              left: 0,
              width: "100%",
              height: "100%",
              background: "rgba(0,0,0,0.4)",
              zIndex: 1000,
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
            }}
            onClick={() => setSettingsOpen(false)}
          >
            <div
              style={{
                background: "#222",
                color: "#fff",
                borderRadius: "8px",
                minWidth: "320px",
                minHeight: "180px",
                padding: "24px",
                boxShadow: "0 4px 24px rgba(0,0,0,0.4)",
                position: "relative",
              }}
              onClick={e => e.stopPropagation()}
            >
              <div style={{ fontSize: "20px", marginBottom: "16px" }}>
                Settings
              </div>
              {/* Settings content: Tool dropdowns */}
              <div style={{ marginBottom: "24px" }}>
                <div style={{ marginBottom: 12, fontWeight: "bold" }}>Mouse Button Tool Bindings</div>
                {MOUSE_BUTTONS.map((btn) => (
                  <div key={btn.value} style={{ marginBottom: 10 }}>
                    <label style={{ marginRight: 8 }}>{btn.label} Button:</label>
                    <select
                      value={mouseToolBindings[btn.value]}
                      onChange={e => setMouseToolBindings(prev => ({ ...prev, [btn.value]: e.target.value }))}
                      style={{
                        background: "#333",
                        color: "#fff",
                        border: "1px solid #444",
                        borderRadius: "4px",
                        padding: "4px 8px",
                        fontSize: "14px",
                      }}
                    >
                      {TOOL_OPTIONS.map(opt => (
                        <option key={opt.value} value={opt.value}>{opt.label}</option>
                      ))}
                    </select>
                  </div>
                ))}

                {/* Expandable advanced mouse bindings */}
                <div style={{ margin: "12px 0" }}>
                  <button
                    style={{
                      background: "#333",
                      color: "#fff",
                      border: "none",
                      borderRadius: "4px",
                      padding: "4px 12px",
                      cursor: "pointer",
                      fontSize: "14px",
                      marginBottom: "8px",
                    }}
                    onClick={() => setShowAdvancedMouseBindings(v => !v)}
                  >
                    {showAdvancedMouseBindings ? "Hide" : "Show"} Advanced Mouse Bindings
                  </button>
                  {showAdvancedMouseBindings && (
                    <div style={{ marginTop: 8 }}>
                      {ALL_MOUSE_BINDINGS.slice(3).map((btn) => (
                        <div key={btn.value} style={{ marginBottom: 10 }}>
                          <label style={{ marginRight: 8 }}>{btn.label}:</label>
                          <select
                            value={mouseToolBindings[btn.value] || ""}
                            onChange={e => setMouseToolBindings(prev => ({ ...prev, [btn.value]: e.target.value }))}
                            style={{
                              background: "#333",
                              color: "#fff",
                              border: "1px solid #444",
                              borderRadius: "4px",
                              padding: "4px 8px",
                              fontSize: "14px",
                            }}
                          >
                            <option value="">(None)</option>
                            {TOOL_OPTIONS.map(opt => (
                              <option key={opt.value} value={opt.value}>{opt.label}</option>
                            ))}
                          </select>
                        </div>
                      ))}
                    </div>
                  )}
                </div>
                {/* Ctrl + Mouse Button Tool Bindings */}
                <div style={{ margin: "18px 0 8px 0", fontWeight: "bold" }}>Ctrl + Mouse Button Tool Bindings</div>
                {MOUSE_BUTTONS.map((btn) => (
                  <div key={btn.value + "_ctrl"} style={{ marginBottom: 10 }}>
                    <label style={{ marginRight: 8 }}>{btn.label} + Ctrl:</label>
                    <select
                      value={ctrlMouseToolBindings[btn.value] || ""}
                      onChange={e => setCtrlMouseToolBindings(prev => ({ ...prev, [btn.value]: e.target.value }))}
                      style={{
                        background: "#333",
                        color: "#fff",
                        border: "1px solid #444",
                        borderRadius: "4px",
                        padding: "4px 8px",
                        fontSize: "14px",
                      }}
                    >
                      <option value="">(None)</option>
                      {TOOL_OPTIONS.map(opt => (
                        <option key={opt.value} value={opt.value}>{opt.label}</option>
                      ))}
                    </select>
                  </div>
                ))}

                {/* Separate button for advanced Ctrl+Mouse bindings */}
                <div style={{ margin: "12px 0" }}>
                  <button
                    style={{
                      background: "#333",
                      color: "#fff",
                      border: "none",
                      borderRadius: "4px",
                      padding: "4px 12px",
                      cursor: "pointer",
                      fontSize: "14px",
                      marginBottom: "8px",
                    }}
                    onClick={() => setShowAdvancedCtrlMouseBindings(v => !v)}
                  >
                    {showAdvancedCtrlMouseBindings ? "Hide" : "Show"} Advanced Ctrl + Mouse Bindings
                  </button>
                  {showAdvancedCtrlMouseBindings && (
                    <div style={{ marginTop: 8 }}>
                      {ALL_MOUSE_BINDINGS.slice(3).map((btn) => (
                        <div key={btn.value + "_ctrl"} style={{ marginBottom: 10 }}>
                          <label style={{ marginRight: 8 }}>{btn.label} + Ctrl:</label>
                          <select
                            value={ctrlMouseToolBindings[btn.value] || ""}
                            onChange={e => setCtrlMouseToolBindings(prev => ({ ...prev, [btn.value]: e.target.value }))}
                            style={{
                              background: "#333",
                              color: "#fff",
                              border: "1px solid #444",
                              borderRadius: "4px",
                              padding: "4px 8px",
                              fontSize: "14px",
                            }}
                          >
                            <option value="">(None)</option>
                            {TOOL_OPTIONS.map(opt => (
                              <option key={opt.value} value={opt.value}>{opt.label}</option>
                            ))}
                          </select>
                        </div>
                      ))}
                    </div>
                  )}
                </div>
              </div>
              <button
                style={{
                  position: "absolute",
                  top: 12,
                  right: 12,
                  background: "transparent",
                  color: "#fff",
                  border: "none",
                  fontSize: "20px",
                  cursor: "pointer",
                }}
                onClick={() => setSettingsOpen(false)}
                aria-label="Close"
              >
                ×
              </button>
            </div>
          </div>
        )
      }
      {
        metaModalOpen && (
          <div
            style={{
              position: "fixed",
              top: 0,
              left: 0,
              width: "100vw",
              height: "100vh",
              background: "rgba(0,0,0,0.5)",
              zIndex: 2000,
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
            }}
            onClick={() => setMetaModalOpen(false)}
          >
            <div
              style={{
                background: "#222",
                color: "#fff",
                borderRadius: "8px",
                minWidth: "320px",
                maxWidth: "80vw",
                maxHeight: "80vh",
                padding: "24px",
                boxShadow: "0 4px 24px rgba(0,0,0,0.4)",
                position: "relative",
                overflow: "auto",
              }}
              onClick={e => e.stopPropagation()}
            >
              <div style={{ fontSize: "20px", marginBottom: "16px" }}>
                DICOM Metadata
              </div>
              {metaModalContent}
              <button
                style={{
                  position: "absolute",
                  top: 12,
                  right: 12,
                  background: "transparent",
                  color: "#fff",
                  border: "none",
                  fontSize: "20px",
                  cursor: "pointer",
                }}
                onClick={() => setMetaModalOpen(false)}
                aria-label="Close"
              >
                ×
              </button>
            </div>
          </div>
        )
      }
      {/* ...rest of your UI... */}

    </div >
  )

}

export default App

function setupTools(MAIN_TOOL_GROUP_ID) {
  console.log('Setting up tools...', MAIN_TOOL_GROUP_ID);
  cornerstoneTools.addTool(PanTool)
  cornerstoneTools.addTool(WindowLevelTool)
  cornerstoneTools.addTool(StackScrollTool)
  cornerstoneTools.addTool(ZoomTool)
  cornerstoneTools.addTool(PlanarRotateTool)
  cornerstoneTools.addTool(LengthTool)
  cornerstoneTools.addTool(ProbeTool)
  cornerstoneTools.addTool(ArrowAnnotateTool)
  cornerstoneTools.addTool(RectangleROITool)
  cornerstoneTools.addTool(EllipticalROITool)
  cornerstoneTools.addTool(PlanarFreehandROITool)
  cornerstoneTools.addTool(PlanarFreehandContourSegmentationTool)
  cornerstoneTools.addTool(SculptorTool)
  cornerstoneTools.addTool(ReferenceCursors)
  cornerstoneTools.addTool(CrosshairsTool)
  cornerstoneTools.addTool(ReferenceLinesTool);
  cornerstoneTools.addTool(NoLabelArrowAnnotateTool);



  let mainToolGroup = ToolGroupManager.getToolGroup(MAIN_TOOL_GROUP_ID);
  if (!mainToolGroup) {
    mainToolGroup = ToolGroupManager.createToolGroup(MAIN_TOOL_GROUP_ID);
    // Add all tools you need for both stack and volume
    mainToolGroup.addTool(WindowLevelTool.toolName);
    mainToolGroup.addTool(PanTool.toolName);
    mainToolGroup.addTool(ZoomTool.toolName);
    mainToolGroup.addTool(StackScrollTool.toolName, { loop: false });
    mainToolGroup.addTool(PlanarRotateTool.toolName);
    mainToolGroup.addTool(LengthTool.toolName);
    mainToolGroup.addTool(NoLabelArrowAnnotateTool.toolName, { getTextCallback: () => '' });
    mainToolGroup.addTool(ArrowAnnotateTool.toolName);
    mainToolGroup.addTool(ProbeTool.toolName, { statsCalculator: () => null });
    mainToolGroup.addTool(RectangleROITool.toolName, { calculateStats: false, getTextCallback: () => '' });
    mainToolGroup.addTool(EllipticalROITool.toolName, { getTextCallback: () => '' });
    mainToolGroup.addTool(PlanarFreehandROITool.toolName, { calculateStats: false });
    mainToolGroup.addTool(PlanarFreehandContourSegmentationTool.toolName, {});
    mainToolGroup.addTool(SculptorTool.toolName, {});
    //mainToolGroup.addTool(CrosshairsTool.toolName);
    mainToolGroup.addTool(ReferenceLinesTool.toolName);
    mainToolGroup.addTool(ReferenceCursors.toolName);
  }

  return mainToolGroup;
}

// Add this helper to find the next/previous annotation index for the current viewport
function findNextAnnotationIdx(currentIdx: number, imageIds: string[], annotations: any[], direction: 1 | -1) {
  if (!annotations.length || !imageIds.length) return null;
  // Find all annotation indices in this stack
  const annIndices = annotations
    .map(ann => imageIds.findIndex(id => id === ann.metadata.referencedImageId))
    .filter(idx => idx !== -1)
    .sort((a, b) => a - b);

  if (!annIndices.length) return null;

  // Find the next/prev annotation index
  if (direction === 1) {
    // Next
    for (let idx of annIndices) {
      if (idx > currentIdx) return idx;
    }
    return annIndices[0]; // wrap around
  } else {
    // Previous
    for (let i = annIndices.length - 1; i >= 0; i--) {
      if (annIndices[i] < currentIdx) return annIndices[i];
    }
    return annIndices[annIndices.length - 1]; // wrap around
  }
}