// instruction_scanner.hako — InstructionScannerBox
// Minimal JSON v0 instruction object scanner with tolerant parsing.

using selfhost.shared.json.core.json_cursor as JsonCursorBox
using selfhost.vm.helpers.cfg_navigator as CfgNavigatorBox

static box InstructionScannerBox {
  _tprint(msg) { if msg.indexOf("[ERROR]") >= 0 { print(msg) } }

  index_of_from(hay, needle, pos) { return CfgNavigatorBox.index_of_from(hay, needle, pos) }

  find_balanced_object_end(json, idx) { return JsonCursorBox.seek_obj_end(json, idx) }


  normalize_delimiters(seg) {
    if seg == null { return "" }
    local out = ""
    local i = 0
    local n = seg.length()
    loop (i < n) {
      local ch = seg.substring(i, i+1)
      if i+2 <= n {
        local two = seg.substring(i, i+2)
        if two == "}," {
          if i+2 < n && seg.substring(i+2, i+3) == "{" { out = out + "}|{"  i = i + 3  continue }
        }
      }
      out = out + ch
      i = i + 1
    }
    return out
  }

  _extract_op(obj) {
    // Prefer explicit op key with escape-aware string end detection
    local k1 = "\"op\":\""
    local p1 = obj.indexOf(k1)
    if p1 >= 0 {
      local i = p1 + k1.length()           // start of value (right after opening quote)
      local j = JsonCursorBox.scan_string_end(obj, i - 1)
      if j > i { return obj.substring(i, j) }
    }
    // v1 style
    local kk = "\"kind\":\""
    local pk = obj.indexOf(kk)
    if pk >= 0 {
      local i2 = pk + kk.length()
      local j2 = JsonCursorBox.scan_string_end(obj, i2 - 1)
      if j2 > i2 {
        local k = obj.substring(i2, j2)
        if k == "Const" { return "const" }
        if k == "Ret" { return "ret" }
        if k == "Compare" { return "compare" }
        if k == "Branch" { return "branch" }
        if k == "Jump" { return "jump" }
      }
    }
    // compare v1 shorthand
    local ko = "\"operation\":\""
    local po = obj.indexOf(ko)
    if po >= 0 { return "compare" }
    // last-resort heuristics
    // Use dual-key probe to handle both plain and escaped forms when needed
    if me._has_key(obj, "lhs") == 1 and me._has_key(obj, "rhs") == 1 { return "compare" }
    if me._has_key(obj, "cond") == 1 { return "branch" }
    if me._has_key(obj, "target") == 1 { return "jump" }
    // Const fallback (typed value object) — tolerant to spaces
    if obj.indexOf("\"type\":\"i64\"") >= 0 { return "const" }
    // Detect explicit ret first
    if obj.indexOf("\"op\":\"ret\"") >= 0 { return "ret" }
    // Detect v1-style Ret without op key: must have top-level "value" and not have other discriminators including explicit op/dst
    if me._has_key(obj, "value") == 1 {
      if me._has_key(obj, "op") == 0 && me._has_key(obj, "dst") == 0 && me._has_key(obj, "lhs") == 0 && me._has_key(obj, "rhs") == 0 && me._has_key(obj, "cond") == 0 && me._has_key(obj, "target") == 0 && me._has_key(obj, "src") == 0 && me._has_key(obj, "op_kind") == 0 {
        return "ret"
      }
    }
    return ""
  }

  // Dual-key existence probe (plain/escaped)
  _has_key(seg, key) {
    if seg == null { return 0 }
    local plain = "\"" + key + "\""
    local escaped = "\\\"" + key + "\\\""
    local pos = JsonCursorBox.find_key_dual(seg, plain, escaped, 0)
    if pos >= 0 { return 1 } else { return 0 }
  }

  // Return a map {start,end,op} for next object starting at/after pos, or null if none
  next(seg, pos) {
    if seg == null { return null }
    if pos < 0 { pos = 0 }
    local start = me.index_of_from(seg, "{", pos)
    if start < 0 { return null }
    local endp = me.find_balanced_object_end(seg, start)
    if endp < 0 { return null }
    endp = endp + 1
    local obj = seg.substring(start, endp)
    local op = me._extract_op(obj)
    return { start: start, end: endp, op: op }
  }

  // Mini‑VM friendly variant: return "start,end,op" to avoid MapBox dependency
  next_tuple(seg, pos) {
    if seg == null { return "" }
    if pos < 0 { pos = 0 }
    local start = me.index_of_from(seg, "{", pos)
    if start < 0 { return "" }
    local endp = me.find_balanced_object_end(seg, start)
    if endp < 0 { return "" }
    endp = endp + 1
    local obj = seg.substring(start, endp)
    local op = me._extract_op(obj)
    return "" + start + "," + endp + "," + op
  }
}
