<script setup>
import { ref, watch, onMounted, onBeforeUnmount, computed } from "vue";
import PointSwitcher from "@/components/PointSwitcher.vue";

import { MESSAGETYPES, MESSAGETYPESV2 } from "@/constants";
import { simulShootAPI } from "@/apis";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user, device } = storeToRefs(store);

const props = defineProps({
  currentRound: {
    type: Number,
    default: 0,
  },
  totalRound: {
    type: Number,
    default: 0,
  },
  scores: {
    type: Array,
    default: () => [],
  },
  blueScores: {
    type: Array,
    default: () => [],
  },
  mode: {
    type: String,
    default: "solo", // solo 单排，team 双排
  },
  stop: {
    type: Boolean,
    default: false,
  },
  targetRadius: {
    type: Number,
    default: 20,
  },
  hitRadiusPx: {
    type: Number,
    default: 2,
  },
  zoomHitRadiusPx: {
    type: Number,
    default: 5,
  },
});

const pMode = ref(true);
const latestOne = ref(null);
const bluelatestOne = ref(null);
const prevScores = ref([]);
const prevBlueScores = ref([]);
const timer = ref(null);
const dirTimer = ref(null);
const angle = ref(null);
const circleColor = ref("");
const ROUND_TIP_OFFSET_Y = -32;
const EXPERIENCE_TIP_OFFSET_Y = -68;

watch(
  () => props.scores,
  (newVal) => {
    if (newVal.length - prevScores.value.length === 1) {
      latestOne.value = newVal[newVal.length - 1];
      if (timer.value) clearTimeout(timer.value);
      timer.value = setTimeout(() => {
        latestOne.value = null;
      }, 1000);
    }
    prevScores.value = [...newVal];
  },
  {
    deep: true,
  }
);

watch(
  () => props.blueScores,
  (newVal) => {
    if (newVal.length - prevBlueScores.value.length === 1) {
      bluelatestOne.value = newVal[newVal.length - 1];
      if (timer.value) clearTimeout(timer.value);
      timer.value = setTimeout(() => {
        bluelatestOne.value = null;
      }, 1000);
    }
    prevBlueScores.value = [...newVal];
  },
  {
    deep: true,
  }
);

const safeTargetRadius = computed(() => {
  const radius = Number(props.targetRadius);
  return Number.isFinite(radius) && radius > 0 ? radius : 20;
});

const currentHitRadiusPx = computed(() => {
  const radius = Number(
    pMode.value ? props.zoomHitRadiusPx : props.hitRadiusPx
  );
  return Number.isFinite(radius) && radius >= 0 ? radius : 0;
});

function getShotPoint(shot, fallbackCenter = false) {
  const x = Number(shot?.x);
  const y = Number(shot?.y);
  if (Number.isFinite(x) && Number.isFinite(y)) return { x, y };
  return fallbackCenter ? { x: 0, y: 0 } : null;
}

function getPointDirection(point) {
  if (!point) return null;
  const distance = Math.sqrt(point.x * point.x + point.y * point.y);
  if (distance === 0) return null;

  return {
    x: point.x / distance,
    y: point.y / distance,
  };
}

function formatPxOffset(value) {
  if (!value) return "";
  const operator = value > 0 ? "+" : "-";
  return ` ${operator} ${Math.abs(value)}px`;
}

function formatTargetPosition(percent, offset) {
  const pxOffset = formatPxOffset(offset);
  return pxOffset ? `calc(${percent}%${pxOffset})` : `${percent}%`;
}

function getTargetPositionStyle(point, offsetPx = 0, extraOffset = {}) {
  if (!point) return { display: "none" };

  const radius = safeTargetRadius.value;
  const diameter = radius * 2;
  const direction = getPointDirection(point);
  const xOffset = (direction ? direction.x * offsetPx : 0) + (extraOffset.x || 0);
  const yOffset = (direction ? -direction.y * offsetPx : 0) + (extraOffset.y || 0);
  const leftPercent = ((point.x + radius) / diameter) * 100;
  const topPercent = ((radius - point.y) / diameter) * 100;

  return {
    left: formatTargetPosition(leftPercent, xOffset),
    top: formatTargetPosition(topPercent, yOffset),
    transform: "translate(-50%, -50%)",
  };
}

function getHitStyle(shot) {
  const radius = currentHitRadiusPx.value;
  const point = getShotPoint(shot);

  return {
    ...getTargetPositionStyle(point, radius),
    width: `${radius * 2}px`,
    height: `${radius * 2}px`,
  };
}

function getRoundTipStyle(shot) {
  const point = getShotPoint(shot, true);
  return getTargetPositionStyle(
    point,
    shot?.ring ? currentHitRadiusPx.value : 0,
    { y: ROUND_TIP_OFFSET_Y }
  );
}

