<script setup lang="ts">
import { assetUrl } from '@/commons/assets.ts'
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'

const props = defineProps<{
    imageUrls: Record<string, string>
    countdownInterval: number
    isDivDescVisible: boolean
    tick: number
    rank: number
    currentGu: string
}>()

const emit = defineEmits<{
    hit: [score: number]
}>()

// ── 圈圈丢出动画帧 (31帧, 原图325×733 → 渲染宽165) ──
const circleAnimSprite = assetUrl('game5/sprites/circle-animation.png')

// ── 四种马的帧 (各21帧, 原图196×204 → 渲染180×160) ──
const horseSprites: Record<number, string> = {
    1: assetUrl('game5/sprites/horse1-no-circle.png'),
    2: assetUrl('game5/sprites/horse2-no-circle.png'),
    3: assetUrl('game5/sprites/horse3-no-circle.png'),
    4: assetUrl('game5/sprites/horse4-no-circle.png'),
}

// ── 布局常量 ──
const STAGE_WIDTH = 750
const STAGE_HEIGHT = 1624
const RUNWAY_TOP = 696
const RUNWAY_HEIGHT = 306
const RUNWAY_HALF_Y = RUNWAY_TOP + RUNWAY_HEIGHT / 2  // 849
const HORSE_WIDTH = 180
const HORSE_HEIGHT = 200                                  // 原图196×204→渲染180×200，留足跳跃帧头部空间
const HORSE_Y = RUNWAY_TOP + (RUNWAY_HEIGHT - HORSE_HEIGHT) / 2  // 749
const CIRCLE_THROW_MS = 1200  // 圈圈飞行时长
const HIT_FADE_MS = 400       // 被套中后马消失延迟

// ── 难度递增参数：随游戏时间从 0→60s 指数曲线变化 ──
const HORSE_SPEED_MIN = 30    // px/s  初始速度
const HORSE_SPEED_MAX = 200   // px/s  最大速度（60s 时，7倍提升）
const SPAWN_MIN_START = 3000  // ms    初始最短生成间隔
const SPAWN_MIN_END = 800     // ms    最终最短生成间隔
const SPAWN_MAX_START = 6000  // ms    初始最长生成间隔
const SPAWN_MAX_END = 1800    // ms    最终最长生成间隔
const MAX_HORSES_ON_SCREEN = 8 // 场上最多马匹数

// 马动画帧率控制（参考 Rank1View：每 N 个 raf tick 切一帧 → ~20fps）
const HORSE_FRAME_TICK = 3

// circle-outer: bottom:72, height:345 → outerTop = 1624 - 72 - 345 = 1207
// circle-inner 圆心 Y = outerTop + 345/2 = 1379.5
const CIRCLE_CENTER_X = 375
const CIRCLE_CENTER_Y = STAGE_HEIGHT - 72 - 345 + 345 / 2  // 1379.5
const CIRCLE_ANIM_WIDTH = 165
const CIRCLE_SPRITE_COLS = 8
const HORSE_SPRITE_COLS = 7

function spritePosition(frameIdx: number, cols: number, frameWidth: number, frameHeight: number) {
    return `-${(frameIdx % cols) * frameWidth}px -${Math.floor(frameIdx / cols) * frameHeight}px`
}
const CIRCLE_ANIM_HEIGHT = Math.round(733 * (CIRCLE_ANIM_WIDTH / 325))  // ≈ 372

// ── 类型 ──
interface Horse {
    id: number
    type: number
    x: number
    frameIdx: number
    frameTimer: number   // 帧切换滴答计数器（每帧+1，满 HORSE_FRAME_TICK 切帧）
    alive: boolean
    dying: boolean
    dieAt: number
}

interface CircleThrow {
    y: number
    frameIdx: number
    startY: number
    startTime: number
}

interface HitEffect {
    id: number
    x: number
    y: number
    score: number
}

// ── 响应式状态 ──
const horses = ref<Horse[]>([])
const circleThrow = ref<CircleThrow | null>(null)
const isCircleFlying = ref(false)
const hitEffects = ref<HitEffect[]>([])

// ── 内部变量 ──
let horseIdSeq = 0
let hitEffectIdSeq = 0
let raf = 0
let prevTs = 0
let spawnAccum = 0
let nextSpawn = 2000
let gameElapsed = 0   // 游戏已进行时间 ms（用于难度递增）

const HIT_EFFECT_MS = 800  // "+10" 动画持续时长

// ── 游戏状态 ──
const gameStarted = ref(true)     // MobileStage 已完成 3-2-1，游戏直接开始
const gameActive = ref(true)      // 游戏进行中（倒计时到 0 后变 false）

