<template>
  <div class="code-editor" :class="{ running: readOnly }">
    <div class="editor-header">
      <span>Code</span>
      <span class="line-count mono">{{ instrCount }} {{ instrCount === 1 ? 'instruction' : 'instructions' }}</span>
    </div>
    <div class="editor-body">
      <div class="line-numbers" ref="lineNumsEl">
        <div v-for="n in totalLines" :key="n" class="ln"
             :class="{ current: n-1 === currentLine, error: n-1 === errorLine }">{{ n }}</div>
      </div>
      <textarea
        v-if="!readOnly"
        ref="ta"
        :value="modelValue"
        @input="$emit('update:modelValue', $event.target.value)"
        @scroll="syncScroll"
        @keydown.tab.prevent="insertTab"
        spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off"
      ></textarea>
      <pre v-else class="code-display" ref="codeDisplay" @scroll="syncScroll"><code><template
        v-for="(line, i) in displayLines" :key="i"
      ><span class="cl" :class="{ current: i === currentLine, error: i === errorLine }"
             v-html="hl(line)"></span>
</template></code></pre>
    </div>
  </div>
</template>

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

const props = defineProps({
  modelValue: String,
  currentLine: { type: Number, default: -1 },
  errorLine: { type: Number, default: -1 },
  readOnly: Boolean,
})
defineEmits(['update:modelValue'])

const ta = ref(null), lineNumsEl = ref(null), codeDisplay = ref(null)

const totalLines = computed(() => (props.modelValue||'').split('\n').length)
const displayLines = computed(() => (props.modelValue||'').split('\n'))
const instrCount = computed(() => {
  let c = 0
  for (const line of (props.modelValue||'').split('\n')) {
    let l = line; const ci = l.indexOf(';'); if (ci >= 0) l = l.slice(0, ci); l = l.trim()
    if (!l) continue
    const lm = l.match(/^[a-zA-Z_]\w*\s*:(.*)$/); if (lm) { l = lm[1].trim(); if (!l) continue }
    c++
  }
  return c
})

function hl(line) {
  let h = line.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
  h = h.replace(/(;.*)$/, '<span class="sc">$1</span>')
  h = h.replace(/^(\s*\w+\s*:)/, '<span class="sl">$1</span>')
  h = h.replace(/\b(MOV|ADD|SUB|MUL|XDIV|XMOD|AND|ORR|EOR|MVN|LSL|LSR|CMP|BEQ|BNE|BGT|BLT|BGE|BLE|LDR|STR|PUSH|POP|XOUT|XHLT|NOP|B)\b/gi,
    '<span class="sk">$1</span>')
  h = h.replace(/\b(R[0-7])\b/gi, '<span class="sr">$1</span>')
  h = h.replace(/(\[[^\]]+\])/g, '<span class="sm">$1</span>')
  h = h.replace(/(#(?:0x[0-9a-fA-F]+|0b[01]+|\d+))/g, '<span class="sn">$1</span>')
  // Bare numbers not already highlighted
  h = h.replace(/(?<![#\w])\b(\d+)\b(?![0-9a-fA-F])/g, '<span class="sn">$1</span>')
  return h || ' '
}

function syncScroll(e) { if (lineNumsEl.value) lineNumsEl.value.scrollTop = e.target.scrollTop }
function insertTab() {
  const el = ta.value; if (!el) return
  const s = el.selectionStart, e2 = el.selectionEnd, v = props.modelValue
  el.value = v.substring(0,s) + '  ' + v.substring(e2)
  el.selectionStart = el.selectionEnd = s + 2
  el.dispatchEvent(new Event('input'))
}

watch(() => props.currentLine, async (line) => {
  if (line < 0) return; await nextTick()
  const c = codeDisplay.value; if (!c) return
  const el = c.querySelectorAll('.cl')[line]; if (el) el.scrollIntoView({ block: 'nearest' })
})
</script>

<style scoped>
.code-editor { display: flex; flex-direction: column; background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; min-height: 0; }
.editor-header { display: flex; justify-content: space-between; padding: 6px 14px; background: var(--bg-surface); font-size: 12px; color: var(--text-muted); border-bottom: 1px solid var(--border); flex-shrink: 0; }
.editor-body { display: flex; flex: 1; min-height: 0; overflow: hidden; }
.line-numbers { flex-shrink: 0; padding: 10px 0; background: var(--bg-dark); user-select: none; min-width: 36px; overflow: hidden; border-right: 1px solid var(--border); }
.ln { padding: 0 8px 0 10px; font-family: var(--font-mono); font-size: 13px; line-height: 1.6; color: var(--text-muted); text-align: right; }
.ln.current { color: var(--accent-yellow); background: rgba(245,158,11,0.1); }
.ln.error { color: var(--accent-red); background: rgba(239,68,68,0.1); }
textarea { flex: 1; padding: 10px 14px; background: var(--bg-dark); border: none; color: var(--text-primary); font-family: var(--font-mono); font-size: 13px; line-height: 1.6; resize: none; outline: none; tab-size: 2; white-space: pre; overflow: auto; }
.code-display { flex: 1; padding: 10px 14px; background: var(--bg-dark); margin: 0; overflow: auto; min-height: 0; }
.code-display code { display: block; }
.cl { display: block; font-size: 13px; line-height: 1.6; padding: 0 4px; border-radius: 2px; white-space: pre; min-height: 1.6em; }
.cl.current { background: rgba(245,158,11,0.15); border-left: 2px solid var(--accent-yellow); padding-left: 2px; }
.cl.error { background: rgba(239,68,68,0.15); border-left: 2px solid var(--accent-red); padding-left: 2px; }
:deep(.sk) { color: var(--accent-blue); font-weight: 600; }
:deep(.sr) { color: var(--accent-cyan); }
:deep(.sn) { color: var(--accent-yellow); }
:deep(.sc) { color: var(--text-muted); font-style: italic; }
:deep(.sm) { color: var(--accent-purple); }
:deep(.sl) { color: var(--accent-pink); }
</style>
