<template>
  <div class="complexity-demo">
    <header class="demo-header">
      <div class="demo-icon">📊</div>
      <div>
        <h1 class="demo-title">时间复杂度动态演示</h1>
        <p class="demo-subtitle">通过动画和计数直观理解不同算法的时间复杂度</p>
      </div>
    </header>

    <!-- 控制面板 -->
    <section class="ctrl-section">
      <div class="ctrl-row">
        <label class="ctrl-label">输入规模 n：</label>
        <input type="range" min="1" max="100" v-model.number="n" class="n-slider" />
        <span class="n-value">{{ n }}</span>
      </div>
      <div class="ctrl-row">
        <label class="ctrl-label">选择算法：</label>
        <select v-model="selectedAlgo" class="algo-select">
          <option v-for="a in algorithms" :key="a.id" :value="a.id">
            {{ a.label }} — {{ a.complexity }}
          </option>
        </select>
      </div>
      <div class="ctrl-actions">
        <button class="ctrl-btn" @click="run" :disabled="running">▶ 运行演示</button>
        <button class="ctrl-btn" @click="stepForward" :disabled="running || stepIndex >= totalSteps - 1">下一步</button>
        <button class="ctrl-btn" @click="resetDemo">⟳ 重置</button>
      </div>
    </section>

    <!-- 算法说明 -->
    <section class="info-section" v-if="currentAlgo">
      <div class="algo-info-card">
        <h3>{{ currentAlgo.label }}</h3>
        <div class="complexity-badges">
          <span class="badge time-badge">时间复杂度：{{ currentAlgo.complexity }}</span>
          <span class="badge space-badge">空间复杂度：{{ currentAlgo.space }}</span>
        </div>
        <p>{{ currentAlgo.description }}</p>
        <div class="op-count" v-if="opCount !== null">
          操作次数：<strong>{{ opCount }}</strong>
          <span class="op-formula">≈ {{ currentAlgo.formula.replace('n', n) }}</span>
        </div>
      </div>
    </section>

    <!-- 动态演示区域 -->
    <section class="demo-section" v-if="currentAlgo">
      <div class="visualization-area">
        <!-- 数组可视化 -->
        <div class="array-display" v-if="arrayData.length">
          <div class="array-label">数据状态：</div>
          <div class="array-bars">
            <div
              v-for="(val, idx) in arrayData"
              :key="idx"
              class="array-bar-wrapper"
              :style="{ height: barHeight(val) + 'px' }"
            >
              <div
                class="array-bar"
                :class="barClass(idx)"
                :style="{ height: '100%' }"
              >
                <span class="bar-val">{{ val }}</span>
              </div>
            </div>
          </div>
        </div>

        <!-- 日志/步骤说明 -->
        <div class="step-log" v-if="steps.length">
          <div class="log-header">📝 执行步骤</div>
          <div class="log-body" ref="logRef">
            <div
              v-for="(step, idx) in visibleSteps"
              :key="idx"
              class="log-line"
              :class="{ current: idx === stepIndex, done: idx < stepIndex }"
            >
              <span class="log-step-num">{{ idx + 1 }}</span>
              <span class="log-text">{{ step }}</span>
            </div>
          </div>
        </div>

        <!-- 复杂度对比图表 -->
        <div class="chart-section" v-if="showChart">
          <div class="chart-header">📈 复杂度增长对比</div>
          <div class="chart-canvas">
            <div class="chart-row" v-for="item in chartData" :key="item.label">
              <div class="chart-label">{{ item.label }}</div>
              <div class="chart-bar-track">
                <div
                  class="chart-bar"
                  :style="{ width: item.percent + '%', backgroundColor: item.color }"
                >
                  <span class="chart-bar-text" v-if="item.percent > 15">{{ item.count }}</span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>

    <div class="nav-back">
      <router-link to="/chapter/ch1" class="back-link">← 返回第一章</router-link>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, watch } from 'vue'

const n = ref(10)
const selectedAlgo = ref('linear')
const running = ref(false)
const opCount = ref(null)
const steps = ref([])
const stepIndex = ref(-1)
const arrayData = ref([])
const highlights = ref({})
const showChart = ref(false)
const logRef = ref(null)

