<template lang="pug">
  .svg-container(
    :class="{ 'canvas-mode': mode === 'animate', 'hero-mode': mode === 'hero' }"
  )
    svg.kanji-svg(
      v-show="!loading"
      viewBox="0 0 109 109"
    )
      g(v-if="mode === 'animate'")
        path.stroke-ghost(
          v-for="(stroke, i) in strokes"
          :key="'ghost-'+i"
          :d="stroke.d"
        )

      g(v-for="(stroke, i) in strokes" :key="i")
        path.stroke-path(
          :d="stroke.d"
          :class="getStrokeClass(i)"
          :style="getStrokeStyle(stroke)"
        )

      g(v-if="mode === 'animate' && (!isPlaying || currentStrokeIdx > -1)")
        g(v-for="(stroke, i) in strokes" :key="'anno-'+i")
          g(v-show="!isPlaying || currentStrokeIdx >= i")
             path.stroke-arrow-line(
               v-if="isPlaying && currentStrokeIdx === i && stroke.arrow"
               d="M -7 0 L 2 0 M -1 -3 L 2 0 L -1 3"
               :transform="getArrowTransform(stroke.arrow)"
             )

             g.stroke-badge-group(
               v-if="stroke.start"
               :transform="`translate(${stroke.start.x}, ${stroke.start.y})`"
             )
               circle.stroke-badge-bg(r="4")
               text.stroke-badge-text(
                 dy="0.5"
               ) {{ i + 1 }}

    button.play-btn(
      v-if="mode === 'animate' && !loading && !isPlaying"
      @click.stop="playAnimation"
    )
      svg(viewBox="0 0 24 24" fill="currentColor")
        path(d="M8,5.14V19.14L19,12.14L8,5.14Z")
</template>

<script setup>
/* eslint-disable no-unused-vars */

import { ref, watch, onMounted } from 'vue';

const props = defineProps({
  char: { type: String, required: true },
  mode: {
    type: String,
    default: 'animate',
    validator: (v) => ['hero', 'animate'].includes(v),
  },
  svgContent: { type: String, default: '' },
});

const strokes = ref([]);
const loading = ref(true);
const isPlaying = ref(false);
const currentStrokeIdx = ref(-1);

async function loadData(char) {
  loading.value = true;
  isPlaying.value = false;
  strokes.value = [];
  const hex = char.charCodeAt(0).toString(16).padStart(5, '0');

  try {
    let txt = props.svgContent;

    if (!txt) {
      const baseUrl = 'https://raw.githubusercontent.com/KanjiVG/kanjivg/master/kanji';
      const res = await fetch(`${baseUrl}/${hex}.svg`);
      txt = await res.text();
    }

    const parser = new DOMParser();
    const doc = parser.parseFromString(txt, 'image/svg+xml');
    const rawPaths = Array.from(doc.getElementsByTagName('path')).map((p) => p.getAttribute('d'));

    strokes.value = rawPaths.map((d) => {
      const data = {
        d, start: null, arrow: null, len: 0, duration: 0,
      };
      const tempPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
      tempPath.setAttribute('d', d);
      try { data.len = tempPath.getTotalLength(); } catch (e) { data.len = 100; }
      data.duration = Math.floor(data.len * 20);

      const startMatch = d.match(/[Mm]\s*([\d.]+)[,\s]([\d.]+)/);
      if (startMatch) data.start = { x: parseFloat(startMatch[1]), y: parseFloat(startMatch[2]) };

      try {
        const mid = tempPath.getPointAtLength(data.len / 2);
        const prev = tempPath.getPointAtLength(Math.max(0, (data.len / 2) - 1));
        const angle = Math.atan2(mid.y - prev.y, mid.x - prev.x) * (180 / Math.PI);
        data.arrow = { x: mid.x, y: mid.y, angle };
      } catch (e) { console.error(e); }
      return data;
    });
  } catch (e) { console.error(e); } finally { loading.value = false; }
}

function getArrowTransform(arrow) {
  if (!arrow) return '';
  return `translate(${arrow.x}, ${arrow.y}) rotate(${arrow.angle})`;
}

function getStrokeClass(i) {
  if (props.mode === 'hero') return 'drawn';

  if (isPlaying.value) {
    if (currentStrokeIdx.value === i) return 'animating';
    if (currentStrokeIdx.value > i) return 'drawn';
    return 'hidden';
  }
  return 'drawn';
}

function getStrokeStyle(stroke) {
  if (props.mode === 'hero') return {};
  return { '--len': stroke.len, '--duration': `${stroke.duration}ms` };
}

async function playAnimation() {
  if (isPlaying.value) return;
  isPlaying.value = true;
  currentStrokeIdx.value = -1;

  await new Promise((r) => { setTimeout(r, 200); });

  for (let i = 0; i < strokes.value.length; i += 1) {
    currentStrokeIdx.value = i;
    // eslint-disable-next-line no-await-in-loop
    await new Promise((r) => { setTimeout(r, strokes.value[i].duration); });
    // eslint-disable-next-line no-await-in-loop
    await new Promise((r) => { setTimeout(r, 100); });
  }

  await new Promise((r) => { setTimeout(r, 500); });
  isPlaying.value = false;
}

defineExpose({ playAnimation });
onMounted(() => { if (props.char) loadData(props.char); });
watch(() => props.char, (n) => { if (n) loadData(n); });
</script>
