<script lang="ts">
  import type { PageData } from './$types';
  import { tick, onMount } from 'svelte';

  // --- 상태 변수 ---
  export let data: PageData;
  let dataNumbers: DataNumberInfo[] = [];
  let rectangles: Rectangle[] = [];
  let selectedMovie: number | null = null;
  let selectedDataNumberInfo: DataNumberInfo | null = null;
  let isLoadingDataNumbers = false;
  let isLoadingImage = false;
  let imageHasError = false;

  // --- Undo/Redo 및 OF 결과 저장을 위한 상태 변수 ---
  let originalRectsFromDB: Rectangle[] = []; // DB에서 불러온 원본 사각형
  let stabilizedRectsResult: { // OF 계산 결과를 저장 (사각형 + flow lines)
    stabilizedRects: Rectangle[],
    flowLines: any[]
  } | null = null;

  // --- UI 제어 상태 변수 ---
  let isCvReady = false;
  let opticalFlowCanvas: HTMLCanvasElement;
  let showOpticalFlowVectors = true;
  let showPrevRects = true;
  let showCurrentRects = true;
  let prevRectsForDisplay: Rectangle[] = [];

  // --- 상호작용 상태 변수 ---
  let selectedRectIds: number[] = []; 
  let imageElement: HTMLImageElement;
  let svgElement: SVGSVGElement;
  let viewBox = '0 0 100 100';
  let activeInteraction: {
      type: 'move' | 'resize';
      handle?: string;
      startX: number;
      startY: number;
      initialRects?: Rectangle[]; 
  } | null = null;
  
  let dataNumberPanel: HTMLDivElement;
  let clipboard: Omit<Rectangle, 'id'>[] | null = null;

  // --- 반응형 로직 ---
  let selectedRectangles: Rectangle[] = [];
  $: selectedRectangles = rectangles.filter(r => selectedRectIds.includes(r.id));

  $: if (imageElement) {
    const updateViewBox = () => {
      if (imageElement.naturalWidth > 0 && imageElement.naturalHeight > 0) {
        viewBox = `0 0 ${imageElement.naturalWidth} ${imageElement.naturalHeight}`;
      }
    };
    if (imageElement.complete) {
      updateViewBox();
    } else {
      imageElement.onload = updateViewBox;
    }
  }

  $: if (selectedDataNumberInfo) {
    imageHasError = false;
    selectedRectIds = [];
    if (dataNumberPanel) {
        const selectedElement = dataNumberPanel.querySelector(`[data-index="${selectedDataNumberInfo.Index}"]`);
        if (selectedElement) {
            const containerRect = dataNumberPanel.getBoundingClientRect();
            const elementRect = selectedElement.getBoundingClientRect();
            if (elementRect.top < containerRect.top || elementRect.bottom > containerRect.bottom) {
                selectedElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
            }
        }
    }
  }

  // 반응형으로 캔버스 다시 그리기
  $: if (opticalFlowCanvas) {
    showOpticalFlowVectors, stabilizedRectsResult, redrawCanvas();
  }

  onMount(() => {
    const initialize = () => {
      if (cv && cv.Mat) { isCvReady = true; return; }
      if (cv) { cv.onRuntimeInitialized = () => { isCvReady = true; }; }
    };
    const intervalId = setInterval(() => {
      if (typeof cv !== 'undefined') { clearInterval(intervalId); initialize(); }
    }, 50);
  });

  // 계산만 담당하는 함수
  async function calculateOpticalFlow(prevImgSrc: string, currentImgSrc: string, prevRects: Rectangle[]): Promise<{ stabilizedRects: Rectangle[], flowLines: any[] }> {
    const emptyResult = { stabilizedRects: prevRects, flowLines: [] };
    if (!isCvReady) return emptyResult;

    const loadImage = (src: string): Promise<HTMLImageElement> => new Promise((resolve, reject) => {
        const img = new Image();
        img.crossOrigin = "Anonymous";
        img.onload = () => resolve(img);
        img.onerror = (err) => reject(new Error(`Failed to load image: ${src}, error: ${err}`));
        img.src = src;
    });

    try {
        const [prevImg, currentImg] = await Promise.all([loadImage(prevImgSrc), loadImage(currentImgSrc)]);
        const stabilizedRects: Rectangle[] = [];
        let allGoodFlowLines: any[] = [];
        let src1 = cv.imread(prevImg);
        let src2 = cv.imread(currentImg);
        let gray1 = new cv.Mat();
        let gray2 = new cv.Mat();
        cv.cvtColor(src1, gray1, cv.COLOR_RGBA2GRAY);
        cv.cvtColor(src2, gray2, cv.COLOR_RGBA2GRAY);

        for (const prevRect of prevRects) {
            let allFlowLines: any[] = [];
            let points1 = new cv.Mat();
            let points2 = new cv.Mat();
            let status = new cv.Mat();
            let err = new cv.Mat();
            const roiRect = new cv.Rect(prevRect.x, prevRect.y, prevRect.width, prevRect.height);
            let roiGray1 = gray1.roi(roiRect);
            cv.goodFeaturesToTrack(roiGray1, points1, 200, 0.01, 5);
            if (points1.rows === 0) {
                stabilizedRects.push({ ...prevRect });
                roiGray1.delete(); points1.delete(); points2.delete(); status.delete(); err.delete();
                continue;
            }
            for(let i = 0; i < points1.rows; i++) {
                points1.data32F[i * 2] += prevRect.x;
                points1.data32F[i * 2 + 1] += prevRect.y;
            }
            cv.calcOpticalFlowPyrLK(gray1, gray2, points1, points2, status, err);
            let initial_avg_dx = 0, initial_avg_dy = 0, tracked_count = 0;
            for (let i = 0; i < status.rows; ++i) {
                if (status.data[i] === 1) {
                    const p1 = new cv.Point(points1.data32F[i * 2], points1.data32F[i * 2 + 1]);
                    const p2 = new cv.Point(points2.data32F[i * 2], points2.data32F[i * 2 + 1]);
                    const dx = p2.x - p1.x;
                    const dy = p2.y - p1.y;
                    allFlowLines.push({ p1, p2, dx, dy, length: Math.sqrt(dx*dx + dy*dy) });
                    initial_avg_dx += dx;
                    initial_avg_dy += dy;
                    tracked_count++;
                }
            }
            if (tracked_count > 0) {
                initial_avg_dx /= tracked_count;
                initial_avg_dy /= tracked_count;
            }
            const maxLength = 40;
            const goodFlowLines = allFlowLines.filter(line => {
                if (line.length > maxLength) return false;
                const dotProduct = (line.dx * initial_avg_dx) + (line.dy * initial_avg_dy);
                if (dotProduct < 0) return false;
                return true;
            });
            allGoodFlowLines.push(...goodFlowLines);
            let final_avg_dx = 0, final_avg_dy = 0;
            if (goodFlowLines.length > 0) {
                goodFlowLines.forEach(line => { final_avg_dx += line.dx; final_avg_dy += line.dy; });
                final_avg_dx /= goodFlowLines.length;
                final_avg_dy /= goodFlowLines.length;
            }
            stabilizedRects.push({ ...prevRect, x: prevRect.x + final_avg_dx, y: prevRect.y + final_avg_dy });
            points1.delete(); points2.delete(); status.delete(); err.delete(); roiGray1.delete();
        }
        src1.delete(); src2.delete(); gray1.delete(); gray2.delete();
        return { stabilizedRects, flowLines: allGoodFlowLines };
    } catch (error) {
        console.error("Optical Flow 처리 중 에러:", error);
        return emptyResult;
    }
  }

  // 그리기를 전담하는 함수
  function redrawCanvas() {
    const ctx = opticalFlowCanvas.getContext('2d');
    if (!ctx || !imageElement) return;

    const canvasWidth = opticalFlowCanvas.clientWidth;
    const canvasHeight = opticalFlowCanvas.clientHeight;
    opticalFlowCanvas.width = canvasWidth;
    opticalFlowCanvas.height = canvasHeight;
    ctx.clearRect(0, 0, canvasWidth, canvasHeight);

    if (showOpticalFlowVectors && stabilizedRectsResult) {
        const { stabilizedRects, flowLines } = stabilizedRectsResult;
        const imgNaturalWidth = imageElement.naturalWidth;
        const imgNaturalHeight = imageElement.naturalHeight;
        const scale = Math.min(canvasWidth / imgNaturalWidth, canvasHeight / imgNaturalHeight);
        const renderedImgWidth = imgNaturalWidth * scale;
        const renderedImgHeight = imgNaturalHeight * scale;
        const offsetX = (canvasWidth - renderedImgWidth) / 2;
        const offsetY = (canvasHeight - renderedImgHeight) / 2;

        // 화살표 그리기
        ctx.lineWidth = 2;
        ctx.strokeStyle = 'rgba(255, 255, 0, 0.8)';
        ctx.fillStyle = 'rgba(255, 255, 0, 0.8)';
        const headlen = 5;
        flowLines.forEach(line => {
            const startX = (line.p1.x * scale) + offsetX;
            const startY = (line.p1.y * scale) + offsetY;
            const endX = (line.p2.x * scale) + offsetX;
            const endY = (line.p2.y * scale) + offsetY;
            if (Math.abs(startX - endX) < 1 && Math.abs(startY - endY) < 1) return;
            const angle = Math.atan2(endY - startY, endX - startX);
            ctx.beginPath();
            ctx.moveTo(startX, startY);
            ctx.lineTo(endX, endY);
            ctx.stroke();
            ctx.beginPath();
            ctx.moveTo(endX, endY);
            ctx.lineTo(endX - headlen * Math.cos(angle - Math.PI / 6), endY - headlen * Math.sin(angle - Math.PI / 6));
            ctx.lineTo(endX - headlen * Math.cos(angle + Math.PI / 6), endY - headlen * Math.sin(angle + Math.PI / 6));
            ctx.lineTo(endX, endY);
            ctx.fill();
        });

        // 예측 사각형 그리기
        stabilizedRects.forEach(rect => {
            const rectX = (rect.x * scale) + offsetX;
            const rectY = (rect.y * scale) + offsetY;
            const rectWidth = rect.width * scale;
            const rectHeight = rect.height * scale;
            ctx.strokeStyle = 'rgba(0, 255, 0, 0.9)';
            ctx.lineWidth = 2;
            ctx.strokeRect(rectX, rectY, rectWidth, rectHeight);
        });
    }
  }

  // --- 데이터 로딩 함수 ---
  async function selectMovie(movieIndex: number) {
    if (selectedMovie === movieIndex) return;
    selectedMovie = movieIndex;
    selectedDataNumberInfo = null;
    rectangles = [];
    dataNumbers = [];
    isLoadingDataNumbers = true;
    try {
      const response = await fetch(`/api/datanumbers/${movieIndex}`);
      if (!response.ok) throw new Error('Failed to fetch data numbers');
      dataNumbers = await response.json();
    } catch (error) {
      console.error(error);
      alert((error as Error).message);
    } finally {
      isLoadingDataNumbers = false;
    }
   }

  async function selectDataNumber(dataInfo: DataNumberInfo) {
    if (selectedDataNumberInfo?.Index === dataInfo.Index || isLoadingImage) return;
    const prevRects = JSON.parse(JSON.stringify(rectangles));
    const prevImgSrc = selectedDataNumberInfo?.LocationFile ? `/api/images/${selectedDataNumberInfo.LocationFile}` : null;
    prevRectsForDisplay = prevRects;
    isLoadingImage = true;
    selectedRectIds = [];
    stabilizedRectsResult = null;
    try {
        const newImgSrc = `/api/images/${dataInfo.LocationFile}`;
        const preloadImagePromise = new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => resolve(true);
            img.onerror = () => reject(new Error('Image preloading failed'));
            img.src = newImgSrc;
        });
        const fetchRectsPromise = fetch(`/api/rectangles/by-ref/${dataInfo.Index}`);
        const [_, response] = await Promise.all([preloadImagePromise, fetchRectsPromise]);
        if (!response.ok) throw new Error('Failed to fetch rectangles');
        const newRectsFromAPI = await response.json();
        
        originalRectsFromDB = newRectsFromAPI;
        selectedDataNumberInfo = dataInfo;
        await tick();
        
        const conditions = {
            "1. 이전 이미지 경로 존재 여부": !!prevImgSrc,
            "2. 이전 사각형 존재 여부": prevRects.length > 0,
            "3. OpenCV 준비 여부": isCvReady
        };
        if (conditions["1. 이전 이미지 경로 존재 여부"] && conditions["2. 이전 사각형 존재 여부"] && conditions["3. OpenCV 준비 여부"]) {
            stabilizedRectsResult = await calculateOpticalFlow(prevImgSrc!, newImgSrc, prevRects);
        }
        rectangles = newRectsFromAPI;
        const newButton = dataNumberPanel?.querySelector(`li[data-index="${dataInfo.Index}"] button`) as HTMLElement;
        newButton?.focus();
    } catch (error) {
        console.error(error);
        alert((error as Error).message);
    } finally {
        isLoadingImage = false;
    }
  }

  // --- Apply/Undo 함수 ---
  function applyOpticalFlowResult() {
    // OF 결과나 DB 원본 데이터가 없으면 실행하지 않음
    if (!stabilizedRectsResult || !originalRectsFromDB) return;

    const stabilizedRects = stabilizedRectsResult.stabilizedRects;

    // 이전 프레임과 현재 프레임의 사각형 개수가 다르면 로직을 중단 (예외 처리)
    if (stabilizedRects.length !== originalRectsFromDB.length) {
        console.error("사각형 개수가 일치하지 않아 Apply Flow를 실행할 수 없습니다.");
        alert("The number of rectangles does not match between frames. Cannot apply flow.");
        return;
    }

    // --- 여기가 최종 수정된 부분입니다 ---
    // 현재 프레임의 사각형 ID는 유지하면서,
    // x, y, width, height는 모두 OF 예측 결과로 업데이트합니다.
    const updatedRects = originalRectsFromDB.map((originalRect, index) => {
        const stabilizedRect = stabilizedRects[index];
        return {
            id: originalRect.id,          // id는 현재 프레임의 원본 값을 사용
            x: stabilizedRect.x,          // x 좌표를 OF 결과로 업데이트
            y: stabilizedRect.y,          // y 좌표를 OF 결과로 업데이트
            width: stabilizedRect.width,  // width를 OF 결과로 업데이트
            height: stabilizedRect.height, // height를 OF 결과로 업데이트
        };
    });

    // 1. 화면의 사각형을 최종적으로 업데이트된 정보로 교체
    rectangles = updatedRects;

    // 2. 변경된 사각형 정보를 데이터베이스에 저장
    saveRectangles(rectangles);
  }
  
  function undoOpticalFlowResult() {
    // 1. 현재 사각형을 메모리에 저장해둔 DB 원본 값으로 즉시 되돌림
    rectangles = JSON.parse(JSON.stringify(originalRectsFromDB));

    // 2. 되돌린 사각형 정보를 데이터베이스에 저장
    saveRectangles(rectangles);
  }

  // --- 사각형 CRUD 함수 ---
  async function addNewRectangle() {
    if (!selectedDataNumberInfo) return;
    const defaultRect = { x: 10, y: 10, width: 100, height: 100 };
    try {
        const url = `/api/rectangles/by-ref/${selectedDataNumberInfo.Index}`;
        const response = await fetch(url, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(defaultRect),
        });
        if (!response.ok) {
            const errorBody = await response.text();
            throw new Error(`Failed to create rectangle on server: ${errorBody}`);
        }
        const newRectFromServer = await response.json();
        const finalNewRect = { ...defaultRect, id: newRectFromServer.id };

        // --- 여기가 수정된 부분입니다 ---
        // 두 배열 모두에 새 사각형을 추가하여 상태를 동기화합니다.
        rectangles = [...rectangles, finalNewRect];
        originalRectsFromDB = [...originalRectsFromDB, finalNewRect];

        selectedRectIds = [finalNewRect.id];
    } catch (error) {
        console.error(error);
        alert((error as Error).message);
    }
  }

  async function saveRectangles(rects: Rectangle[]) {
    if (!rects || rects.length === 0) return;
    const url = `/api/rectangles/bulk-update`; 
    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(rects),
      });
      if (!response.ok) {
        throw new Error('Failed to save rectangles');
      }
      console.log(`${rects.length}개의 사각형이 성공적으로 저장되었습니다.`);
    } catch (error) {
      console.error(error);
      alert((error as Error).message);
    }
  }

  async function deleteSelectedRect() {
    if (selectedRectIds.length === 0) return;
    const idsToDelete = [...selectedRectIds];
    rectangles = rectangles.filter(r => !idsToDelete.includes(r.id));
    selectedRectIds = [];
    try {
      await Promise.all(
        idsToDelete.map(id =>
          fetch(`/api/rectangles/by-id/${id}`, { method: 'DELETE' }).then(res => {
            if (!res.ok) throw new Error(`Failed to delete rectangle ${id}`);
          })
        )
      );
    } catch (error) {
      console.error(error);
    }
  }

  function copySelectedRect() {
    if (selectedRectangles.length === 0) {
      clipboard = null;
      return;
    }
    clipboard = selectedRectangles.map(({ id, ...rest }) => rest);
  }

  async function pasteRect() {
    if (!clipboard || clipboard.length === 0 || !selectedDataNumberInfo) return;
    const newRectsData = clipboard.map(rect => ({
      x: rect.x + 10,
      y: rect.y + 10,
      width: rect.width,
      height: rect.height,
    }));
    try {
      const url = `/api/rectangles/by-ref/${selectedDataNumberInfo.Index}`;
      const createdRectsFromServer = await Promise.all(
        newRectsData.map(rectData =>
          fetch(url, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(rectData),
          }).then(async response => {
            if (!response.ok) {
              throw new Error(`Failed to create rectangle on server`);
            }
            return response.json();
          })
        )
      );
      const finalNewRects = newRectsData.map((rectData, index) => ({
        ...rectData,
        id: createdRectsFromServer[index].id,
      }));
      rectangles = [...rectangles, ...finalNewRects];
      selectedRectIds = finalNewRects.map(r => r.id);
    } catch (error) {
      console.error(error);
      alert((error as Error).message);
    }
  }

  // --- 이벤트 핸들러 ---
  function getMousePosition(event: MouseEvent) {
    if (!imageElement || !svgElement) return { x: 0, y: 0 };
    const svgRect = svgElement.getBoundingClientRect();
    const { naturalWidth, naturalHeight, clientWidth, clientHeight } = imageElement;
    const scale = Math.min(clientWidth / naturalWidth, clientHeight / naturalHeight);
    const renderedWidth = naturalWidth * scale;
    const renderedHeight = naturalHeight * scale;
    const offsetX = (clientWidth - renderedWidth) / 2;
    const offsetY = (clientHeight - renderedHeight) / 2;
    const mouseX = (event.clientX - svgRect.left - offsetX) / scale;
    const mouseY = (event.clientY - svgRect.top - offsetY) / scale;
    return { x: mouseX, y: mouseY };
  }

  function handleInteractionStart(event: MouseEvent, rect: Rectangle, type: 'move' | 'resize', handle: string) {
    event.stopPropagation();
    const alreadySelected = selectedRectIds.includes(rect.id);
    if (event.shiftKey) {
        if (alreadySelected) {
            selectedRectIds = selectedRectIds.filter(id => id !== rect.id);
        } else {
            selectedRectIds = [...selectedRectIds, rect.id];
        }
    } else if (!alreadySelected) {
        selectedRectIds = [rect.id];
    }
    if (type === 'resize' && selectedRectIds.length > 1) {
        return;
    }
    const pos = getMousePosition(event);
    activeInteraction = {
      type,
      handle,
      startX: pos.x,
      startY: pos.y,
      initialRects: rectangles
        .filter(r => selectedRectIds.includes(r.id))
        .map(r => ({ ...r })),
    };
    window.addEventListener('mousemove', handleMouseMove);
    window.addEventListener('mouseup', handleMouseUp);
  }

  function handleMouseMove(event: MouseEvent) {
    if (!activeInteraction || !activeInteraction.initialRects || activeInteraction.initialRects.length === 0) return;
    const pos = getMousePosition(event);
    const dx = pos.x - activeInteraction.startX;
    const dy = pos.y - activeInteraction.startY;
    if (activeInteraction.type === 'resize') {
      const initialRect = activeInteraction.initialRects[0];
      const i = rectangles.findIndex(r => r.id === initialRect.id);
      if (i === -1) return;
      const { handle } = activeInteraction;
      let { x, y, width, height } = initialRect;
      if (handle!.includes('e')) width += dx;
      if (handle!.includes('w')) { width -= dx; x += dx; }
      if (handle!.includes('s')) height += dy;
      if (handle!.includes('n')) { height -= dy; y += dy; }
      if (width < 10) width = 10;
      if (height < 10) height = 10;
      rectangles[i] = { ...rectangles[i], x, y, width, height };
    } else if (activeInteraction.type === 'move') {
      activeInteraction.initialRects.forEach(initialRect => {
        const i = rectangles.findIndex(r => r.id === initialRect.id);
        if (i !== -1) {
            rectangles[i] = {
                ...rectangles[i],
                x: initialRect.x + dx,
                y: initialRect.y + dy,
            };
        }
      });
    }
  }

  function handleMouseUp() {
    if (activeInteraction) {
      saveRectangles(rectangles);
    }
    activeInteraction = null;
    window.removeEventListener('mousemove', handleMouseMove);
    window.removeEventListener('mouseup', handleMouseUp);
  }

  function deselectRect(event: MouseEvent) {
    if (event.target === svgElement) {
        selectedRectIds = [];
    }
  }

  function handleKeyDown(event: KeyboardEvent) {
    if (event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement) {
        return;
    }
    if (event.ctrlKey || event.metaKey) {
        if (event.key.toLowerCase() === 'c') {
            event.preventDefault();
            copySelectedRect();
            return;
        }
        if (event.key.toLowerCase() === 'v') {
            event.preventDefault();
            pasteRect();
            return;
        }
    }
    if (!selectedDataNumberInfo || dataNumbers.length <= 1) return;
    const currentIndex = dataNumbers.findIndex(d => d.Index === selectedDataNumberInfo!.Index);
    if (currentIndex === -1) return;
    let nextIndex = -1;
    switch (event.key) {
        case 'ArrowDown':
        case 'ArrowRight':
        case 'PageDown':
            event.preventDefault();
            if (currentIndex < dataNumbers.length - 1) nextIndex = currentIndex + 1;
            break;
        case 'ArrowUp':
        case 'ArrowLeft':
        case 'PageUp':
            event.preventDefault();
            if (currentIndex > 0) nextIndex = currentIndex - 1;
            break;
    }
    if (nextIndex !== -1) selectDataNumber(dataNumbers[nextIndex]);
  }

  const handles = ['n', 's', 'e', 'w', 'nw', 'ne', 'sw', 'se'];
