<template>
  <div
    :style="boxStyle"
    class="w-full h-full flex items-center justify-center overflow-hidden relative"
  >
    <ImageBg
      v-if="styleMap?.mainStyle?.imageSrc"
      :src="styleMap?.mainStyle?.imageSrc"
      :image-style="styleMap?.mainStyle?.imageStyle"
    />
    <ImageBg
      :src="currentSrc || defaultImg"
      :image-style="styleMap?.mainStyle?.image"
      :image-props="imageProps"
    />
  </div>
</template>

<script setup lang="ts">
import { computed, ImgHTMLAttributes, onBeforeUnmount, ref, watch } from 'vue'
import { useProjectStore } from '@/store/modules/project'
import defaultImg from '@/assets/default.png'
import ImageBg from '../ImageBg.vue'
import { useWidgetStyle } from '../hooks/useWidgetStyle'

const props = defineProps<{
  width: number
  height: number
  styles: any
  part?: string
  state?: string
  images: string[]
  time: number
  repeatCount: number
  playback: boolean
  playbackTime: number
  playbackDelay: number
  autoPlay: boolean
  reverse: boolean
}>()

const projectStore = useProjectStore()
const currentIndex = ref(0)
let timer: ReturnType<typeof setTimeout> | null = null

const styleMap = useWidgetStyle({
  widget: 'lv_animimg',
  props
})

const frameSources = computed(() => {
  return (props.images || [])
    .map((id) => {
      const imagePath = projectStore.project?.resources.images.find((item) => item.id === id)?.path
      if (!imagePath) return ''
      return `local:///${(projectStore.projectPath + imagePath).replaceAll('\\', '/')}`
    })
    .filter((item) => item)
})

const currentSrc = computed(() => frameSources.value[currentIndex.value] || '')

const imageProps = computed((): ImgHTMLAttributes => {
  return {
    width: '100%',
    height: '100%',
    style: {
      objectFit: 'fill'
    }
  }
})

const boxStyle = computed(() => {
  return {
    ...(styleMap.value?.mainStyle || {}),
    width: `${props.width}px`,
    height: `${props.height}px`,
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center'
  }
})

const clearTimer = () => {
  if (timer) {
    clearTimeout(timer)
    timer = null
  }
}

const setInitialFrame = () => {
  const count = frameSources.value.length
  currentIndex.value = count ? (props.reverse ? count - 1 : 0) : 0
}

const buildCycleSteps = () => {
  const count = frameSources.value.length
  if (!count) return [] as { index: number; delay: number }[]

  const all = Array.from({ length: count }, (_, index) => index)
  const primary = props.reverse ? [...all].reverse() : all
  const secondary = props.playback
    ? props.reverse
      ? all.slice(1)
      : [...all].reverse().slice(1)
    : []

  const primaryDelay =
    primary.length > 1 ? Math.max(0, props.time / Math.max(primary.length - 1, 1)) : props.time
  const secondaryDelay =
    secondary.length > 0
      ? Math.max(0, props.playbackTime / Math.max(secondary.length, 1))
      : props.playbackTime

  const steps: { index: number; delay: number }[] = []

  primary.forEach((index, stepIndex) => {
    steps.push({
      index,
      delay:
        stepIndex === primary.length - 1
          ? secondary.length
            ? props.playbackDelay
            : 0
          : primaryDelay
    })
  })

  secondary.forEach((index) => {
    steps.push({
      index,
      delay: secondaryDelay
    })
  })

  return steps
}

const startPlayback = () => {
  clearTimer()
  const steps = buildCycleSteps()
  if (!steps.length) {
    currentIndex.value = 0
    return
  }

  currentIndex.value = steps[0].index

  if (!props.autoPlay || steps.length === 1) {
    return
  }

  const totalCycles = props.repeatCount === -1 ? Number.POSITIVE_INFINITY : props.repeatCount + 1
  let cycleIndex = 0
  let stepIndex = 0

  const schedule = () => {
    if (!props.autoPlay) return

    const currentStep = steps[stepIndex]
    timer = setTimeout(
      () => {
        if (stepIndex < steps.length - 1) {
          stepIndex += 1
          currentIndex.value = steps[stepIndex].index
          schedule()
          return
        }

        cycleIndex += 1
        if (cycleIndex >= totalCycles) {
          return
        }

        stepIndex = 0
        currentIndex.value = steps[0].index
        schedule()
      },
      Math.max(0, currentStep.delay)
    )
  }

  schedule()
}

watch(
  () => [
    props.images,
    props.time,
    props.repeatCount,
    props.playback,
    props.playbackTime,
    props.playbackDelay,
    props.autoPlay,
    props.reverse
  ],
  () => {
    setInitialFrame()
    startPlayback()
  },
  {
    deep: true,
    immediate: true
  }
)

onBeforeUnmount(() => {
  clearTimer()
})
</script>