function getExperienceTipStyle(shot) {
  const point = getShotPoint(shot, true);
  return getTargetPositionStyle(
    point,
    shot?.ring ? currentHitRadiusPx.value : 0,
    { y: EXPERIENCE_TIP_OFFSET_Y }
  );
}
const simulShoot = async () => {
  if (device.value.deviceId) await simulShootAPI(device.value.deviceId);
};
const simulShoot2 = async () => {
  if (device.value.deviceId) {
    const r1 = Math.random() > 0.5 ? 0.01 : 0.02;
    await simulShootAPI(device.value.deviceId, r1, r1);
  }
};

const env = computed(() => {
  const accountInfo = uni.getAccountInfoSync();
  return accountInfo.miniProgram.envVersion;
});

const arrowStyle = computed(() => {
  return {
    transform: `rotateX(180deg) translate(-50%, -50%) rotate(${
      360 - angle.value
    }deg) translateY(105%)`,
  };
});

async function onReceiveMessage(message) {
  if (Array.isArray(message)) return;
  if (message.type === MESSAGETYPESV2.ShootResult && message.shootData) {
    if (
      message.shootData.playerId === user.value.id &&
      !message.shootData.ring &&
      message.shootData.angle >= 0
    ) {
      angle.value = null;
      setTimeout(() => {
        if (props.scores[0]) {
          circleColor.value =
            message.shootData.playerId === props.scores[0].playerId
              ? "#ff4444"
              : "#1840FF";
        }
        angle.value = message.shootData.angle;
      }, 200);
    }
  }
}

onMounted(() => {
  uni.$on("socket-inbox", onReceiveMessage);
});

onBeforeUnmount(() => {
  if (timer.value) {
    clearTimeout(timer.value);
    timer.value = null;
  }
  if (dirTimer.value) {
    clearTimeout(dirTimer.value);
    dirTimer.value = null;
  }
  uni.$off("socket-inbox", onReceiveMessage);
});
</script>