// ── Web Audio API 音效 ──
let audioContext: AudioContext | null = null

function getAudioContext(): AudioContext {
    if (!audioContext) {
        audioContext = new AudioContext()
    }
    if (audioContext.state === 'suspended') {
        audioContext.resume()
    }
    return audioContext
}

/** 丢圈"咻"声 — 短促上升频率扫频 */
function playThrowSound() {
    try {
        const ctx = getAudioContext()
        const now = ctx.currentTime
        const osc = ctx.createOscillator()
        const gain = ctx.createGain()
        osc.connect(gain)
        gain.connect(ctx.destination)
        osc.type = 'sine'
        osc.frequency.setValueAtTime(400, now)
        osc.frequency.exponentialRampToValueAtTime(800, now + 0.08)
        osc.frequency.exponentialRampToValueAtTime(200, now + 0.2)
        gain.gain.setValueAtTime(0.25, now)
        gain.gain.exponentialRampToValueAtTime(0.01, now + 0.2)
        osc.start(now)
        osc.stop(now + 0.2)
    } catch { /* ignore audio errors */ }
}

/** 套中"叮"声 — 两个简短的上升音 */
function playHitSound() {
    try {
        const ctx = getAudioContext()
        const now = ctx.currentTime
        // 第一音 880 Hz
        const o1 = ctx.createOscillator()
        const g1 = ctx.createGain()
        o1.connect(g1)
        g1.connect(ctx.destination)
        o1.type = 'sine'
        o1.frequency.setValueAtTime(880, now)
        g1.gain.setValueAtTime(0.3, now)
        g1.gain.exponentialRampToValueAtTime(0.01, now + 0.25)
        o1.start(now)
        o1.stop(now + 0.25)
        // 第二音 1320 Hz 延时 0.08s
        const o2 = ctx.createOscillator()
        const g2 = ctx.createGain()
        o2.connect(g2)
        g2.connect(ctx.destination)
        o2.type = 'sine'
        o2.frequency.setValueAtTime(1320, now + 0.08)
        g2.gain.setValueAtTime(0.22, now + 0.08)
        g2.gain.exponentialRampToValueAtTime(0.01, now + 0.35)
        o2.start(now + 0.08)
        o2.stop(now + 0.35)
    } catch { /* ignore audio errors */ }
}

// ── 清理全部音效（组件卸载时） ──
function disposeAudioContext() {
    if (audioContext) {
        audioContext.close().catch(() => {})
        audioContext = null
    }
}

// ── 难度系数（0~1，游戏 60s，指数曲线让后期明显加速） ──
function difficultyFactor(): number {
    return Math.min(gameElapsed / 60000, 1)
}

// ── 当前马速度（二次曲线：前期缓、后期猛） ──
function currentSpeed(): number {
    const t = difficultyFactor()
    // 二次曲线：t² 让前期平缓、30s后加速明显
    return HORSE_SPEED_MIN + (HORSE_SPEED_MAX - HORSE_SPEED_MIN) * t * t
}

// ── 生成一匹马 ──
function spawnHorse() {
    const type = Math.floor(Math.random() * 4) + 1
    horses.value.push({
        id: ++horseIdSeq,
        type,
        x: -HORSE_WIDTH - Math.random() * 200,
        frameIdx: Math.floor(Math.random() * 21),   // 随机起始帧，避免多马同帧
        frameTimer: 0,
        alive: true,
        dying: false,
        dieAt: 0,
    })
}

// ── 点击丢出圈圈 ──
function throwCircle() {
    if (!gameActive.value || !gameStarted.value || isCircleFlying.value) return
    isCircleFlying.value = true
    playThrowSound()
    circleThrow.value = {
        y: CIRCLE_CENTER_Y,
        frameIdx: 0,
        startY: CIRCLE_CENTER_Y,
        startTime: performance.now(),
    }
}

