<template>
  <div v-if="hasSpriteConfig" class="sprite-animator" :style="spriteStyle" />
</template>

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

const props = withDefaults(
  defineProps<{
    src?: string;
    frameWidth?: number;
    frameHeight?: number;
    totalFrames?: number;
    columns?: number;
    displayWidth?: number;
    fps?: number;
    autoplay?: boolean;
    loop?: boolean;
  }>(),
  {
    src: "",
    frameWidth: 0,
    frameHeight: 0,
    totalFrames: 0,
    columns: 0,
    displayWidth: 0,
    fps: 12,
    autoplay: true,
    loop: true,
  },
);

/* JS 精确逐帧方案（使用 setInterval，确保整数偏移、无插值） */
const currentFrame = ref(0);
let intervalId: number | null = null;

const play = () => {
  stop();
  const safeFps = Math.max(1, Math.floor(props.fps || 12));
  const durationPerFrame = 1000 / safeFps;
  // 强制整数像素偏移，使用 setInterval 在各运行时更可靠
  const setter = (globalThis as any).setInterval || setInterval;
  intervalId = setter(() => {
    const safeTotal = Math.max(1, Math.floor(props.totalFrames || 0));
    if (currentFrame.value >= safeTotal - 1) {
      if (props.loop) {
        currentFrame.value = 0;
      } else {
        stop();
        return;
      }
    } else {
      currentFrame.value++;
    }
  }, durationPerFrame) as unknown as number;
};

const stop = () => {
  if (intervalId) {
    clearInterval(intervalId);
    intervalId = null;
  }
};

onMounted(() => {
  console.log("[SpriteAnimator] mounted", {
    autoplay: props.autoplay,
    fps: props.fps,
  });
  if (props.autoplay) play();
});

onUnmounted(() => stop());

const hasSpriteConfig = computed(() => {
  return (
    !!props.src &&
    Number(props.frameWidth) > 0 &&
    Number(props.frameHeight) > 0 &&
    Number(props.totalFrames) > 0 &&
    Number(props.columns) > 0
  );
});

const spriteStyle = computed(() => {
  const {
    src,
    frameWidth: rawFrameWidth,
    frameHeight: rawFrameHeight,
    totalFrames: rawTotalFrames,
    columns: rawColumns,
  } = props;

  const frameWidth = Math.max(1, Math.floor(rawFrameWidth || 0));
  const frameHeight = Math.max(1, Math.floor(rawFrameHeight || 0));
  const totalFrames = Math.max(1, Math.floor(rawTotalFrames || 0));
  let columns = Math.floor(Number(rawColumns) || 0);
  if (!columns || columns <= 0) columns = Math.min(1, totalFrames);
  columns = Math.min(columns, totalFrames);

  const displayWidth = props.displayWidth || frameWidth;

  const rows = Math.ceil(totalFrames / columns);
  const imageWidth = columns * frameWidth;
  const imageHeight = rows * frameHeight;

  const scale = displayWidth / frameWidth;
  const displayHeight = Math.round(frameHeight * scale);

  const scaledImageWidth = Math.round(imageWidth * scale);
  const scaledImageHeight = Math.round(imageHeight * scale);

  if (currentFrame.value < 0) currentFrame.value = 0;
  if (currentFrame.value >= totalFrames) currentFrame.value = totalFrames - 1;
  const col = currentFrame.value % columns;
  const row = Math.floor(currentFrame.value / columns);

  const offsetX = Math.round(col * frameWidth * scale);
  const offsetY = Math.round(row * frameHeight * scale);

  const styleStr = `width: ${Math.round(displayWidth)}px; height: ${displayHeight}px; background-image: url("${src}"); background-repeat: no-repeat; background-size: ${scaledImageWidth}px ${scaledImageHeight}px; background-position: -${offsetX}px -${offsetY}px;`;
  return styleStr as any;
});

// 对外暴露
defineExpose({ play, stop, currentFrame });
</script>

<style scoped>
.sprite-animator {
  display: block;
  will-change: background-position;
}
</style>
