<!-- 使用示例 已经全局暴露直接用就行 注意！这个组件的性能不如用AE写的动画-->
<!-- <donghua :width="`1300rpx`" :height="`900rpx`" :links="blueArray" :playing="photoplay" :loop="true" :interval="120" /> -->
<!-- 注意看参数是什么意思 -->
<!-- 通用的生成函数 这个方法可以快速让你写出图片数组
	function genPaths(base, prefix, count, ext = 'png', startIndex = 0, pad = false) {
		return Array.from({ length: count }, (_, i) => {
			const idx = pad
				? String(i + startIndex).padStart(2, '0')
				: i + startIndex
			return `${base}/${prefix}${idx}.${ext}`
		})
	} -->
<!-- 数组的示例
	const leftArray = ref(genPaths(
		'/static/index/newindex/leftmenu',地址
		'',图片前缀
		3,     // 一共加一起多少张图片
		'png',	类型
		0,     // 起始索引
		false  // 不补零
	)) -->
<template>
	<view>
		<image :src="displaySrc" :style="{ width: width, height: height }" :mode="objectFit" @error="handleError"
			@load="handleLoad" />
		<button v-if="showButton" @click="togglePlaying">
			{{ playing ? '停止播放' : '开始播放' }}
		</button>
	</view>
</template>

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

	/* ---------------- props ---------------- */
	const props = defineProps({
		links: {
			type: Array as () => string[],
			default: () => []
		},
		width: { type: String, default: '65rpx' },
		height: { type: String, default: '65rpx' },
		objectFit: { type: String, default: 'aspectFill' },
		defaultImage: { type: String, default: '' },
		interval: { type: Number, default: 80 }, // ms
		playing: { type: Boolean, default: false },
		showButton: { type: Boolean, default: false },
		loop: { type: Boolean, default: false },
		// 可选：每帧最大重试次数（失败后跳过）
		maxRetryPerFrame: { type: Number, default: 1 }
	})

	const emit = defineEmits(['update:playing'])

	/* ---------------- local state ---------------- */
	const currentIndex = ref(0)
	const internalPlaying = ref(false) // 与 props.playing 同步（主要用于内部判断）
	const timer : { id : number | null } = { id: null } // 使用对象包装，方便类型
	// 记录每帧失败次数
	const frameRetries = new Map<number, number>()
	// 记录已经判定为“失败很严重、需要显示默认图”的帧
	const failedFrames = new Set<number>()

	/* ---------------- helpers / computed ---------------- */
	const hasLinks = computed(() => Array.isArray(props.links) && props.links.length > 0)
	const displaySrc = computed(() => {
		if (!hasLinks.value) {
			return props.defaultImage || ''
		}
		// 如果该帧被标为失败则显示 defaultImage（兜底）
		if (failedFrames.has(currentIndex.value) && props.defaultImage) {
			return props.defaultImage
		}
		// 正常显示链接（或 defaultImage 如果索引越界）
		const idx = currentIndex.value
		return props.links[idx] || props.defaultImage || ''
	})

	/* ---------------- play control (使用递归 setTimeout) ---------------- */
	function clearTimer() {
		if (timer.id !== null) {
			clearTimeout(timer.id)
			timer.id = null
		}
	}

	function scheduleNextTick() {
		// 防止重复 schedule
		if (timer.id !== null) return
		timer.id = setTimeout(() => {
			timer.id = null
			tickFrame()
			// 继续循环（如果仍在播放）
			if (props.playing) {
				// 若没有 links 则不再 schedule
				if (hasLinks.value) {
					scheduleNextTick()
				}
			}
		}, Math.max(16, props.interval)) as unknown as number
	}

	function startPlay() {
		if (!hasLinks.value) return
		if (timer.id !== null) return
		internalPlaying.value = true
		scheduleNextTick()
	}

	function stopPlay() {
		internalPlaying.value = false
		clearTimer()
	}

	function tickFrame() {
		// 当没有帧时什么也不做
		if (!hasLinks.value) return

		const len = props.links.length
		if (len === 0) return

		if (props.loop) {
			// 循环播放
			currentIndex.value = (currentIndex.value + 1) % len
		} else {
			// 非循环播放：到末尾停止并通知父组件
			if (currentIndex.value < len - 1) {
				currentIndex.value++
			} else {
				// 到尾了，停止播放
				stopPlay()
				// 告知父组件（保持 playing 为单一真相）
				emit('update:playing', false)
			}
		}
	}

	/* ---------------- image event handlers ---------------- */
	function handleError() {
		// 记录重试次数并决定是否跳过该帧
		const idx = currentIndex.value
		const prev = frameRetries.get(idx) || 0
		const nextCount = prev + 1
		frameRetries.set(idx, nextCount)

		if (nextCount > (props.maxRetryPerFrame || 0)) {
			// 标记为失败帧，用 defaultImage 兜底并尝试跳到下一帧（避免卡住）
			failedFrames.add(idx)
			// 立即跳到下一帧（但不用强制立即 schedule — 因为 scheduleNextTick 会继续）
			// 若你想立即切换也可以直接调用 tickFrame()
			if (hasLinks.value) {
				// 如果是 loop 模式或尚未到尾（非 loop），直接 advance，否则 stop
				if (props.loop || currentIndex.value < props.links.length - 1) {
					// 立即 advance 一帧，但不要重复 schedule（tickFrame 内部不依赖 timer）
					tickFrame()
				} else {
					stopPlay()
					emit('update:playing', false)
				}
			}
		} else {
			// 尝试跳过到下一帧（减少卡顿风险）
			if (hasLinks.value) {
				if (props.loop || currentIndex.value < props.links.length - 1) {
					tickFrame()
				} else {
					stopPlay()
					emit('update:playing', false)
				}
			}
		}
	}

	function handleLoad() {
		// 成功加载清理重试记录（该帧已正常）
		const idx = currentIndex.value
		frameRetries.delete(idx)
		failedFrames.delete(idx)
	}

	/* ---------------- watch props ---------------- */
	// 以 props.playing 为单一真相源；当 props.playing 改变时启动/停止
	watch(() => props.playing, (val) => {
		if (val) {
			// reset index 到 0（可选，如果你希望保留进度可以去掉）
			// currentIndex.value = 0
			startPlay()
		} else {
			stopPlay()
			// 保证视觉上复位到首帧（可选）
			setTimeout(() => {
				currentIndex.value = 0
			}, 50)
		}
	})

	// 监听 links 的引用变化（但不要 deep watch）
	// 当父组件传入新的数组对象时触发：重置状态，清理 retry/failed 信息
	watch(() => props.links, (newLinks, oldLinks) => {
		// 如果引用相同且长度相同，尽量不重置（减少不必要抖动）
		if (newLinks === oldLinks) return
		// 重置索引和错误记录
		currentIndex.value = 0
		frameRetries.clear()
		failedFrames.clear()
		// 如果当前 props.playing 为 true，重启播放（先清理 timer 再开始）
		if (props.playing) {
			stopPlay()
			// 延迟一点点再启动，避免连续多次触发时重复 schedule
			setTimeout(() => {
				if (props.playing) startPlay()
			}, 30)
		}
	}, { immediate: false })

	/* ---------------- toggle via internal button ---------------- */
	function togglePlaying() {
		emit('update:playing', !props.playing)
	}

	/* ---------------- cleanup ---------------- */
	onUnmounted(() => {
		clearTimer()
	})
</script>