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

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

const emit = defineEmits<{
    touch: [mole: boolean]
}>()

type TargetKind = 'mole' | 'rabbit'
type TargetState = 'idle' | 'show' | 'hide' | 'hit'

type TargetItem = {
    id: number
    kind: TargetKind | null
    state: TargetState
    showMs: number
    hideMs: number
    hitMs: number
    timer?: ReturnType<typeof window.setTimeout>
}

const list = ref<TargetItem[]>([]);
const moleContainerRef = ref<HTMLElement | null>(null)
for (let i = 0; i < 9; i++) {
    list.value.push({
        id: i,
        kind: null,
        state: 'idle',
        showMs: 650,
        hideMs: 350,
        hitMs: 700,
    })
}

let spawnTimer: ReturnType<typeof window.setTimeout> | undefined

function clearSpawnTimer() {
    if (spawnTimer) {
        window.clearTimeout(spawnTimer)
        spawnTimer = undefined
    }
}

function clearTargetTimer(item: TargetItem) {
    if (item.timer) {
        window.clearTimeout(item.timer)
        item.timer = undefined
    }
}

function setTargetState(index: number, state: TargetState) {
    const item = list.value[index]
    if (!item) {
        return
    }

    item.state = state
}

function resetTarget(index: number) {
    const item = list.value[index]
    if (!item) {
        return
    }

    clearTargetTimer(item)
    item.kind = null
    item.state = 'idle'
}

function playTargetLifecycle(index: number, kind: TargetKind, duration = 1000) {
    const item = list.value[index]
    if (!item) {
        return
    }

    clearTargetTimer(item)
    item.kind = kind
    item.state = 'show'
    item.showMs = Math.round(duration * 0.65)
    item.hideMs = Math.max(1, duration - item.showMs)
    item.hitMs = kind === 'mole' ? 740 : 660

    item.timer = window.setTimeout(() => {
        setTargetState(index, 'hide')
        item.timer = window.setTimeout(() => {
            resetTarget(index)
        }, item.hideMs)
    }, item.showMs)
}

function randomTargetKind(): TargetKind {
    return Math.random() < 0.75 ? 'mole' : 'rabbit'
}

function randomCount(min = 1, max = 3) {
    return Math.floor(Math.random() * (max - min + 1)) + min
}

function spawnRandomTargets(duration = 1000) {
    const idleIndexes = list.value
        .map((item, index) => item.state === 'idle' ? index : -1)
        .filter(index => index >= 0)
        .sort(() => Math.random() - 0.5)

    const count = Math.min(randomCount(1, 3), idleIndexes.length)
    for (const index of idleIndexes.slice(0, count)) {
        playTargetLifecycle(index, randomTargetKind(), duration)
    }
}

function scheduleRandomTargets() {
    if (props.isGameEnded) {
        return
    }
    spawnRandomTargets(1000)
    spawnTimer = window.setTimeout(scheduleRandomTargets, 1200)
}

function handleTargetTouch(index: number) {
    const item = list.value[index]
    if (!item || item.state === 'idle' || item.state === 'hit') {
        return
    }
    emit('touch', item.kind === 'mole')
    clearTargetTimer(item)
    item.state = 'hit'
    item.timer = window.setTimeout(() => {
        resetTarget(index)
    }, item.hitMs)
}

function handleBoardTouch(event: MouseEvent) {
    const container = moleContainerRef.value
    if (!container) {
        return
    }

    const rect = container.getBoundingClientRect()
    const x = ((event.clientX - rect.left) / rect.width) * 750
    const y = ((event.clientY - rect.top) / rect.height) * 600
    const colWidth = 250
    const rowGap = 70
    const rowHeight = (600 - rowGap * 2) / 3
    const hitRadius = 225

    let targetIndex = -1
    let targetDistance = Number.POSITIVE_INFINITY

    list.value.forEach((item, index) => {
        if (!item.kind || item.state === 'idle' || item.state === 'hit') {
            return
        }

        const col = index % 3
        const row = Math.floor(index / 3)
        const centerX = col * colWidth + colWidth / 2
        const centerY = row * (rowHeight + rowGap) + rowHeight / 2
        const distance = Math.hypot(x - centerX, y - centerY)

        if (distance <= hitRadius && distance < targetDistance) {
            targetIndex = index
            targetDistance = distance
        }
    })

    if (targetIndex >= 0) {
        handleTargetTouch(targetIndex)
    }
}

onMounted(() => {
    scheduleRandomTargets()
})

watch(() => props.isGameEnded, (isEnded) => {
    if (!isEnded) {
        scheduleRandomTargets()
        return
    }

    clearSpawnTimer()
    for (const item of list.value) {
        resetTarget(item.id)
    }
})

onBeforeUnmount(() => {
    clearSpawnTimer()

    for (const item of list.value) {
        clearTargetTimer(item)
    }
})
</script>