const algorithms = [
  {
    id: 'constant',
    label: '常数阶 — 数组随机访问',
    complexity: 'O(1)',
    space: 'O(1)',
    formula: '1',
    description: '直接通过索引访问数组元素，无论数组多大，执行时间恒定。这是最优的时间复杂度。'
  },
  {
    id: 'logarithmic',
    label: '对数阶 — 二分查找',
    complexity: 'O(log n)',
    space: 'O(1)',
    formula: '⌈log₂(n)⌉ = ',
    description: '每次将搜索范围缩小一半。即使 n 很大，需要的步数也增长很慢。例如 n=1024 时只需 10 步。'
  },
  {
    id: 'linear',
    label: '线性阶 — 线性查找',
    complexity: 'O(n)',
    space: 'O(1)',
    formula: 'n = ',
    description: '顺序遍历所有元素。执行时间与输入规模成正比，是最直观的算法复杂度。'
  },
  {
    id: 'nlogn',
    label: '线性对数阶 — 归并排序分解',
    complexity: 'O(n log n)',
    space: 'O(n)',
    formula: 'n·⌈log₂(n)⌉ = ',
    description: '常见的"分治"算法复杂度。每一层处理 n 个元素，共有 log n 层。'
  },
  {
    id: 'quadratic',
    label: '平方阶 — 冒泡排序',
    complexity: 'O(n²)',
    space: 'O(1)',
    formula: 'n(n-1)/2 = ',
    description: '双重循环嵌套，最常见于简单排序算法。当 n 翻倍时，运行时间变为原来的 4 倍。'
  },
  {
    id: 'exponential',
    label: '指数阶 — 递归斐波那契',
    complexity: 'O(2ⁿ)',
    space: 'O(n)',
    formula: '2ⁿ = ',
    description: '递归树呈指数增长。n 稍大时就会爆炸，因此实用中必须优化（如动态规划）。'
  }
]

const currentAlgo = computed(() => algorithms.find(a => a.id === selectedAlgo.value))

const totalSteps = computed(() => steps.value.length)

const visibleSteps = computed(() => {
  // 最多显示最近 50 步
  if (stepIndex.value < 50) return steps.value.slice(0, Math.max(stepIndex.value + 1, 0))
  return steps.value.slice(stepIndex.value - 49, stepIndex.value + 1)
})

const maxVal = computed(() => {
  if (!arrayData.value.length) return 1
  return Math.max(...arrayData.value, 1)
})

function barHeight(val) {
  return Math.max(20, (val / maxVal.value) * 200)
}

function barClass(idx) {
  const h = highlights.value || {}
  const classes = []
  if (h.sorted?.includes(idx)) classes.push('bar-sorted')
  if (h.comparing?.includes(idx)) classes.push('bar-comparing')
  if (h.current === idx) classes.push('bar-current')
  return classes
}

function resetDemo() {
  running.value = false
  opCount.value = null
  steps.value = []
  stepIndex.value = -1
  arrayData.value = []
  highlights.value = {}
  showChart.value = false
}

function sleep(ms) {
  return new Promise(r => setTimeout(r, ms))
}

async function run() {
  if (running.value) return
  running.value = true
  showChart.value = false
  steps.value = []
  stepIndex.value = -1
  opCount.value = 0
  highlights.value = {}

  const algo = selectedAlgo.value

  switch (algo) {
    case 'constant': await runConstant(); break
    case 'logarithmic': await runLogarithmic(); break
    case 'linear': await runLinear(); break
    case 'nlogn': await runNlogn(); break
    case 'quadratic': await runQuadratic(); break
    case 'exponential': await runExponential(); break
  }

  running.value = false
  showChart.value = true
  scrollToLogBottom()
}

function scrollToLogBottom() {
  setTimeout(() => {
    if (logRef.value) {
      logRef.value.scrollTop = logRef.value.scrollHeight
    }
  }, 50)
}

function addStep(msg) {
  steps.value.push(msg)
  stepIndex.value = steps.value.length - 1
}

// O(1) — 数组随机访问
async function runConstant() {
  const arr = Array.from({ length: n.value }, () => Math.floor(Math.random() * 100) + 1)
  arrayData.value = arr
  addStep(`生成包含 ${n.value} 个元素的数组`)
  await sleep(300)

  const idx = Math.floor(Math.random() * n.value)
  highlights.value = { current: idx }
  addStep(`直接通过索引 arr[${idx}] 访问元素，执行 1 次操作`)
  await sleep(600)

  opCount.value = 1
  addStep(`✅ 无论 n 多大，始终只需 1 次操作 → O(1)`)
}

