<template>
  <div class="p-2 bg-cyan-600 rounded w-full my-4">
    <p>Developer's tip: Right click a sprite to open the context menu and add, replace or remove sprites.</p>
  </div>
  <div class="space-y-4">
    <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 sm:gap-0">
      <div class="flex flex-col sm:flex-row items-start sm:items-center gap-3 sm:gap-4 w-full sm:w-auto">
        <div class="flex items-center">
          <input id="pixel-perfect" type="checkbox" v-model="settingsStore.pixelPerfect" class="mr-2 w-4 h-4" @change="drawCanvas" />
          <label for="pixel-perfect" class="dark:text-gray-200 text-sm sm:text-base">Pixel perfect rendering</label>
        </div>
        <div class="flex items-center">
          <input id="allow-cell-swap" type="checkbox" v-model="allowCellSwap" class="mr-2 w-4 h-4" />
          <label for="allow-cell-swap" class="dark:text-gray-200 text-sm sm:text-base">Allow moving between cells</label>
        </div>
        <!-- Add new checkbox for showing all sprites -->
        <div class="flex items-center">
          <input id="show-all-sprites" type="checkbox" v-model="showAllSprites" class="mr-2" />
          <label for="show-all-sprites" class="dark:text-gray-200">Compare sprites</label>
        </div>
      </div>
    </div>

    <div class="relative border border-gray-300 dark:border-gray-600 rounded-lg overflow-auto">
      <!-- Zoom controls - visible on all screen sizes and positioned outside cells -->
      <div class="relative flex space-x-2 bg-white/90 dark:bg-gray-800/90 p-2 rounded-lg shadow-md z-20">
        <button @click="zoomIn" class="p-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-100 rounded-lg transition-colors">
          <i class="fas fa-plus"></i>
        </button>
        <button @click="zoomOut" class="p-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-100 rounded-lg transition-colors">
          <i class="fas fa-minus"></i>
        </button>
        <button @click="resetZoom" class="p-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-100 rounded-lg transition-colors">
          <i class="fas fa-expand"></i>
        </button>
      </div>

      <div class="canvas-container touch-manipulation relative" :style="{ transform: `scale(${zoom})`, transformOrigin: 'top left' }">
        <canvas
          ref="canvasRef"
          @mousedown="startDrag"
          @mousemove="drag"
          @mouseup="stopDrag"
          @mouseleave="stopDrag"
          @touchstart="handleTouchStart"
          @touchmove="handleTouchMove"
          @touchend="stopDrag"
          @contextmenu.prevent
          @dragover="handleDragOver"
          @dragenter="handleDragEnter"
          @dragleave="handleDragLeave"
          @drop="handleDrop"
          class="w-full transition-all"
          :class="{ 'ring-4 ring-blue-500 ring-opacity-50': isDragOver }"
          :style="settingsStore.pixelPerfect ? { 'image-rendering': 'pixelated' } : {}"
        ></canvas>

        <!-- Offset labels in corners -->
        <div v-if="canvasRef" class="absolute inset-0 pointer-events-none">
          <div
            v-for="position in spritePositions"
            :key="position.id"
            class="absolute text-[23px] leading-none font-mono text-cyan-600 dark:text-cyan-400 bg-white/90 dark:bg-gray-900/90 px-1 py-0.5 rounded-sm"
            :style="{
              left: `calc(${(position.cellX / canvasRef.width) * 100}% + ${(position.maxWidth / canvasRef.width) * 100}% - 2px)`,
              top: `calc(${(position.cellY / canvasRef.height) * 100}% + ${(position.maxHeight / canvasRef.height) * 100}% - 2px)`,
              transform: 'translate(-100%, -100%)',
            }"
          >
            <span>{{ position.x }},{{ position.y }}</span>
          </div>
        </div>
      </div>
    </div>

    <!-- Context Menu -->
    <div v-if="showContextMenu" class="fixed bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg z-50 py-1" :style="{ left: contextMenuX + 'px', top: contextMenuY + 'px' }">
      <button @click="addSprite" class="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-200 flex items-center gap-2">
        <i class="fas fa-plus"></i>
        Add sprite
      </button>
      <button v-if="contextMenuSpriteId" @click="replaceSprite" class="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-200 flex items-center gap-2">
        <i class="fas fa-exchange-alt"></i>
        Replace sprite
      </button>
      <button v-if="contextMenuSpriteId" @click="removeSprite" class="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-red-600 dark:text-red-400 flex items-center gap-2">
        <i class="fas fa-trash"></i>
        Remove sprite
      </button>
    </div>

    <!-- Hidden file input for replace functionality -->
    <input ref="fileInput" type="file" accept="image/*" class="hidden" @change="handleFileChange" />
  </div>