<template>
    <div class="h5-page 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-hydt"></div>
        <div class="rank-content">
            <div>
                实时得分
            </div>
            <div>
                {{ tick }}
            </div>
            <!-- <div class="txt-left">
                <div>排名</div>
                <div class="txt-bold"></div>
            </div>
            <div class="txt-right">
                <div>实时得分</div>
                <div class="txt-bold">{{ tick }}</div>
            </div> -->
        </div>

        <div ref="moleContainerRef" class="mole-container" @click="handleBoardTouch">
            <div v-for="(item, index) in list" :key="item.id">
                <div class="img-land"></div>
                <span v-if="item.kind" class="img-mole" :class="[`is-${item.kind}`, `is-${item.state}`]" :style="{
                    '--show-duration': `${item.showMs}ms`,
                    '--hide-duration': `${item.hideMs}ms`,
                    '--hit-duration': `${item.hitMs}ms`,
                }"></span>
            </div>
        </div>
        <div class="img-hammer"></div>
        <!-- <div class="play-content">
            <div>当前击鼓次数</div>
            <div>{{ tick }}</div>
            <div>当前排名: &nbsp;第&nbsp;<label></label>&nbsp;名</div>
        </div> -->
        <!-- <div class="bg-bottom"></div> -->
        <!-- <div class="play-gu" :style="{ background: `${currentGu} center/cover` }" @click="$emit('touch')"></div> -->
    </div>
</template>

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

.img-hydt {
    position: absolute;
    width: 362px;
    height: 104px;
    top: 250px;
    left: 50%;
    transform: translate(-50%, -50%);
    background: v-bind('imageUrls.hydt') center/cover;
}

.rank-content {
    width: 600px;
    height: 140px;
    border: 1px solid #A10A06;
    border-radius: 25px;
    background: #F7DA81;
    /* background: v-bind('imageUrls.rank') center/cover; */
    position: absolute;
    left: 50%;
    top: 480px;
    display: flex;
    /* align-items: center;
    justify-content: space-between; */
    transform: translate(-50%, -50%);
    font-size: 44px;
    color: #A10A06;

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

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

.img-hammer {
    width: 148px;
    height: 148px;
    background: v-bind('imageUrls.hammer') center/cover;
    position: absolute;
    left: 50%;
    bottom: 100px;
    transform: translate(-50%, 0);
}

.mole-container {
    position: absolute;
    width: 750px;
    height: 600px;
    row-gap: 70px;
    top: 560px;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(3, 1fr);
    place-items: center;

    >div {
        position: relative;
        display: flex;
        width: 100%;
        height: 100%;
        align-items: center;
        justify-content: center;
    }
}

.img-land,
.img-mole {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 150px;
    height: 150px;
    transform: translate(-50%, -50%) scale(3);
    transform-origin: center;
}

.img-mole {
    display: block;
    pointer-events: none;
}

.img-land,
.img-mole {
    background-position: 0 0;
    background-repeat: no-repeat;
    background-size: auto 150px;
}

.img-land {
    background-image: v-bind('imageUrls.land');
    pointer-events: none;
}

.img-mole.is-mole.is-show {
    background-image: v-bind('imageUrls.moleShowSprite');
    animation: sprite-show var(--show-duration, 650ms) steps(31) forwards;
}

.img-mole.is-mole.is-hide {
    background-image: v-bind('imageUrls.moleHideSprite');
    animation: sprite-hide var(--hide-duration, 350ms) steps(16) forwards;
}

.img-mole.is-mole.is-hit {
    background-image: v-bind('imageUrls.moleHideprite');
    animation: sprite-mole-hit var(--hit-duration, 740ms) steps(37) forwards;
}

.img-mole.is-rabbit.is-show {
    background-image: v-bind('imageUrls.rabbitshowprite');
    animation: sprite-show var(--show-duration, 650ms) steps(31) forwards;
}

.img-mole.is-rabbit.is-hide {
    background-image: v-bind('imageUrls.rabbitHideprite');
    animation: sprite-hide var(--hide-duration, 350ms) steps(16) forwards;
}

.img-mole.is-rabbit.is-hit {
    background-image: v-bind('imageUrls.rabbitFaintprite');
    animation: sprite-rabbit-hit var(--hit-duration, 660ms) steps(33) forwards;
}

@keyframes sprite-show {
    from {
        background-position: 0 0;
    }

    to {
        background-position: -4650px 0;
    }
}

@keyframes sprite-hide {
    from {
        background-position: 0 0;
    }

    to {
        background-position: -2400px 0;
    }
}

@keyframes sprite-mole-hit {
    from {
        background-position: 0 0;
    }

    to {
        background-position: -5550px 0;
    }
}

@keyframes sprite-rabbit-hit {
    from {
        background-position: 0 0;
    }

    to {
        background-position: -4950px 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);
}

.bg-bottom {
    position: absolute;
    bottom: 0;
    left: var(--stage-viewport-left, 0);
    width: var(--stage-viewport-width, 750px);
    height: 479px;
    background: v-bind('imageUrls.bg2') center/cover;
    opacity: 0.6;
}

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

.play-content {
    margin-top: 240px;
    margin-left: 40px;
    margin-right: 40px;
    padding: 30px 0;
    border-radius: 25px;
    background: rgba(40, 12, 8, 0.52);
    color: white;
    font-size: 28pt;
    text-align: center;
}

.play-content>div:nth-child(2) {
    display: inline-block;
    margin: 10px 0;
    background: linear-gradient(180deg, #FFE68D 0%, #FAD500 100%);
    background-clip: text;
    color: transparent;
    font-size: 48pt;
    font-weight: bold;
    transform: scaleY(1.2);
    transform-origin: center;
    -webkit-background-clip: text;
}

.play-content>div:nth-child(3) {
    position: relative;
    top: -10px;
}

.play-content>div:nth-child(3) label {
    color: white;
    font-size: 42pt;
}

.play-gu {
    position: absolute;
    bottom: 350px;
    left: 50%;
    width: 400px;
    height: 400px;
    transform: translateX(-50%) scale(2.4);
}
</style>