<template>
  <view class="container">
    <view class="header" v-if="totalRound > 0">
      <text v-if="totalRound > 0" class="round-count">{{
        (currentRound > totalRound ? totalRound : currentRound) +
        "/" +
        totalRound
      }}</text>
    </view>
    <view class="target">
      <view v-if="angle !== null" class="arrow-dir" :style="arrowStyle">
        <view :style="{ background: circleColor }">
          <image src="../static/dot-circle.png" mode="widthFix" />
        </view>
      </view>
      <view v-if="stop" class="stop-sign">中场休息</view>
      <view
        v-if="latestOne && latestOne.ring && user.id === latestOne.playerId"
        class="e-value fade-in-out"
        :style="getExperienceTipStyle(latestOne)"
      >
        经验 +1
      </view>
      <view
        v-if="latestOne"
        class="round-tip fade-in-out"
        :style="getRoundTipStyle(latestOne)"
        >{{ latestOne.ringX ? "X" : latestOne.ring || "未上靶"
        }}<text v-if="latestOne.ring">环</text>
      </view>
      <view
        v-if="
          bluelatestOne &&
          bluelatestOne.ring &&
          user.id === bluelatestOne.playerId
        "
        class="e-value fade-in-out"
        :style="getExperienceTipStyle(bluelatestOne)"
      >
        经验 +1
      </view>
      <view
        v-if="bluelatestOne"
        class="round-tip fade-in-out"
        :style="getRoundTipStyle(bluelatestOne)"
        >{{ bluelatestOne.ringX ? "X" : bluelatestOne.ring || "未上靶"
        }}<text v-if="bluelatestOne.ring">环</text></view
      >
      <block v-for="(bow, index) in scores" :key="index">
        <view
          v-if="bow.ring > 0"
          :class="`hit ${pMode ? 'b' : 's'}-point ${
            index === scores.length - 1 && latestOne ? 'pump-in' : ''
          }`"
          :style="{
            ...getHitStyle(bow),
            backgroundColor: mode === 'solo' ? '#00bf04' : '#FF0000',
          }"
          ><text v-if="pMode">{{ index + 1 }}</text></view
        >
      </block>
      <block v-for="(bow, index) in blueScores" :key="index">
        <view
          v-if="bow.ring > 0"
          :class="`hit ${pMode ? 'b' : 's'}-point ${
            index === blueScores.length - 1 && bluelatestOne ? 'pump-in' : ''
          }`"
          :style="{
            ...getHitStyle(bow),
            backgroundColor: '#1840FF',
          }"
        >
          <text v-if="pMode">{{ index + 1 }}</text>
        </view>
      </block>
      <image src="../static/bow-target.png" mode="widthFix" />
    </view>
    <view class="footer">
      <PointSwitcher
        :onChange="(val) => (pMode = val)"
        :style="{ zIndex: 999 }"
      />
    </view>
    <view class="simul" v-if="env !== 'release'">
      <button @click="simulShoot">模拟</button>
      <button @click="simulShoot2">射箭</button>
    </view>
  </view>
</template>

<style scoped>
.container {
  width: calc(100vw - 30px);
  height: calc(100vw - 30px);
  padding: 0px 15px;
  position: relative;
}
.target {
  position: relative;
  margin: 10px;
  width: calc(100% - 20px);
  height: calc(100% - 20px);
  z-index: -1;
}
.e-value {
  position: absolute;
  background-color: #0006;
  color: #fff;
  font-size: 12px;
  padding: 4px 7px;
  border-radius: 5px;
  z-index: 2;
  width: 50px;
  text-align: center;
}
.round-tip {
  position: absolute;
  color: #fff;
  font-size: 30px;
  font-weight: bold;
  z-index: 2;
  width: 100px;
  text-align: center;
}
.round-tip > text {
  font-size: 24px;
  margin-left: 5px;
}
@keyframes target-tip-fade-in-out {
  0% {
    transform: translate(-50%, -50%) translateY(20px);
    opacity: 0;
  }

  30% {
    transform: translate(-50%, -50%);
    opacity: 1;
  }

  80% {
    transform: translate(-50%, -50%);
    opacity: 1;
  }

  100% {
    transform: translate(-50%, -50%);
    opacity: 0;
  }
}
.round-tip.fade-in-out,
.e-value.fade-in-out {
  animation: target-tip-fade-in-out 1.2s ease forwards;
}
.target > image:last-child {
  width: 100%;
  height: 100%;
}
.hit {
  position: absolute;
  border-radius: 50%;
  z-index: 1;
  color: #fff;
  transition: all 0.3s ease;
  box-sizing: border-box;
}
.b-point {
  border: 1px solid #fff;
  z-index: 1;
  display: flex;
  justify-content: center;
  align-items: center;
}
.b-point > text {
  font-size: 16rpx;
  color: #fff;
  font-family: "DINCondensed";
  /* text-align: center;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);*/
  margin-top: 2rpx;
}
@keyframes target-pump-in {
  from {
    transform: translate(-50%, -50%) scale(2);
  }

  to {
    transform: translate(-50%, -50%) scale(1);
  }
}
.hit.pump-in {
  animation: target-pump-in 0.3s ease-out forwards;
  transform-origin: center center;
}
.header {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: -40px;
}
.header > image:first-child {
  width: 40px;
  height: 40px;
}
.round-count {
  font-size: 20px;
  color: #fed847;
  top: 75px;
  font-weight: bold;
}
.footer {
  width: calc(100% - 20px);
  padding: 0 10px;
  display: flex;
  margin-top: -40px;
  justify-content: flex-end;
}
.footer > image {
  width: 40px;
  min-height: 40px;
  max-height: 40px;
  border-radius: 50%;
  border: 1px solid #fff;
}
.simul {
  position: absolute;
  top: 0;
  right: 20px;
  margin-left: 20px;
  z-index: 999;
}
.simul > button {
  color: #fff;
}
.stop-sign {
  position: absolute;
  font-size: 44px;
  color: #fff9;
  text-align: center;
  width: 200px;
  height: 60px;
  left: calc(50% - 100px);
  top: calc(50% - 30px);
  z-index: 99;
  font-weight: bold;
}
.arrow-dir {
  position: absolute;
  width: 100%;
  height: 52%;
  left: 50%;
  bottom: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.arrow-dir > view {
  width: 40rpx;
  height: 40rpx;
  border-radius: 50%;
}
.arrow-dir > view > image {
  width: 100rpx;
  height: 100rpx;
  transform: translate(-30%, -30%);
}
@keyframes spring-in {
  0% {
    transform: scale(2);
    opacity: 0.4;
  }
  15% {
    transform: scale(3);
    opacity: 1;
  }
  30% {
    transform: scale(2);
    opacity: 0.4;
  }
  45% {
    transform: scale(3);
    opacity: 1;
  }
  60% {
    transform: scale(2);
    opacity: 0.4;
  }
  75% {
    transform: scale(3);
    opacity: 1;
  }
  100% {
    transform: scale(1);
    opacity: 0;
  }
}
@keyframes disappear {
  0% {
    opacity: 1;
  }
  75% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}
.arrow-dir > view {
  animation: disappear 3s ease forwards;
}
.arrow-dir > view > image {
  animation: spring-in 3s ease forwards;
  width: 100%;
}
</style>