// O(log n) — 二分查找
async function runLogarithmic() {
  const arr = Array.from({ length: n.value }, (_, i) => i + 1)
  arrayData.value = arr
  const target = Math.floor(Math.random() * n.value) + 1
  addStep(`有序数组 [1..${n.value}]，查找目标值 ${target}`)
  await sleep(400)

  let left = 0, right = n.value - 1
  let count = 0

  while (left <= right) {
    count++
    const mid = Math.floor((left + right) / 2)
    highlights.value = { comparing: [left, right], current: mid }
    addStep(`第 ${count} 次：区间 [${left + 1}, ${right + 1}]，中间索引 ${mid + 1} (值=${arr[mid]})`)
    await sleep(500)

    if (arr[mid] === target) {
      highlights.value = { sorted: [mid], current: mid }
      addStep(`✅ 找到目标 ${target} 在位置 ${mid + 1}，共 ${count} 次比较`)
      opCount.value = count
      return
    } else if (arr[mid] < target) {
      left = mid + 1
      addStep(`   ${arr[mid]} < ${target}，搜右侧`)
    } else {
      right = mid - 1
      addStep(`   ${arr[mid]} > ${target}，搜左侧`)
    }
    await sleep(400)
  }

  addStep(`❌ 未找到，共 ${count} 次比较`)
  opCount.value = count
}

// O(n) — 线性查找
async function runLinear() {
  const arr = Array.from({ length: n.value }, () => Math.floor(Math.random() * 100))
  arrayData.value = arr
  const target = Math.floor(Math.random() * 100)
  addStep(`数组长度=${n.value}，查找目标值 ${target}`)
  await sleep(400)

  let count = 0
  for (let i = 0; i < n.value; i++) {
    count++
    highlights.value = { current: i }
    addStep(`第 ${count} 次：比较 arr[${i}] = ${arr[i]} ${arr[i] === target ? '✅ 找到' : ''}`)
    await sleep(200)
    if (arr[i] === target) {
      highlights.value = { sorted: [i] }
      addStep(`✅ 找到目标 ${target} 在位置 ${i + 1}，共 ${count} 次比较`)
      opCount.value = count
      return
    }
  }
  addStep(`❌ 未找到，共 ${count} 次比较`)
  opCount.value = count
}

// O(n log n) — 归并排序分解过程
async function runNlogn() {
  const size = Math.min(n.value, 32)
  const arr = Array.from({ length: size }, () => Math.floor(Math.random() * 100))
  arrayData.value = arr
  addStep(`数组 [${arr.join(', ')}] (n=${size})`)
  await sleep(400)

  let count = 0

  async function mergeSort(arr, left, right, depth = 0) {
    if (left >= right) return
    const mid = Math.floor((left + right) / 2)
    count++
    highlights.value = { range: [left, right], current: mid }
    addStep(`  分解 [${left + 1}..${right + 1}] → 左[${left + 1}..${mid + 1}] 右[${mid + 2}..${right + 1}]`)
    await sleep(300)

    await mergeSort(arr, left, mid, depth + 1)
    await mergeSort(arr, mid + 1, right, depth + 1)

    // 合并
    const temp = []
    let i = left, j = mid + 1
    while (i <= mid && j <= right) {
      count++
      if (arr[i] <= arr[j]) temp.push(arr[i++])
      else temp.push(arr[j++])
    }
    while (i <= mid) { temp.push(arr[i++]); count++ }
    while (j <= right) { temp.push(arr[j++]); count++ }

    for (let k = left; k <= right; k++) {
      arr[k] = temp[k - left]
      highlights.value = { sorted: Array.from({ length: right + 1 }, (_, i) => i).slice(0, k + 1) }
    }
    addStep(`  合并完成 [${left + 1}..${right + 1}]`)
    await sleep(300)
  }

  await mergeSort(arr, 0, size - 1)
  arrayData.value = [...arr]
  const nlogn = Math.ceil(size * Math.log2(size))
  opCount.value = count
  addStep(`✅ 归并排序完成，总操作约 ${count} 次，理论值 O(n·log₂n) ≈ ${nlogn}`)
}