</script>

<svelte:window on:keydown={handleKeyDown} />

<div class="flex w-full h-full">
  <nav class="w-[15%] min-w-[200px] bg-white p-4 border-r border-gray-200 overflow-y-auto">
    <div class="nav-section border border-gray-200 rounded-lg p-3">
      <h2 class="text-sm font-bold text-gray-600 uppercase tracking-wider mb-2 px-1">Movie Index</h2>
      <ul class="space-y-1">
        {#if data.movies && data.movies.length > 0}
          {#each data.movies as movie}
            <li>
              <button
                on:click={() => selectMovie(movie)}
                class="w-full text-left px-3 py-2 rounded-md text-gray-700 transition-colors duration-150"
                class:bg-blue-500={selectedMovie === movie}
                class:text-white={selectedMovie === movie}
                class:hover:bg-gray-100={selectedMovie !== movie}
              >
                {movie}
              </button>
            </li>
          {/each}
        {:else if data.error}
          <p class="text-sm text-red-500 px-3">{data.error}</p>
        {:else}
          <p class="text-sm text-gray-500 px-3">No movies found.</p>
        {/if}
      </ul>
    </div>
  </nav>

  <div bind:this={dataNumberPanel} class="w-[15%] min-w-[200px] bg-gray-50 p-4 border-r border-gray-200 overflow-y-auto">
    {#if selectedMovie}
      <div class="nav-section border border-gray-200 rounded-lg p-3 bg-white">
        <h2 class="text-sm font-bold text-gray-600 uppercase tracking-wider mb-2 px-1">Data Number</h2>
        {#if isLoadingDataNumbers}
          <p class="text-sm text-gray-500 px-3">Loading...</p>
        {:else if dataNumbers.length > 0}
          <ul class="space-y-1">
            {#each dataNumbers as dataInfo (dataInfo.Index)}
              <li data-index={dataInfo.Index}>
                <button
                  on:click={() => selectDataNumber(dataInfo)}
                  class="w-full text-left px-3 py-2 rounded-md text-gray-700 transition-colors duration-150"
                  class:bg-green-500={selectedDataNumberInfo?.Index === dataInfo.Index}
                  class:text-white={selectedDataNumberInfo?.Index === dataInfo.Index}
                  class:hover:bg-gray-100={selectedDataNumberInfo?.Index !== dataInfo.Index}
                >
                  {dataInfo.DataNumber}
                </button>
              </li>
            {/each}
          </ul>
        {:else}
          <p class="text-sm text-gray-500 px-3">No data found.</p>
        {/if}
      </div>
    {:else}
      <div class="text-center text-gray-400 text-sm mt-4">
        <p>Select a Movie Index to see data numbers.</p>
      </div>
    {/if}
  </div>

  <div class="w-[70%] flex flex-col bg-gray-200">
    <div class="flex-shrink-0 p-2 bg-white border-b border-gray-300 shadow-sm flex items-center justify-between">
        
        <div class="flex items-center gap-2">
            <button on:click={addNewRectangle} disabled={!selectedDataNumberInfo}
              class="px-3 py-1.5 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700 disabled:bg-gray-400">
              Add Rectangle
            </button>
            <button on:click={copySelectedRect} disabled={selectedRectangles.length === 0}
              class="px-3 py-1.5 text-sm font-medium text-white bg-indigo-600 rounded-md hover:bg-indigo-700 disabled:bg-gray-400">
              Copy
            </button>
            <button on:click={pasteRect} disabled={!clipboard || clipboard.length === 0}
              class="px-3 py-1.5 text-sm font-medium text-white bg-purple-600 rounded-md hover:bg-purple-700 disabled:bg-gray-400">
              Paste
            </button>
            <button on:click={deleteSelectedRect} disabled={selectedRectangles.length === 0}
              class="px-3 py-1.5 text-sm font-medium text-white bg-red-600 rounded-md hover:bg-red-700 disabled:bg-gray-400">
              Delete Selected
            </button>
            <button on:click={() => saveRectangles(rectangles)} disabled={rectangles.length === 0}
              class="px-3 py-1.5 text-sm font-medium text-white bg-green-600 rounded-md hover:bg-green-700 disabled:bg-gray-400">
              Save All
            </button>

            <div class="flex items-center gap-2 border-l-2 border-gray-200 ml-2 pl-2">
              <button on:click={applyOpticalFlowResult} disabled={!stabilizedRectsResult}
                class="px-3 py-1.5 text-sm font-medium text-white bg-green-600 rounded-md hover:bg-green-700 disabled:bg-gray-400">
                Apply Flow
              </button>
              <button on:click={undoOpticalFlowResult} disabled={rectangles === originalRectsFromDB}
                class="px-3 py-1.5 text-sm font-medium text-white bg-gray-500 rounded-md hover:bg-gray-600 disabled:bg-gray-400">
                Undo
              </button>
            </div>
        </div>

        <div class="flex items-center gap-4 text-sm text-gray-600 pr-2">
          <label class="flex items-center gap-1.5 cursor-pointer">
            <input type="checkbox" bind:checked={showOpticalFlowVectors} class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" />
            <span>Flow</span>
          </label>
          <label class="flex items-center gap-1.5 cursor-pointer">
            <input type="checkbox" bind:checked={showPrevRects} class="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" />
            <span>Prev</span>
          </label>
          <label class="flex items-center gap-1.5 cursor-pointer">
            <input type="checkbox" bind:checked={showCurrentRects} class="h-4 w-4 rounded border-gray-300 text-yellow-500 focus:ring-yellow-400" />
            <span>Current</span>
          </label>
        </div>

    </div>

    <div class="flex-grow p-6 overflow-auto">
      {#if !selectedDataNumberInfo}
        <div class="h-full flex justify-center items-center text-gray-500"><p>Select a data number to view the image.</p></div>
      {:else}
        <div class="relative w-full h-full shadow-lg bg-white image-viewer-container">
          {#if !imageHasError}
              <img 
                bind:this={imageElement} 
                src={`/api/images/${selectedDataNumberInfo.LocationFile}`} 
                alt={`Data ${selectedDataNumberInfo.DataNumber}`} 
                class="block w-full h-full object-contain" 
                on:error={() => imageHasError = true} 
              />
          {/if}
          
          <svg 
            bind:this={svgElement} 
            viewBox={viewBox}  
            class="absolute top-0 left-0 w-full h-full" 
            on:mousedown|self={deselectRect} 
            role="region" 
            aria-label="Image annotation area">
            {#if !imageHasError}
              {#if showPrevRects}
                {#each prevRectsForDisplay as rect (rect.id)}
                  <rect
                    x={rect.x} y={rect.y} width={rect.width} height={rect.height}
                    class="fill-transparent stroke-blue-500 stroke-2"
                    style="stroke-dasharray: 6 3;"
                  />
                {/each}
              {/if}

              {#if showCurrentRects}
                {#each rectangles as rect (rect.id)}
                  {@const isSelected = selectedRectIds.includes(rect.id)}
                  <rect
                    x={rect.x} y={rect.y} width={rect.width} height={rect.height}
                    role="button"
                    tabindex="0"
                    aria-label={`Annotation ${rect.id}`}
                    class="fill-yellow-400/20 stroke-yellow-400 stroke-2"
                    class:selected={isSelected} class:cursor-move={isSelected}
                    on:mousedown|stopPropagation={(e) => handleInteractionStart(e, rect, 'move', 'body')}
                  />
                  {#if selectedRectangles.length === 1 && selectedRectangles[0].id === rect.id}
                    {#each handles as handle}
                      {@const handleX = handle.includes('w') ? rect.x : (handle.includes('e') ? rect.x + rect.width : rect.x + rect.width / 2)}
                      {@const handleY = handle.includes('n') ? rect.y : (handle.includes('s') ? rect.y + rect.height : rect.y + rect.height / 2)}
                      <rect
                        x={handleX - 4} y={handleY - 4} width="8" height="8"
                        role="button"
                        tabindex="0"
                        aria-label={`Resize handle ${handle}`}
                        class="fill-white stroke-blue-600 stroke-2 cursor-{handle}-resize"
                        on:mousedown|stopPropagation={(e) => handleInteractionStart(e, rect, 'resize', handle)}
                      />
                    {/each}
                  {/if}
                {/each}
              {/if}
            {/if}
          </svg>

          <canvas bind:this={opticalFlowCanvas} class="absolute top-0 left-0 w-full h-full pointer-events-none"></canvas>
        </div>
      {/if}
    </div>
  </div>
</div>

<style>
  .image-viewer-container {
      user-select: none;
      -webkit-user-select: none;
      -moz-user-select: none;
      -ms-user-select: none;
    }
    rect.selected {
        stroke: #0ea5e9;
        stroke-width: 3px;
        stroke-dasharray: 6 3;
    }
    .cursor-n-resize { cursor: n-resize; }
    .cursor-s-resize { cursor: s-resize; }
    .cursor-e-resize { cursor: e-resize; }
    .cursor-w-resize { cursor: w-resize; }
    .cursor-nw-resize { cursor: nw-resize; }
    .cursor-ne-resize { cursor: ne-resize; }
    .cursor-sw-resize { cursor: sw-resize; }
    .cursor-se-resize { cursor: se-resize; }
</style>