<script lang="ts">
  import { onMount } from 'svelte'

  // 基础定义部分
  type WriterStage = { text: string; editing: string[] } | string
  type WriterWord = { stages: WriterStage[] }

  // 辅助函数
  const typeCharacters = (base: string, chars: string) => [
    base,
    ...chars.split('').map((_, i) => base + chars.slice(0, i + 1)),
  ]

  const words: WriterWord[] = [
    {
      stages: [
        { text: '', editing: ['x', 'xi', 'xih', "xi'hr"] },
        { text: '喜欢', editing: ['y', 'yb', 'ybl'] },
        ...typeCharacters('喜欢音', 'MAD'),
      ],
    },
    {
      stages: [
        { text: '', editing: ['x', 'xi', 'xih', "xi'hr"] },
        ...typeCharacters('喜欢', 'YTPMV'),
      ],
    },
    {
      stages: [
        { text: '', editing: ['x', 'xi', 'xih', "xi'hr"] },
        { text: '喜欢', editing: ['u', 'ui', 'uij', "ui'jt"] },
        { text: '喜欢视觉', editing: ['y', 'yi', 'yiu', "yi'uu"] },
        '喜欢视觉艺术',
      ],
    },
    {
      stages: [
        { text: '', editing: ['x', 'xt', 'xtx', "xt'xi"] },
        { text: '学习', editing: ['q', 'qm', 'qmd', "qm'dr"] },
        { text: '学习前端', editing: ['k', 'kd', 'kdf', "kd'fa"] },
        '学习前端开发',
      ],
    },
    {
      stages: [
        { text: '', editing: ['x', 'xt', 'xtx', "xt'xi"] },
        { text: '学习', editing: ['h', 'hz', "hz'd", "hz'dr"] },
        { text: '学习后端', editing: ['k', 'kd', 'kdf', "kd'fa"] },
        '学习后端开发',
      ],
    },
    {
      stages: [
        { text: '', editing: ['x', 'xt', 'xtx', "xt'xi"] },
        {
          text: '学习',
          editing: ['q', 'qm', 'qmr', "qm'ru", "qm'ru'u", "qm'ru'ui"],
        },
        '学习嵌入式',
      ],
    },
    {
      stages: [
        { text: '', editing: ['x', 'xt', 'xtx', "xt'xi"] },
        ...typeCharacters('学习', 'DevOps'),
      ],
    },
    {
      stages: [
        { text: '', editing: ['z', 'zr', "zr'y", "zr'yj"] },
        { text: '钻研', editing: ['h', 'hy', 'hyd'] },
        { text: '钻研混', editing: ['m', 'mu'] },
        '钻研混母',
      ],
    },
    {
      stages: [
        { text: '', editing: ['z', 'zr', "zr'y", "zr'yj"] },
        { text: '钻研', editing: ['u', 'uz', "uz'g", "uz'gs"] },
        { text: '钻研手工', editing: ['k', 'kd', 'kdf', "kd'fa"] },
        '钻研手工开发',
      ],
    },
    {
      stages: [
        { text: '', editing: ['b', 'bw', "bw'v", "bw'vj"] },
        { text: '备战', editing: ['k', 'kc', "kc'y", "kc'yj"] },
        '备战考研',
      ],
    },
  ]

  // 状态机定义
  type StatusMachine = {
    wipWord: number
    wipStage: number
    wipEditing: number
    countdown: number
  }
  let status = $state<StatusMachine>({
    wipWord: 0,
    wipStage: 0,
    wipEditing: 0,
    countdown: 0,
  })

  // --- 显示 ---
  /** 当前正在处理的词语 */
  let wipWord = $derived(words[status.wipWord])

  /** 当前正在处理的词语的阶段。如果是最后一个阶段（也就是删除阶段），则取最后
   * 一个元素。
   */
  let wipStage = $derived(
    wipWord.stages[Math.min(status.wipStage, wipWord.stages.length - 1)],
  )

  /** 当前的文本（未经过裁剪的，不含输入法未上屏部分） */
  let currentText = $derived(
    typeof wipStage == 'string' ? wipStage : wipStage.text,
  )

  /** 如果在最后一个阶段，则根据进度去裁剪文本 */
  let currentTextSliced = $derived(
    status.wipStage >= wipWord.stages.length
      ? currentText.slice(0, currentText.length - status.wipEditing)
      : currentText,
  )

  /** 输入法待上屏区域 */
  let currentEditing = $derived(
    typeof wipStage == 'string' || status.wipEditing <= 0
      ? ''
      : wipStage.editing[status.wipEditing - 1],
  )

  // 主要的状态更新逻辑
  const DELAY_STEP_WORD = 250
  const DELAY_STEP_STAGE = 80
  const DELAY_STEP_EDIT = 80
  const DELAY_STEP_DELETE = 50
  const DELAY_DISPLAY_HOLD = 3000

  // 调试用，如果发现动画有问题，可以在这里调慢动画
  const DELAY_GLOBAL_FACTOR = 1

  function step(status: StatusMachine, dt: number): StatusMachine {
    if (status.countdown > 0) {
      return {
        ...status,
        countdown: status.countdown - dt / DELAY_GLOBAL_FACTOR,
      }
    }

    if (typeof wipStage == 'object') {
      if (status.wipEditing < wipStage.editing.length) {
        return {
          ...status,
          wipEditing: status.wipEditing + 1,
          countdown: status.countdown + DELAY_STEP_EDIT,
        }
      }
    } else if (status.wipStage >= wipWord.stages.length) {
      // 此时执行删除操作
      if (status.wipEditing < currentText.length) {
        return {
          ...status,
          wipEditing: status.wipEditing + 1,
          countdown: status.countdown + DELAY_STEP_DELETE,
        }
      }
    }

    if (status.wipStage < wipWord.stages.length) {
      return {
        ...status,
        wipStage: status.wipStage + 1,
        wipEditing: 0,
        countdown:
          status.wipStage == wipWord.stages.length - 1
            ? DELAY_DISPLAY_HOLD
            : DELAY_STEP_STAGE,
      }
    }

    return {
      ...status,
      wipWord: (status.wipWord + 1) % words.length,
      wipStage: 0,
      wipEditing: 0,
      countdown: DELAY_STEP_WORD,
    }
  }

  // 时间循环
  onMount(() => {
    let enabled = true
    let lastTime = Date.now()

    function _step() {
      if (!enabled) return

      let currentTime = Date.now()

      status = step(status, currentTime - lastTime)
      lastTime = currentTime

      requestAnimationFrame(_step)
    }

    _step()

    return () => {
      enabled = false
    }
  })
</script>

<span class="typewriter"
  ><span style="user-select: none;">&#xFEFF;</span><span class="text"
    >{currentTextSliced}</span
  ><span class="edit">{currentEditing}</span></span
>

<style>
  @keyframes blink {
    from,
    to {
      opacity: 1;
    }
    50% {
      opacity: 0;
    }
  }

  .typewriter {
    display: inline-block;
    position: relative;
    background-color: var(--color-bg-1);
    padding: 0.25rem 0.5rem;
    margin: 0 0.25rem;
    border-radius: 0.5rem;

    & > .edit {
      text-decoration: underline;
      /* font-family: var(--font-mono); */
    }

    &::after {
      --gap-ud: 0.5rem;

      content: '';
      position: absolute;
      inset-inline-end: 0.5rem;
      inset-block-start: var(--gap-ud);
      block-size: calc(100% - var(--gap-ud) * 2);
      inline-size: 2px;
      background-color: var(--color-fg-0);
      animation: blink 1s step-end infinite;
    }
  }
</style>