// O(n²) — 冒泡排序
async function runQuadratic() {
  const size = Math.min(n.value, 20)
  const arr = Array.from({ length: size }, () => Math.floor(Math.random() * 100))
  arrayData.value = arr
  addStep(`初始数组 [${arr.join(', ')}] (n=${size})`)
  await sleep(400)

  let count = 0
  const len = arr.length

  for (let i = 0; i < len - 1; i++) {
    for (let j = 0; j < len - 1 - i; j++) {
      count++
      highlights.value = { comparing: [j, j + 1] }
      addStep(`比较 arr[${j}]=${arr[j]} 和 arr[${j + 1}]=${arr[j + 1]}`)
      await sleep(200)

      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
        highlights.value = { swapping: [j, j + 1] }
        addStep(`  交换 → [${arr.join(', ')}]`)
        await sleep(200)
      }
    }
    highlights.value = { sorted: Array.from({ length: len }, (_, k) => k).slice(len - 1 - i) }
  }

  arrayData.value = [...arr]
  const expected = size * (size - 1) / 2
  opCount.value = count
  addStep(`✅ 冒泡排序完成，共 ${count} 次比较，理论值 n(n-1)/2 = ${expected}`)
}

// O(2ⁿ) — 递归斐波那契
async function runExponential() {
  const size = Math.min(n.value, 10) // 限制，防止浏览器卡死
  addStep(`递归计算 Fibonacci(${size})，展示递归调用树`)
  await sleep(400)

  let count = 0
  let callStack = []

  function fib(k) {
    count++
    if (k <= 1) {
      addStep(`  fib(${k}) = ${k} (基准情况)`)
      return k
    }
    addStep(`  fib(${k}) = fib(${k - 1}) + fib(${k - 2})`)
    const a = fib(k - 1)
    const b = fib(k - 2)
    const result = a + b
    addStep(`  fib(${k}) = ${a} + ${b} = ${result}`)
    return result
  }

  const result = fib(size)
  const expected = Math.pow(2, size)
  opCount.value = count
  addStep(`✅ Fibonacci(${size}) = ${result}，递归调用 ${count} 次，理论 O(2ⁿ) ≈ ${expected}`)
}

// 单步前进
function stepForward() {
  if (stepIndex.value < totalSteps.value - 1) {
    stepIndex.value++
    scrollToLogBottom()
  }
}

// 对比图表数据
const chartData = computed(() => {
  const currentN = n.value
  const items = [
    { label: 'O(1)', count: 1, color: '#22c55e' },
    { label: 'O(log n)', count: Math.ceil(Math.log2(currentN)), color: '#3b82f6' },
    { label: 'O(n)', count: currentN, color: '#f59e0b' },
    { label: 'O(n log n)', count: Math.ceil(currentN * Math.log2(currentN)), color: '#f97316' },
    { label: 'O(n²)', count: currentN * currentN, color: '#ef4444' },
    { label: 'O(2ⁿ)', count: Math.min(Math.pow(2, currentN), 999999), color: '#dc2626' }
  ]
  const maxCount = Math.max(...items.map(i => i.count), 1)
  return items.map(item => ({
    ...item,
    percent: Math.min((item.count / maxCount) * 100, 100)
  }))
})
</script>

<style scoped>
.complexity-demo {
  max-width: 1000px;
  margin: 0 auto;
  padding: 0 24px;
}

.demo-header {
  display: flex;
  gap: 16px;
  align-items: flex-start;
  padding: 28px 0 20px;
  border-bottom: 1px solid var(--border-color);
  margin-bottom: 24px;
}

.demo-icon {
  font-size: 40px;
  flex-shrink: 0;
}

.demo-title {
  font-size: 26px;
  font-weight: 800;
  color: var(--text-primary);
  margin-bottom: 4px;
}

.demo-subtitle {
  font-size: 14px;
  color: var(--text-secondary);
}

/* 控制面板 */
.ctrl-section {
  background: var(--card-bg);
  border: 1px solid var(--border-color);
  border-radius: 12px;
  padding: 20px;
  margin-bottom: 24px;
}

.ctrl-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}

.ctrl-label {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  min-width: 100px;
}

.n-slider {
  flex: 1;
  max-width: 300px;
  accent-color: var(--primary-color);
}

.n-value {
  font-family: var(--mono);
  font-size: 18px;
  font-weight: 700;
  color: var(--primary-color);
  min-width: 40px;
}

.algo-select {
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid var(--border-color);
  background: var(--bg);
  color: var(--text-primary);
  font-size: 14px;
  flex: 1;
  max-width: 400px;
  cursor: pointer;
}

.ctrl-actions {
  display: flex;
  gap: 10px;
}

.ctrl-btn {
  padding: 8px 20px;
  border-radius: 8px;
  border: 1px solid var(--border-color);
  background: var(--bg);
  color: var(--text-primary);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.15s;
}

.ctrl-btn:hover:not(:disabled) {
  background: var(--hover-bg);
  border-color: var(--primary-color);
}