// ── 碰撞检测（收紧：圈心必须落在马身体中间区域） ──
function checkHit(ts: number): boolean {
    if (!circleThrow.value) return false
    const ct = circleThrow.value
    const cx = CIRCLE_CENTER_X
    const cy = ct.y

    // 命中区域：马身体宽度 20%~80%、高度 20%~80%（中间 60% 区域）
    const hitMarginX = HORSE_WIDTH * 0.2
    const hitMarginY = HORSE_HEIGHT * 0.2
    const halfHitW = HORSE_WIDTH / 2 - hitMarginX
    const halfHitH = HORSE_HEIGHT / 2 - hitMarginY

    for (const h of horses.value) {
        if (!h.alive || h.dying) continue
        const horseCenterX = h.x + HORSE_WIDTH / 2
        const horseCenterY = HORSE_Y + HORSE_HEIGHT / 2
        if (
            Math.abs(cx - horseCenterX) <= halfHitW &&
            Math.abs(cy - horseCenterY) <= halfHitH
        ) {
            h.dying = true
            h.dieAt = ts
            circleThrow.value = null
            isCircleFlying.value = false
            // 在马头顶生成 "+10" 飘字效果
            const effectId = ++hitEffectIdSeq
            hitEffects.value.push({
                id: effectId,
                x: horseCenterX,
                y: HORSE_Y,
                score: 10,
            })
            setTimeout(() => {
                const idx = hitEffects.value.findIndex(e => e.id === effectId)
                if (idx >= 0) hitEffects.value.splice(idx, 1)
            }, HIT_EFFECT_MS)
            playHitSound()
            emit('hit', 10)
            return true
        }
    }
    return false
}

// ── 主循环 ──
function loop(ts: number) {
    if (!gameActive.value) {
        // 游戏结束：保持最后一帧，不再更新动画
        raf = requestAnimationFrame(loop)
        return
    }
    if (!prevTs) prevTs = ts
    const dt = Math.min(ts - prevTs, 50)
    prevTs = ts
    gameElapsed += dt

    // 1. 更新飞行中的圈圈
    if (circleThrow.value) {
        const ct = circleThrow.value
        const elapsed = ts - ct.startTime
        const progress = Math.min(elapsed / CIRCLE_THROW_MS, 1)
        ct.y = ct.startY - (ct.startY - RUNWAY_HALF_Y) * progress
        ct.frameIdx = Math.min(Math.floor(progress * 31), 30)

        if (checkHit(ts)) {
            // 命中：已处理
        } else if (progress >= 1) {
            // 落空
            circleThrow.value = null
            isCircleFlying.value = false
        }
    }

    // 2. 更新马匹位置（速度随难度递增，帧率控制防闪烁）
    const speed = currentSpeed()
    for (let i = horses.value.length - 1; i >= 0; i--) {
        const h = horses.value[i]!
        if (h.dying) {
            if (ts - h.dieAt >= HIT_FADE_MS) {
                horses.value.splice(i, 1)
            }
            continue
        }
        h.x += speed * (dt / 1000)
        // 帧率控制：每 HORSE_FRAME_TICK 次循环切换一帧（约 20fps）
        h.frameTimer++
        if (h.frameTimer >= HORSE_FRAME_TICK) {
            h.frameTimer = 0
            h.frameIdx = (h.frameIdx + 1) % 21
        }
        if (h.x > STAGE_WIDTH + 120) {
            horses.value.splice(i, 1)
        }
    }

    // 3. 生成新马（难度递增：间隔越来越短、数量受上限约束）
    spawnAccum += dt
    if (spawnAccum >= nextSpawn) {
        spawnAccum = 0
        const t = difficultyFactor()
        const t2 = t * t   // 二次曲线与速度同步
        const minSpawn = SPAWN_MIN_START + (SPAWN_MIN_END - SPAWN_MIN_START) * t2
        const maxSpawn = SPAWN_MAX_START + (SPAWN_MAX_END - SPAWN_MAX_START) * t2
        nextSpawn = minSpawn + Math.random() * (maxSpawn - minSpawn)
        if (horses.value.length < MAX_HORSES_ON_SCREEN) {
            spawnHorse()
        }
    }

    raf = requestAnimationFrame(loop)
}

// ── 预加载所有帧图片，避免运行时切换帧的闪烁 ──
function preloadSprites() {
    for (const url of [circleAnimSprite, ...Object.values(horseSprites)]) {
        const img = new Image()
        img.src = url
    }
}

// ── 监听父组件倒计时，到 0 时暂停游戏 ──
watch(() => props.countdownInterval, (val) => {
    if (val <= 0 && gameActive.value) {
        gameActive.value = false
        isCircleFlying.value = false
        circleThrow.value = null
    }
})

onMounted(() => {
    preloadSprites()
    // 先启动音频上下文（需用户首次交互后 resume，此处尝试提前激活）
    try { getAudioContext() } catch { /* ignore */ }
    // MobileStage 已完成 3-2-1，游戏直接开始
    prevTs = performance.now()
    spawnHorse()
    setTimeout(spawnHorse, 800)
    setTimeout(spawnHorse, 1600)
    raf = requestAnimationFrame(loop)
})

onBeforeUnmount(() => {
    cancelAnimationFrame(raf)
    disposeAudioContext()
})
</script>

