<template>
  <div class="space-y-4">
    <div class="flex justify-between items-center">
      <div class="flex items-center">
        <input id="pixel-perfect" type="checkbox" v-model="pixelPerfect" class="mr-2" @change="drawCanvas" />
        <label for="pixel-perfect">Pixel perfect rendering (for pixel art)</label>
      </div>
    </div>

    <div class="relative border border-gray-300 rounded-lg">
      <canvas ref="canvasRef" @mousedown="startDrag" @mousemove="drag" @mouseup="stopDrag" @mouseleave="stopDrag" @touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="stopDrag" class="w-full"></canvas>
    </div>
  </div>
</template>

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

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

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

  const emit = defineEmits<{
    (e: 'updateSprite', id: string, x: number, y: number): void;
  }>();

  // Pixel art optimization
  const pixelPerfect = ref(true);

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

  // Dragging state
  const isDragging = ref(false);
  const activeSpriteId = ref<string | null>(null);
  const dragStartX = ref(0);
  const dragStartY = ref(0);
  const spritePosBeforeDrag = ref({ x: 0, y: 0 });

  const spritePositions = computed(() => {
    if (!canvasRef.value) return [];

    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,
      };
    });
  });

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

    props.sprites.forEach(sprite => {
      maxWidth = Math.max(maxWidth, sprite.width);
      maxHeight = Math.max(maxHeight, sprite.height);
    });

    return { maxWidth, maxHeight };
  };

  const startDrag = (event: MouseEvent) => {
    if (!canvasRef.value) 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 current sprite position
      const sprite = props.sprites.find(s => s.id === clickedSprite.id);
      if (sprite) {
        spritePosBeforeDrag.value = { x: sprite.x, y: sprite.y };
      }
    }
  };

  const drag = (event: MouseEvent) => {
    if (!isDragging.value || !activeSpriteId.value || !canvasRef.value) 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 deltaX = mouseX - dragStartX.value;
    const deltaY = mouseY - dragStartY.value;

    // Calculate new position with constraints
    const spriteIndex = props.sprites.findIndex(s => s.id === activeSpriteId.value);
    if (spriteIndex === -1) return;

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

    let newX = spritePosBeforeDrag.value.x + deltaX;
    let newY = spritePosBeforeDrag.value.y + deltaY;

    // Constrain movement strictly within cell
    newX = Math.max(0, Math.min(position.maxWidth - props.sprites[spriteIndex].width, newX));
    newY = Math.max(0, Math.min(position.maxHeight - props.sprites[spriteIndex].height, newY));

    emit('updateSprite', activeSpriteId.value, newX, newY);
    drawCanvas();
  };

  const stopDrag = () => {
    isDragging.value = false;
    activeSpriteId.value = null;
  };

  const handleTouchStart = (event: TouchEvent) => {
    if (event.touches.length === 1) {
      const touch = event.touches[0];
      const mouseEvent = new MouseEvent('mousedown', {
        clientX: touch.clientX,
        clientY: touch.clientY,
      });
      startDrag(mouseEvent);
    }
  };

  const handleTouchMove = (event: TouchEvent) => {
    if (event.touches.length === 1) {
      const touch = event.touches[0];
      const mouseEvent = new MouseEvent('mousemove', {
        clientX: touch.clientX,
        clientY: touch.clientY,
      });
      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];
      const sprite = props.sprites.find(s => s.id === pos.id);

      if (!sprite) continue;

      if (x >= pos.canvasX && x <= pos.canvasX + sprite.width && y >= pos.canvasY && y <= pos.canvasY + sprite.height) {
        return sprite;
      }
    }
    return null;
  };

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

    const { maxWidth, maxHeight } = calculateMaxDimensions();

    // Set canvas size
    const rows = 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
    ctx.value.imageSmoothingEnabled = false;

    // Draw grid
    ctx.value.strokeStyle = '#e5e7eb';
    for (let col = 0; col < props.columns; col++) {
      for (let row = 0; row < rows; row++) {
        ctx.value.strokeRect(Math.floor(col * maxWidth), Math.floor(row * maxHeight), maxWidth, maxHeight);
      }
    }

    // Draw sprites
    props.sprites.forEach((sprite, index) => {
      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
      ctx.value?.drawImage(sprite.img, Math.floor(cellX + sprite.x), Math.floor(cellY + sprite.y));
    });
  };

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

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