</template>

<script setup lang="ts">
  import { ref, onMounted, watch, computed, onUnmounted } from 'vue';
  import { useSettingsStore } from '@/stores/useSettingsStore';

  interface Sprite {
    id: string;
    img: HTMLImageElement;
    width: number;
    height: number;
    x: number;
    y: number;
  }

  interface CellPosition {
    col: number;
    row: number;
    index: number;
  }

  const props = defineProps<{
    sprites: Sprite[];
    columns: number;
  }>();

  const emit = defineEmits<{
    (e: 'updateSprite', id: string, x: number, y: number): void;
    (e: 'updateSpriteCell', id: string, newIndex: number): void;
    (e: 'removeSprite', id: string): void;
    (e: 'replaceSprite', id: string, file: File): void;
    (e: 'addSprite', file: File): void;
    (e: 'addSpriteWithResize', file: File): void;
  }>();

  // Get settings from store
  const settingsStore = useSettingsStore();

  const canvasRef = ref<HTMLCanvasElement | null>(null);
  const ctx = ref<CanvasRenderingContext2D | null>(null);

  // State for tracking drag operations
  const isDragging = ref(false);
  const activeSpriteId = ref<string | null>(null);
  const activeSpriteCellIndex = ref<number | null>(null);
  const dragStartX = ref(0);
  const dragStartY = ref(0);
  const dragOffsetX = ref(0);
  const dragOffsetY = ref(0);
  const allowCellSwap = ref(false);
  const currentHoverCell = ref<CellPosition | null>(null);

  // Visual feedback refs
  const ghostSprite = ref<{ id: string; x: number; y: number } | null>(null);
  const highlightCell = ref<CellPosition | null>(null);

  const showAllSprites = ref(false);
  const showContextMenu = ref(false);
  const contextMenuX = ref(0);
  const contextMenuY = ref(0);
  const contextMenuSpriteId = ref<string | null>(null);
  const replacingSpriteId = ref<string | null>(null);
  const fileInput = ref<HTMLInputElement | null>(null);
  const isDragOver = ref(false);

  const spritePositions = computed(() => {
    const { maxWidth, maxHeight } = calculateMaxDimensions();

    return props.sprites.map((sprite, index) => {
      const col = index % props.columns;
      const row = Math.floor(index / props.columns);

      return {
        id: sprite.id,
        canvasX: col * maxWidth + sprite.x,
        canvasY: row * maxHeight + sprite.y,
        cellX: col * maxWidth,
        cellY: row * maxHeight,
        width: sprite.width,
        height: sprite.height,
        maxWidth,
        maxHeight,
        col,
        row,
        index,
        x: sprite.x,
        y: sprite.y,
      };
    });
  });

  // Cache last known max dimensions to avoid collapsing cells while images are loading
  const lastMaxWidth = ref(1);
  const lastMaxHeight = ref(1);

  const calculateMaxDimensions = () => {
    let maxWidth = 0;
    let maxHeight = 0;

    props.sprites.forEach(sprite => {
      const img = sprite.img as HTMLImageElement | undefined;
      const w = Math.max(0, sprite.width || (img ? img.naturalWidth || img.width || 0 : 0));
      const h = Math.max(0, sprite.height || (img ? img.naturalHeight || img.height || 0 : 0));
      maxWidth = Math.max(maxWidth, w);
      maxHeight = Math.max(maxHeight, h);
    });

    // Keep dimensions at least as large as last known to prevent temporary collapse during loading
    maxWidth = Math.max(1, maxWidth, lastMaxWidth.value);
    maxHeight = Math.max(1, maxHeight, lastMaxHeight.value);

    lastMaxWidth.value = maxWidth;
    lastMaxHeight.value = maxHeight;

    return { maxWidth, maxHeight };
  };

  const startDrag = (event: MouseEvent) => {
    if (!canvasRef.value) return;

    // Hide context menu if open
    showContextMenu.value = false;

    // Handle right-click for context menu
    if ('button' in event && (event as MouseEvent).button === 2) {
      event.preventDefault();
      const rect = canvasRef.value.getBoundingClientRect();
      const scaleX = canvasRef.value.width / rect.width;
      const scaleY = canvasRef.value.height / rect.height;

      const mouseX = (event.clientX - rect.left) * scaleX;
      const mouseY = (event.clientY - rect.top) * scaleY;

      const clickedSprite = findSpriteAtPosition(mouseX, mouseY);
      contextMenuSpriteId.value = clickedSprite?.id || null;
      contextMenuX.value = event.clientX;
      contextMenuY.value = event.clientY;
      showContextMenu.value = true;
      return;
    }

    // Ignore non-left mouse buttons (but allow touch-generated events without a button prop)
    if ('button' in event && (event as MouseEvent).button !== 0) return;

    const rect = canvasRef.value.getBoundingClientRect();
    const scaleX = canvasRef.value.width / rect.width;
    const scaleY = canvasRef.value.height / rect.height;

    const mouseX = (event.clientX - rect.left) * scaleX;
    const mouseY = (event.clientY - rect.top) * scaleY;

    const clickedSprite = findSpriteAtPosition(mouseX, mouseY);
    if (clickedSprite) {
      isDragging.value = true;
      activeSpriteId.value = clickedSprite.id;
      dragStartX.value = mouseX;
      dragStartY.value = mouseY;

      // Find the sprite's position to calculate offset from mouse to sprite origin
      const spritePosition = spritePositions.value.find(pos => pos.id === clickedSprite.id);
      if (spritePosition) {
        dragOffsetX.value = mouseX - spritePosition.canvasX;
        dragOffsetY.value = mouseY - spritePosition.canvasY;
        activeSpriteCellIndex.value = spritePosition.index;

        // Store the starting cell position
        const startCell = findCellAtPosition(mouseX, mouseY);
        if (startCell) {
          currentHoverCell.value = startCell;
          highlightCell.value = null; // No highlight at the start
        }
      }
    }
  };

  const findCellAtPosition = (x: number, y: number): CellPosition | null => {
    const { maxWidth, maxHeight } = calculateMaxDimensions();
    const col = Math.floor(x / maxWidth);
    const row = Math.floor(y / maxHeight);

    // Check if the cell position is valid
    const totalRows = Math.ceil(props.sprites.length / props.columns);
    if (col >= 0 && col < props.columns && row >= 0 && row < totalRows) {
      const index = row * props.columns + col;
      if (index < props.sprites.length) {
        return { col, row, index };
      }
    }
    return null;
  };

  const drag = (event: MouseEvent) => {
    if (!isDragging.value || !activeSpriteId.value || !canvasRef.value || activeSpriteCellIndex.value === null) return;

    const rect = canvasRef.value.getBoundingClientRect();
    const scaleX = canvasRef.value.width / rect.width;
    const scaleY = canvasRef.value.height / rect.height;

    const mouseX = (event.clientX - rect.left) * scaleX;
    const mouseY = (event.clientY - rect.top) * scaleY;

    const spriteIndex = props.sprites.findIndex(s => s.id === activeSpriteId.value);
    if (spriteIndex === -1) return;

    // Find the cell the mouse is currently over
    const hoverCell = findCellAtPosition(mouseX, mouseY);
    currentHoverCell.value = hoverCell;

    if (allowCellSwap.value && hoverCell) {
      // If we're hovering over a different cell than the sprite's current cell
      if (hoverCell.index !== activeSpriteCellIndex.value) {
        // Show a highlight for the target cell
        highlightCell.value = hoverCell;

        // Create a ghost sprite that follows the mouse
        ghostSprite.value = {
          id: activeSpriteId.value,
          x: mouseX - dragOffsetX.value,
          y: mouseY - dragOffsetY.value,
        };

        drawCanvas();
      } else {
        // Same cell as the sprite's origin, just do regular movement
        highlightCell.value = null;
        ghostSprite.value = null;
        handleInCellMovement(mouseX, mouseY, spriteIndex);
      }
    } else {
      // Regular in-cell movement
      handleInCellMovement(mouseX, mouseY, spriteIndex);
    }
  };

  const handleInCellMovement = (mouseX: number, mouseY: number, spriteIndex: number) => {
    if (!activeSpriteId.value) return;

    const position = spritePositions.value.find(pos => pos.id === activeSpriteId.value);
    if (!position) return;

    // Calculate new position based on mouse position and initial click offset
    const newX = mouseX - position.cellX - dragOffsetX.value;
    const newY = mouseY - position.cellY - dragOffsetY.value;

    // Constrain within cell boundaries and ensure integer positions
    const constrainedX = Math.floor(Math.max(0, Math.min(position.maxWidth - props.sprites[spriteIndex].width, newX)));
    const constrainedY = Math.floor(Math.max(0, Math.min(position.maxHeight - props.sprites[spriteIndex].height, newY)));

    emit('updateSprite', activeSpriteId.value, constrainedX, constrainedY);
    drawCanvas();
  };

  const stopDrag = () => {
    if (isDragging.value && allowCellSwap.value && activeSpriteId.value && activeSpriteCellIndex.value !== null && currentHoverCell.value && activeSpriteCellIndex.value !== currentHoverCell.value.index) {
      // We've dragged from one cell to another
      // Tell parent component to update the sprite's cell index
      emit('updateSpriteCell', activeSpriteId.value, currentHoverCell.value.index);

      // Also reset the sprite's position within the cell to 0,0
      emit('updateSprite', activeSpriteId.value, 0, 0);
    }

    // Reset all drag state
    isDragging.value = false;
    activeSpriteId.value = null;
    activeSpriteCellIndex.value = null;
    currentHoverCell.value = null;
    highlightCell.value = null;
    ghostSprite.value = null;

    // Redraw without highlights
    drawCanvas();
  };

  // Add zoom functionality for mobile
  const zoom = ref(1);
  const minZoom = 0.5;
  const maxZoom = 3;
  const zoomStep = 0.25;

  const zoomIn = () => {
    zoom.value = Math.min(maxZoom, zoom.value + zoomStep);
  };

  const zoomOut = () => {
    zoom.value = Math.max(minZoom, zoom.value - zoomStep);
  };

  const resetZoom = () => {
    zoom.value = 1;
  };

  // Improved touch handling
  const handleTouchStart = (event: TouchEvent) => {
    // Don't prevent default to allow scrolling
    if (event.touches.length === 1) {
      if (!canvasRef.value) return;
      const touch = event.touches[0];
      const mouseEvent = {
        clientX: touch.clientX,
        clientY: touch.clientY,
        preventDefault: () => {},
      } as unknown as MouseEvent;
      startDrag(mouseEvent);
    }
  };

  const removeSprite = () => {
    if (contextMenuSpriteId.value) {
      emit('removeSprite', contextMenuSpriteId.value);
      showContextMenu.value = false;
      contextMenuSpriteId.value = null;
    }
  };

  const replaceSprite = () => {
    if (contextMenuSpriteId.value && fileInput.value) {
      // Store the sprite ID separately so it persists after context menu closes
      replacingSpriteId.value = contextMenuSpriteId.value;
      fileInput.value.click();
      // Hide context menu immediately since we've stored the ID
      showContextMenu.value = false;
      contextMenuSpriteId.value = null;
    }
  };

  const addSprite = () => {
    if (fileInput.value) {
      fileInput.value.click();
      // Hide context menu immediately
      showContextMenu.value = false;
      contextMenuSpriteId.value = null;
    }
  };

  const handleFileChange = (event: Event) => {
    const input = event.target as HTMLInputElement;

    if (input.files && input.files.length > 0) {
      const file = input.files[0];
      if (file.type.startsWith('image/')) {
        if (replacingSpriteId.value) {
          emit('replaceSprite', replacingSpriteId.value, file);
        } else {
          // Adding new sprite
          emit('addSprite', file);
        }
      } else {
        alert('Please select an image file.');
      }
    }
    // Clean up after file selection
    replacingSpriteId.value = null;
    input.value = '';
  };

  const hideContextMenu = () => {
    showContextMenu.value = false;
    contextMenuSpriteId.value = null;
  };

  // Drag and drop handlers
  const handleDragOver = (event: DragEvent) => {
    event.preventDefault();
    event.stopPropagation();
    if (event.dataTransfer) {
      event.dataTransfer.dropEffect = 'copy';
    }
    isDragOver.value = true;
  };

  const handleDragEnter = (event: DragEvent) => {
    event.preventDefault();
    event.stopPropagation();
    isDragOver.value = true;
  };

  const handleDragLeave = (event: DragEvent) => {
    event.preventDefault();
    event.stopPropagation();
    // Only set to false if we're leaving the canvas entirely
    const rect = canvasRef.value?.getBoundingClientRect();
    if (rect && (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom)) {
      isDragOver.value = false;
    }
  };

  const handleDrop = async (event: DragEvent) => {
    event.preventDefault();
    event.stopPropagation();
    isDragOver.value = false;

    if (!event.dataTransfer?.files || event.dataTransfer.files.length === 0) {
      return;
    }

    const files = Array.from(event.dataTransfer.files).filter(file => file.type.startsWith('image/'));

    if (files.length === 0) {
      alert('Please drop image files only.');
      return;
    }

    // Process each dropped file
    for (const file of files) {
      await processDroppedImage(file);
    }
  };

  const processDroppedImage = (file: File): Promise<void> => {
    return new Promise((resolve, reject) => {
      const img = new Image();
      const reader = new FileReader();

      reader.onload = e => {
        if (e.target?.result) {
          img.src = e.target.result as string;
        }
      };

      img.onload = () => {
        const { maxWidth, maxHeight } = calculateMaxDimensions();

        // Check if the dropped image is larger than current cells
        if (img.naturalWidth > maxWidth || img.naturalHeight > maxHeight) {
          // Emit event with resize flag
          emit('addSpriteWithResize', file);
        } else {
          // Normal add
          emit('addSprite', file);
        }
        resolve();
      };

      img.onerror = () => {
        console.error('Failed to load image:', file.name);
        reject(new Error('Failed to load image'));
      };

      reader.readAsDataURL(file);
    });
  };

  const handleTouchMove = (event: TouchEvent) => {
    // Only prevent default when we're actually dragging
    if (isDragging.value) {
      event.preventDefault();
    }

    if (event.touches.length === 1) {
      const touch = event.touches[0];
      const mouseEvent = {
        clientX: touch.clientX,
        clientY: touch.clientY,
        preventDefault: () => {},
      } as unknown as MouseEvent;
      drag(mouseEvent);
    }
  };

  const findSpriteAtPosition = (x: number, y: number) => {
    // Search in reverse order to get the topmost sprite first
    for (let i = spritePositions.value.length - 1; i >= 0; i--) {
      const pos = spritePositions.value[i];

      if (x >= pos.canvasX && x <= pos.canvasX + pos.width && y >= pos.canvasY && y <= pos.canvasY + pos.height) {
        return props.sprites.find(s => s.id === pos.id) || null;
      }
    }
    return null;
  };

  const drawCanvas = () => {
    if (!canvasRef.value || !ctx.value) return;

    const { maxWidth, maxHeight } = calculateMaxDimensions();

    // Set canvas size
    const rows = Math.max(1, Math.ceil(props.sprites.length / props.columns));
    canvasRef.value.width = maxWidth * props.columns;
    canvasRef.value.height = maxHeight * rows;

    // Clear canvas
    ctx.value.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height);

    // Disable image smoothing based on pixel perfect setting
    ctx.value.imageSmoothingEnabled = !settingsStore.pixelPerfect;

    // Draw background for each cell
    for (let col = 0; col < props.columns; col++) {
      for (let row = 0; row < rows; row++) {
        const cellX = Math.floor(col * maxWidth);
        const cellY = Math.floor(row * maxHeight);

        // Draw cell background
        ctx.value.fillStyle = settingsStore.darkMode ? '#1F2937' : '#f9fafb';
        ctx.value.fillRect(cellX, cellY, maxWidth, maxHeight);

        // Highlight the target cell if specified
        if (highlightCell.value && highlightCell.value.col === col && highlightCell.value.row === row) {
          ctx.value.fillStyle = 'rgba(59, 130, 246, 0.2)'; // Light blue highlight
          ctx.value.fillRect(cellX, cellY, maxWidth, maxHeight);
        }
      }
    }

    // If showing all sprites, draw all sprites with transparency in each cell
    if (showAllSprites.value) {
      for (let cellIndex = 0; cellIndex < props.sprites.length; cellIndex++) {
        const cellCol = cellIndex % props.columns;
        const cellRow = Math.floor(cellIndex / props.columns);
        const cellX = Math.floor(cellCol * maxWidth);
        const cellY = Math.floor(cellRow * maxHeight);

        // Draw all sprites with transparency in this cell
        ctx.value.globalAlpha = 0.3;
        props.sprites.forEach((sprite, spriteIndex) => {
          if (spriteIndex !== cellIndex) {
            // Don't draw the cell's own sprite with transparency
            ctx.value?.drawImage(sprite.img, Math.floor(cellX + sprite.x), Math.floor(cellY + sprite.y));
          }
        });
        ctx.value.globalAlpha = 1.0;
      }
    }

    // Draw sprites normally
    props.sprites.forEach((sprite, index) => {
      // Skip the active sprite if we're showing a ghost instead
      if (activeSpriteId.value === sprite.id && ghostSprite.value) {
        return;
      }

      const col = index % props.columns;
      const row = Math.floor(index / props.columns);

      const cellX = Math.floor(col * maxWidth);
      const cellY = Math.floor(row * maxHeight);

      // Draw sprite using integer positions for pixel-perfect rendering
      ctx.value?.drawImage(sprite.img, Math.floor(cellX + sprite.x), Math.floor(cellY + sprite.y));
    });

    // Draw ghost sprite if we're dragging between cells
    if (ghostSprite.value && activeSpriteId.value) {
      const sprite = props.sprites.find(s => s.id === activeSpriteId.value);
      if (sprite) {
        // Semi-transparent ghost
        ctx.value.globalAlpha = 0.6;
        ctx.value.drawImage(sprite.img, Math.floor(ghostSprite.value.x), Math.floor(ghostSprite.value.y));
        ctx.value.globalAlpha = 1.0;
      }
    }

    // Draw grid lines on top of everything
    ctx.value.strokeStyle = settingsStore.darkMode ? '#4B5563' : '#e5e7eb';
    ctx.value.lineWidth = 1;

    for (let col = 0; col < props.columns; col++) {
      for (let row = 0; row < rows; row++) {
        const cellX = Math.floor(col * maxWidth);
        const cellY = Math.floor(row * maxHeight);

        // Draw grid lines
        ctx.value.strokeRect(cellX, cellY, maxWidth, maxHeight);
      }
    }
  };

  // Track which images already have listeners
  const imagesWithListeners = new WeakSet<HTMLImageElement>();

  const attachImageListeners = () => {
    props.sprites.forEach(sprite => {
      const img = sprite.img as HTMLImageElement | undefined;
      if (img && !imagesWithListeners.has(img)) {
        imagesWithListeners.add(img);
        if (!img.complete) {
          // Redraw when the image loads or errors (to reflect updated dimensions)
          img.addEventListener('load', handleForceRedraw, { once: true } as AddEventListenerOptions);
          img.addEventListener('error', handleForceRedraw, { once: true } as AddEventListenerOptions);
        }
      }
    });
  };

  onMounted(() => {
    if (canvasRef.value) {
      ctx.value = canvasRef.value.getContext('2d');
      drawCanvas();
    }

    // Attach listeners for current sprites
    attachImageListeners();

    // Listen for forceRedraw event from App.vue
    window.addEventListener('forceRedraw', handleForceRedraw);

    // Hide context menu when clicking elsewhere
    document.addEventListener('click', hideContextMenu);
  });

  onUnmounted(() => {
    window.removeEventListener('forceRedraw', handleForceRedraw);
    document.removeEventListener('click', hideContextMenu);
  });

  // Handler for force redraw event
  const handleForceRedraw = () => {
    // Ensure we're using integer positions for pixel-perfect rendering
    props.sprites.forEach(sprite => {
      sprite.x = Math.floor(sprite.x);
      sprite.y = Math.floor(sprite.y);
    });

    // Force a redraw with the correct image smoothing settings
    if (ctx.value) {
      ctx.value.imageSmoothingEnabled = !settingsStore.pixelPerfect;
      drawCanvas();
    }
  };

  watch(
    () => props.sprites,
    () => {
      attachImageListeners();
      drawCanvas();
    },
    { deep: true }
  );
  watch(() => props.columns, drawCanvas);
  watch(() => settingsStore.pixelPerfect, drawCanvas);
  watch(() => settingsStore.darkMode, drawCanvas);
  watch(showAllSprites, drawCanvas);
</script>

<style scoped></style>