<template>
    <div class="game-stage">
        <Transition name="div-desc-slide" appear>
            <div v-if="isDivDescVisible" class="div-desc">
                <span class="desc-clock" aria-hidden="true"></span>
                <span class="desc-time">{{ countdownInterval }}</span>
            </div>
        </Transition>
        <div class="img-logo"></div>
        <div class="img-center-title"></div>
        <div class="rank-content">
            <div class="txt-left">
                <div>排名</div>
                <div class="txt-bold">{{ rank }}</div>
            </div>
            <div class="txt-right">
                <div>实时积分</div>
                <div class="txt-bold">{{ tick }}</div>
            </div>
        </div>
        <div class="paodao-runway-container">
            <div class="paodao-runway" :class="{ 'paodao-paused': !gameActive }"></div>
        </div>

        <!-- 奔跑的马匹（改用 <img> 防闪烁，参考 Rank1View） -->
        <template v-if="gameStarted">
            <div
                v-for="horse in horses"
                :key="horse.id"
                class="horse"
                :class="{ 'horse-dying': horse.dying }"
                :style="{ transform: `translateX(${horse.x}px)` }"
            >
                <div
                    class="horse-sprite"
                    :style="{
                        backgroundImage: `url(${horseSprites[horse.type]})`,
                        backgroundPosition: spritePosition(horse.frameIdx, HORSE_SPRITE_COLS, HORSE_WIDTH, HORSE_HEIGHT),
                    }"
                ></div>
                <div class="horse-shadow" :class="{ 'shadow-dying': horse.dying }"></div>
            </div>
        </template>

        <!-- 套中加分飘字效果 -->
        <div
            v-for="effect in hitEffects"
            :key="effect.id"
            class="hit-effect"
            :style="{ left: effect.x + 'px', top: effect.y + 'px' }"
        >+{{ effect.score }}</div>

        <!-- 飞行中的圈圈动画 -->
        <div
            v-if="gameStarted && circleThrow"
            class="circle-throw"
            :style="{
                transform: `translate(${CIRCLE_CENTER_X - CIRCLE_ANIM_WIDTH / 2}px, ${circleThrow.y - CIRCLE_ANIM_HEIGHT / 2}px)`,
                width: CIRCLE_ANIM_WIDTH + 'px',
                height: CIRCLE_ANIM_HEIGHT + 'px',
                backgroundImage: `url(${circleAnimSprite})`,
                backgroundPosition: spritePosition(circleThrow.frameIdx, CIRCLE_SPRITE_COLS, CIRCLE_ANIM_WIDTH, CIRCLE_ANIM_HEIGHT),
            }"
        ></div>

        <div class="circle-outer" :class="{ 'circle-disabled': !gameActive || !gameStarted, 'circle-paused': !gameActive }" @click="throwCircle">
            <div class="circle-inner"></div>
        </div>
        <div class="play-click">{{ !gameStarted ? '准备开始…' : !gameActive ? '游戏结束' : '点击按钮，开始套圈' }}</div>
    </div>
</template>

<style scoped>
.game-stage {
    position: absolute;
    left: 0;
    top: 0;
    width: 750px;
    height: 1624px;
    margin: 0;
    padding: 0;
}

.div-desc {
    position: absolute;
    top: 150px;
    left: var(--stage-viewport-left, 0);
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 24px;
    width: 180px;
    height: 80px;
    border-radius: 0 56px 56px 0;
    background: rgba(40, 12, 8, 0.48);
    color: #fff;


    .desc-clock {
        position: absolute;
        left: 20px;
        width: 38px;
        height: 44px;
        border-radius: 50%;
        background: v-bind('imageUrls.clock') center/cover;
    }

    .desc-time {
        position: absolute;
        right: 40px;
        color: white;
        font-size: 35pt;
        line-height: 1;
    }
}

.div-desc-slide-enter-active {
    transition:
        transform 360ms ease-out 220ms,
        opacity 360ms ease-out 220ms;
}

.div-desc-slide-leave-active {
    transition:
        transform 260ms ease-in,
        opacity 260ms ease-in;
}

.div-desc-slide-enter-from,
.div-desc-slide-leave-to {
    opacity: 0;
    transform: translateX(-110%);
}

.div-desc-slide-enter-to,
.div-desc-slide-leave-from {
    opacity: 1;
    transform: translateX(0);
}

.img-logo {
    position: absolute;
    top: 66px;
    left: 50%;
    width: 428px;
    height: 77px;
    background: v-bind('imageUrls.logo') center/cover;
    transform: translateX(-50%);
}