.ctrl-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.ctrl-btn:first-child {
  background: var(--primary-color);
  color: white;
  border-color: var(--primary-color);
}

.ctrl-btn:first-child:hover:not(:disabled) {
  opacity: 0.9;
}

/* 算法说明 */
.info-section {
  margin-bottom: 24px;
}

.algo-info-card {
  background: var(--card-bg);
  border: 1px solid var(--border-color);
  border-radius: 12px;
  padding: 20px;
}

.algo-info-card h3 {
  font-size: 18px;
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: 10px;
}

.complexity-badges {
  display: flex;
  gap: 10px;
  margin-bottom: 12px;
}

.badge {
  font-size: 13px;
  font-weight: 600;
  padding: 4px 12px;
  border-radius: 6px;
}

.time-badge {
  background: #eff6ff;
  color: #2563eb;
}

.space-badge {
  background: #f0fdf4;
  color: #16a34a;
}

.algo-info-card p {
  font-size: 14px;
  color: var(--text-secondary);
  line-height: 1.6;
  margin-bottom: 10px;
}

.op-count {
  font-size: 15px;
  color: var(--text-primary);
  padding: 8px 14px;
  background: var(--hover-bg);
  border-radius: 8px;
}

.op-formula {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--text-tertiary);
  margin-left: 8px;
}

/* 可视化区域 */
.visualization-area {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.array-display {
  background: var(--card-bg);
  border: 1px solid var(--border-color);
  border-radius: 12px;
  padding: 20px;
}

.array-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
  margin-bottom: 12px;
}

.array-bars {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  min-height: 220px;
}

.array-bar-wrapper {
  flex: 1;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  min-width: 12px;
}

.array-bar {
  width: 100%;
  background: var(--primary-color);
  border-radius: 4px 4px 0 0;
  transition: all 0.3s ease;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  min-height: 20px;
}

.bar-val {
  font-size: 10px;
  color: white;
  font-weight: 600;
  padding: 2px 0;
}

.bar-current {
  background: #f59e0b;
}

.bar-comparing {
  background: #60a5fa;
}

.bar-sorted {
  background: #22c55e;
}

/* 步骤日志 */
.step-log {
  background: var(--card-bg);
  border: 1px solid var(--border-color);
  border-radius: 12px;
  overflow: hidden;
}

.log-header {
  padding: 12px 16px;
  font-size: 14px;
  font-weight: 700;
  color: var(--text-primary);
  border-bottom: 1px solid var(--border-color);
  background: var(--hover-bg);
}

.log-body {
  max-height: 300px;
  overflow-y: auto;
  padding: 8px 0;
}

.log-line {
  padding: 5px 16px;
  font-size: 13px;
  font-family: var(--mono);
  color: var(--text-tertiary);
  line-height: 1.5;
  border-left: 3px solid transparent;
}

.log-line.current {
  color: var(--text-primary);
  background: var(--active-bg);
  border-left-color: var(--primary-color);
}

.log-line.done {
  color: var(--text-secondary);
}

.log-step-num {
  display: inline-block;
  min-width: 28px;
  color: var(--text-tertiary);
  font-size: 11px;
}

/* 对比图表 */
.chart-section {
  background: var(--card-bg);
  border: 1px solid var(--border-color);
  border-radius: 12px;
  padding: 20px;
}

.chart-header {
  font-size: 16px;
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: 16px;
}

.chart-canvas {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.chart-row {
  display: flex;
  align-items: center;
  gap: 12px;
}

.chart-label {
  min-width: 90px;
  font-size: 13px;
  font-weight: 600;
  font-family: var(--mono);
  color: var(--text-primary);
}

.chart-bar-track {
  flex: 1;
  height: 28px;
  background: var(--hover-bg);
  border-radius: 6px;
  overflow: hidden;
}

.chart-bar {
  height: 100%;
  border-radius: 6px;
  display: flex;
  align-items: center;
  padding-left: 8px;
  transition: width 0.5s ease;
  min-width: fit-content;
}

.chart-bar-text {
  font-size: 12px;
  font-weight: 600;
  color: white;
  white-space: nowrap;
}

.nav-back {
  margin: 32px 0;
}

.back-link {
  display: inline-block;
  color: var(--primary-color);
  text-decoration: none;
  font-weight: 600;
  font-size: 14px;
}

.back-link:hover {
  text-decoration: underline;
}

@media (prefers-color-scheme: dark) {
  .time-badge { background: #1e3a5f; color: #60a5fa; }
  .space-badge { background: #14532d; color: #4ade80; }
}
</style>