.img-center-title {
    position: absolute;
    top: 158px;
    left: 185px;
    width: 380px;
    height: 114px;
    background: v-bind('imageUrls.centerTile') center/cover;
}

.rank-content {
    width: 600px;
    height: 140px;
    background: v-bind('imageUrls.rank') center/cover;
    position: absolute;
    left: 50%;
    top: 380px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    transform: translate(-50%, -50%);
    font-size: 24px;
    color: #A10A06;
    z-index: 2;

    .txt-bold {
        font-size: 40px;
        font-weight: bold;
    }

    >div {
        display: flex;
        width: 50%;
        height: 100%;
        flex-direction: column;
        align-items: center;
        justify-content: center;
    }
}

/* ── 跑道容器：撑满 viewport 宽度并裁剪溢出 ── */
.paodao-runway-container {
    position: absolute;
    left: var(--stage-viewport-left, 0);
    top: 696px;
    width: var(--stage-viewport-width, 750px);
    height: 306px;
    overflow: hidden;
    z-index: 2;
}

/* ── 跑道内层：2x 宽度通过 transform 滚动，适配任意 viewport 宽 ── */
.paodao-runway {
    width: 200%;
    height: 100%;
    background: v-bind('imageUrls.paodao') repeat-x;
    background-size: auto 100%;
    animation: runway-scroll 12s linear infinite;
}

@keyframes runway-scroll {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); }
}

/* ── 跑道暂停 ── */
.paodao-paused {
    animation-play-state: paused;
}

/* ── 马匹 ── */
.horse {
    position: absolute;
    left: 0;
    top: v-bind('HORSE_Y + "px"');
    width: 180px;
    height: 200px;
    z-index: 3;
    will-change: transform;
}

.horse-sprite {
    width: 100%;
    height: 100%;
    background-size: 1260px 600px;
    background-repeat: no-repeat;
}

.horse-dying {
    opacity: 0;
    transition: opacity 0.3s ease, transform 0.3s ease;
}

/* ── 马匹脚底阴影 ── */
.horse-shadow {
    position: absolute;
    bottom: -10px;
    left: 50%;
    width: 110px;
    height: 18px;
    transform: translateX(-50%);
    background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.35) 0%, rgba(0, 0, 0, 0.10) 40%, rgba(0, 0, 0, 0) 72%);
    border-radius: 50%;
    pointer-events: none;
}

.shadow-dying {
    opacity: 0;
    transition: opacity 0.3s ease;
}

/* ── 套中加分飘字 ── */
.hit-effect {
    position: absolute;
    z-index: 20;
    color: #FFD700;
    font-size: 40px;
    font-weight: 900;
    pointer-events: none;
    text-shadow: 0 3px 6px rgba(0, 0, 0, 0.6);
    animation: score-float 0.8s ease-out forwards;
    white-space: nowrap;
    transform: translate(-50%, -100%);
}

@keyframes score-float {
    0% {
        opacity: 1;
        transform: translate(-50%, -100%) scale(0.5);
    }
    30% {
        opacity: 1;
        transform: translate(-50%, -130%) scale(1.1);
    }
    100% {
        opacity: 0;
        transform: translate(-50%, -220%) scale(0.8);
    }
}

/* ── 飞行中的圈圈 ── */
.circle-throw {
    position: absolute;
    left: 0;
    top: 0;
    background-size: 1320px 1488px;
    background-repeat: no-repeat;
    z-index: 10;
    pointer-events: none;
    will-change: transform, background-position;
    image-rendering: auto;
}

.circle-outer {
    position: absolute;
    left: 50%;
    bottom: 72px;
    width: 345px;
    height: 345px;
    background: v-bind('imageUrls.circleBg') center/cover;
    transform: translateX(-50%);
    animation: circle-spin 3s linear infinite;
    z-index: 4;
}

.circle-disabled {
    pointer-events: none;
    opacity: 0.5;
}

.circle-paused {
    animation-play-state: paused;
}

@keyframes circle-spin {
    0% {
        transform: translateX(-50%) rotate(0deg);
    }
    100% {
        transform: translateX(-50%) rotate(360deg);
    }
}

.circle-inner {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 165px;
    height: 165px;
    background: v-bind('imageUrls.circle') center/cover;
    transform: translate(-50%, -50%);
}

.play-click {
    position: absolute;
    bottom: 0px;
    left: 50%;
    width: 235px;
    height: 26px;
    transform: translateX(-50%) translateY(-190%);
    font-weight: 400;
    font-size: 26px;
    color: #FFFFFF;
    z-index: 5;
}
</style>
