// mir_vm_min.hako — Ny製の最小MIR(JSON v0)実行器（const/compare/copy/branch/jump/ret の最小）
using selfhost.vm.helpers.gc_hooks as GcHooks
using selfhost.vm.helpers.op_handlers as OpHandlersBox
using selfhost.shared.common.string_helpers as StringHelpers
using selfhost.shared.common.string_ops as StringOps
using selfhost.shared.json.utils.json_frag as JsonFragBox
using selfhost.shared.json.core.json_cursor as JsonCursorBox
using selfhost.vm.helpers.mini_map as MiniMap
using selfhost.vm.helpers.operator as OperatorBox
using selfhost.vm.helpers.mini_mir_v1_scan as MiniMirV1Scan
using selfhost.vm.helpers.compare_ops as CompareOpsBox
using selfhost.vm.hakorune-vm.json_v1_reader as JsonV1ReaderBox
using selfhost.vm.helpers.mir_call_v1_handler as MirCallV1HandlerBox
using selfhost.vm.helpers.compare_scan as CompareScanBox
using selfhost.vm.helpers.phi_apply as PhiApplyBox
using selfhost.vm.helpers.guard as GuardBox
using selfhost.vm.helpers.result as Result
using selfhost.vm.helpers.phi_decode as PhiDecodeBox
using selfhost.vm.helpers.ret_resolve as RetResolveSimpleBox
using selfhost.vm.helpers.instruction_scanner as InstructionScannerBox

static box MirVmMin {
  _tprint(msg) {
    // Only emit hard errors by default; avoid env dependencies in Mini‑VM
    // Coerce to string to avoid VoidBox receiver issues during early boot
    using selfhost.vm.hakorune-vm.str_cast as StrCast
    msg = StrCast.to_str(msg)
    if msg.indexOf("[ERROR]") >= 0 { print(msg) }
  }
  _d(msg, trace) { if trace == 1 { print(msg) } }
  _handle_mir_call(seg, regs) {
    MirCallV1HandlerBox.handle(seg, regs)
  }
  // Compatibility runner (prints and returns). Prefer run_min for quiet return-only.
  run(mjson) { local v = me._run_min(mjson) print(me._int_to_str(v)) return v }
  // New: quiet entry that returns the result without printing.
  run_min(mjson) { return me._run_min(mjson) }
  // Thin-mode runner (ret resolution simplified)
  run_thin(mjson) {
    // Inject a lightweight marker into JSON to toggle thin mode inside _run_min
    if mjson.substring(0,1) == "{" {
      local payload = mjson.substring(1, mjson.length())
      local j2 = "{\"__thin__\":1," + payload
      local v = me._run_min(j2)
      print(me._int_to_str(v))
      return v
    }
    // Fallback: no-op when input is unexpected
    local v = me._run_min(mjson)
    print(me._int_to_str(v))
    return v
  }

  // helpers
  _int_to_str(n) { return StringHelpers.int_to_str(n) }
  _is_numeric_str(s){ if s==null {return 0} local n=s.length() if n==0 {return 0} local i=0 if s.substring(0,1)=="-" { if n==1 {return 0} i=1 } loop(i<n){ local ch=s.substring(i,i+1) if ch<"0"||ch>"9" {return 0} i=i+1 } return 1 }
  _load_reg(regs,id){ local v=regs.getField(StringHelpers.int_to_str(id)) if v==null {return 0} local s=""+v if me._is_numeric_str(s)==1 { return JsonFragBox._str_to_int(s) } return 0 }

  // block helpers
  _block_insts_start(mjson,bid){
    // tolerant scan: find '"id"' then parse digits after ':' with optional spaces
    local pos = 0
    loop(true) {
      local pid = StringOps.index_of_from(mjson, "\"id\"", pos)
      if pid < 0 { return -1 }
      // find ':'
      local pc = StringOps.index_of_from(mjson, ":", pid)
      if pc < 0 { return -1 }
      local digits = JsonFragBox.read_int_from(mjson, pc + 1)
      if digits != null {
        local v = JsonFragBox._str_to_int(digits)
        if v == bid {
          // instructions array for this block
          local qk = StringOps.index_of_from(mjson, "\"instructions\"", pc)
          if qk < 0 { pos = pid + 1  continue }
          local qb = StringOps.index_of_from(mjson, "[", qk)
          if qb < 0 { pos = pid + 1  continue }
          return qb
        }
      }
      pos = pc + 1
    }
  }

  // local copy handler
  _handle_copy(seg, regs){ local dst=JsonFragBox.get_int(seg,"dst") local src=JsonFragBox.get_int(seg,"src") if dst==null || src==null {return} local v=regs.getField(StringHelpers.int_to_str(src)) regs.setField(StringHelpers.int_to_str(dst), v) }

  // ret op handler
  _handle_ret_op(seg, regs, last_cmp_dst, last_cmp_val, gc_trace) {
    local v = JsonFragBox.get_int(seg, "value")
    if v == null {
      me._tprint("[ERROR] Undefined ret value field")
      return -1
    }
    if v == last_cmp_dst {
      if gc_trace == 1 { print("[GC] mark=0 sweep=0 survivors=0") }
      return last_cmp_val
    }
    local sval_raw = regs.getField(StringHelpers.int_to_str(v))
    if sval_raw != null {
      local sval = "" + sval_raw
      if me._is_numeric_str(sval) == 1 {
        if gc_trace == 1 { print("[GC] mark=0 sweep=0 survivors=0") }
        return me._load_reg(regs, v)
      }
    }
    me._tprint("[ERROR] Undefined register ret: r"+me._int_to_str(v))
    return -1
  }

  // block ret resolution helper
  _resolve_block_ret(inst_seg, regs, last_cmp_dst, last_cmp_val, gc_trace) {
    if last_cmp_dst >= 0 { return last_cmp_val }
    // Iterate objects in this block and find an explicit ret
    local scan = 0
    loop(true) {
      if scan >= inst_seg.length() { break }
      local tup = InstructionScannerBox.next_tuple(inst_seg, scan)
      if tup == "" { break }
      local c1 = StringOps.index_of_from(tup, ",", 0)
      local c2 = StringOps.index_of_from(tup, ",", c1+1)
      if c1 < 0 || c2 < 0 { break }
      local obj_start = JsonFragBox._str_to_int(tup.substring(0, c1))
      local obj_end = JsonFragBox._str_to_int(tup.substring(c1+1, c2))
      local op = tup.substring(c2+1, tup.length())
      if op == "ret" {
        local seg = inst_seg.substring(obj_start, obj_end)
        me._d("[DEBUG] resolve_ret seg=" + seg, 1)
        local rid = JsonFragBox.get_int(seg, "value")
        if rid != null {
          if rid == last_cmp_dst {
            if gc_trace == 1 { print("[GC] mark=0 sweep=0 survivors=0") }
            return last_cmp_val
          }
          local rv = me._load_reg(regs, rid)
          if gc_trace == 1 { print("[GC] mark=0 sweep=0 survivors=0") }
          return rv
        }
      }
      scan = obj_end
    }
    return me._resolve_final_ret(inst_seg, regs, last_cmp_dst, last_cmp_val, gc_trace)
  }

  // final ret resolution helper
  _resolve_final_ret(inst_seg, regs, last_cmp_dst, last_cmp_val, gc_trace) {
    local r = RetResolveSimpleBox.resolve(inst_seg, regs, last_cmp_dst, last_cmp_val)
    if r != null {
      if gc_trace == 1 { print("[GC] mark=0 sweep=0 survivors=0") }
      return r
    }
    return me._load_reg(regs, 1)
  }

  _run_min(mjson) {
    // Normalize input as string to guarantee String methods availability
    mjson = "" + mjson
    // thin_mode=0: legacy heuristics（互換）; thin_mode=1: simplified ret
    local thin_mode = 0
    if mjson.indexOf("\"__thin__\":1") >= 0 { thin_mode = 1 }
    // trace mode for dev: prints [DEBUG] messages
    local trace = 0
    if mjson.indexOf("\"__trace__\":1") >= 0 { trace = 1 }
    // gc trace (mini‑VM専用マーカー; 環境依存を避ける)
    local gc_trace = 0
    if mjson.indexOf("\"__gc_trace__\":1") >= 0 { gc_trace = 1 }

    // Safety first: handle simple compare→ret or const→ret in Block 0
    // without allocating any runtime boxes. This avoids interpreter-level
    // recursion during early startup (observed as Rust stack overflow) and
    // covers our selfhost M2 minimal cases.
    local b0 = JsonFragBox.block0_segment(mjson)
    if b0 != "" {
      // 1) Pure const→ret (only when const appears before ret and no other ops present)
      if b0.indexOf("\"op\":\"compare\"") < 0 && b0.indexOf("\"op\":\"binop\"") < 0 && b0.indexOf("\"op\":\"copy\"") < 0 && b0.indexOf("\"op\":\"jump\"") < 0 && b0.indexOf("\"op\":\"branch\"") < 0 {
        local ret_pos = b0.indexOf("\"op\":\"ret\"")
        local const_pos = b0.indexOf("\"op\":\"const\"")
        if ret_pos >= 0 and const_pos >= 0 and const_pos < ret_pos {
        // Grab the first typed const value and return it
        local key = "\"value\":{\"type\":\"i64\",\"value\":"
        local p = StringOps.index_of_from(b0, key, 0)
        if p >= 0 {
          local ds = b0.substring(p + key.length(), b0.length())
          // read consecutive digits
          local i = 0
          local out = ""
          loop(true) {
            local ch = ds.substring(i, i+1)
            if ch == "" { break }
            if ch >= "0" && ch <= "9" { out = out + ch  i = i + 1 } else { break }
          }
          if out != "" { return JsonFragBox._str_to_int(out) }
        }
        }
      }
      // 2) Light one-step fast path for pattern: const/const/compare/branch then simple ret in next block
      //    Evaluate block 0 and jump to the chosen block; if it directly returns a register or constant, return it.
      {
        // Mini fast-eval for B0
        local regs0 = new MiniMap()
        local scan0 = 0
        local last_cmp_dst0 = -1
        local last_cmp_val0 = 0
        local bb_choice = -1
        loop(true) {
          if scan0 >= b0.length() { break }
          local tup0 = InstructionScannerBox.next_tuple(b0, scan0)
          if tup0 == "" { break }
          local c10 = StringOps.index_of_from(tup0, ",", 0)
          local c20 = StringOps.index_of_from(tup0, ",", c10+1)
          if c10 < 0 || c20 < 0 { break }
          local s0 = JsonFragBox._str_to_int(tup0.substring(0, c10))
          local e0 = JsonFragBox._str_to_int(tup0.substring(c10+1, c20))
          local op0 = tup0.substring(c20+1, tup0.length())
          local seg0 = b0.substring(s0, e0)
          if op0 == "const" { OpHandlersBox.handle_const(seg0, regs0) }
          else { if op0 == "compare" {
            // evaluate compare and stash dst
            local dst0 = JsonFragBox.get_int(seg0, "dst")
            local l0 = JsonFragBox.get_int(seg0, "lhs")
            local r0 = JsonFragBox.get_int(seg0, "rhs")
            local kind0 = JsonFragBox.get_str(seg0, "cmp")
            if kind0 == "" { local sym0 = JsonFragBox.get_str(seg0, "operation"); if sym0 != "" { kind0 = CompareOpsBox.map_symbol(sym0) } else { kind0 = "Eq" } }
            if dst0 != null && l0 != null && r0 != null {
              local a0 = me._load_reg(regs0, l0)
              local b0v = me._load_reg(regs0, r0)
              local cv0 = CompareOpsBox.eval(kind0, a0, b0v)
              regs0.setField(""+dst0, ""+cv0)
              last_cmp_dst0 = dst0
              last_cmp_val0 = cv0
            }
          } else { if op0 == "branch" {
            local c = JsonFragBox.get_int(seg0, "cond")
            local t = JsonFragBox.get_int(seg0, "then")
            local e = JsonFragBox.get_int(seg0, "else")
            if c != null && t != null && e != null {
              local cv = 0
              if c == last_cmp_dst0 { cv = last_cmp_val0 } else { cv = me._load_reg(regs0, c) }
              if cv != 0 { bb_choice = t } else { bb_choice = e }
              break
            }
          } } }
          scan0 = e0
        }
        if bb_choice >= 0 {
          local bst = JsonV1ReaderBox.block_insts_start(mjson, bb_choice)
          if bst >= 0 {
            local bend = JsonCursorBox.seek_array_end(mjson, bst)
            if bend > bst {
              local seg1 = mjson.substring(bst + 1, bend)
              local scan1 = 0
              loop(true) {
                if scan1 >= seg1.length() { break }
                local tup1 = InstructionScannerBox.next_tuple(seg1, scan1)
                if tup1 == "" { break }
                local c11 = StringOps.index_of_from(tup1, ",", 0)
                local c21 = StringOps.index_of_from(tup1, ",", c11+1)
                if c11 < 0 || c21 < 0 { break }
                local s1 = JsonFragBox._str_to_int(tup1.substring(0, c11))
                local e1 = JsonFragBox._str_to_int(tup1.substring(c11+1, c21))
                local op1 = tup1.substring(c21+1, tup1.length())
                local seg_item = seg1.substring(s1, e1)
                if op1 == "const" { OpHandlersBox.handle_const(seg_item, regs0) }
                if op1 == "ret" {
                  local r = me._handle_ret_op(seg_item, regs0, last_cmp_dst0, last_cmp_val0, gc_trace)
                  return r
                }
                scan1 = e1
              }
            }
          }
        }
      }
    }

    // Use user InstanceBox (MiniMap) with getField/setField to avoid provider dependencies
    local regs = new MiniMap()
    local last_cmp_dst = -1
    local last_cmp_val = 0
    // Optional: adopt OperatorBox for parity checks when JSON has a special marker
    local op_adopt = 0
    if mjson.indexOf("\"__op_adopt__\":1") >= 0 { op_adopt = 1 }
    // Track last binop materialization within the current scan window
    local last_binop_dst = -1
    local last_binop_val = 0
    local bb = 0
    local prev_bb = -1
    local steps = 0
    local max_steps = 200000
    loop(true){
      steps = steps + 1
      if steps > max_steps { return 0 }
      local start = JsonV1ReaderBox.block_insts_start(mjson, bb)
      me._d("[DEBUG] start="+me._int_to_str(start), trace)
      if start < 0 { return 0 }
      local endp = JsonCursorBox.seek_array_end(mjson, start)
      me._d("[DEBUG] endp="+me._int_to_str(endp), trace)
      if endp <= start { return 0 }
      // Take array inner segment (skip '[')
      local inst_seg = mjson.substring(start + 1, endp)
      me._d("[DEBUG] seglen="+me._int_to_str(inst_seg.length()), trace)
      // scan objects in this block
      local scan_pos = 0
      local inst_count = 0
      local moved = 0
      loop(true){
        if scan_pos >= inst_seg.length() { break }
        local tup = InstructionScannerBox.next_tuple(inst_seg, scan_pos)
        if tup == "" { break }
        // parse "start,end,op"
        local c1 = StringOps.index_of_from(tup, ",", 0)
        local c2 = StringOps.index_of_from(tup, ",", c1+1)
        if c1 < 0 || c2 < 0 { break }
        local obj_start = JsonFragBox._str_to_int(tup.substring(0, c1))
        local obj_end = JsonFragBox._str_to_int(tup.substring(c1+1, c2))
        local op = tup.substring(c2+1, tup.length())
        local seg = inst_seg.substring(obj_start, obj_end)
        if op == null { op = "" } if op == "null" { op = "" } if op == 0 { op = "" }
        if op == "" {
          if seg.indexOf("op_kind") >= 0 { op = "binop" } else {
            if seg.indexOf("lhs") >= 0 and seg.indexOf("rhs") >= 0 { op = "compare" } else {
              if seg.indexOf("cond") >= 0 { op = "branch" } else {
                if seg.indexOf("target") >= 0 { op = "jump" } else {
                  if seg.indexOf("src") >= 0 and seg.indexOf("dst") >= 0 { op = "copy" } else { op = "const" }
                }
              }
            }
          }
        }
        if op == "const" {
          OpHandlersBox.handle_const(seg, regs)
        }
        else if op == "copy" { me._handle_copy(seg, regs) }
        else if op == "binop" {
          OpHandlersBox.handle_binop(seg, regs)
          local bdst = JsonFragBox.get_int(seg, "dst")
          if bdst != null { last_binop_dst = bdst  last_binop_val = me._load_reg(regs, bdst) }
        }
        else if op == "compare" {
          me._d("[DEBUG] compare seg=" + seg, trace)
          // Extract fields directly to avoid MapBox dependency
          local kdst_fast = JsonFragBox.get_int(seg, "dst")
          local klhs_fast = JsonFragBox.get_int(seg, "lhs")
          local krhs_fast = JsonFragBox.get_int(seg, "rhs")
          local kcmp_fast = JsonFragBox.get_str(seg, "cmp")
          if kcmp_fast == "" {
            local sym = JsonFragBox.get_str(seg, "operation")
            if sym != "" { kcmp_fast = CompareOpsBox.map_symbol(sym) } else { kcmp_fast = "Eq" }
          }
          // Compute compare result and store
          if kdst_fast != null && klhs_fast != null && krhs_fast != null {
            local a = me._load_reg(regs, klhs_fast)
            local b = me._load_reg(regs, krhs_fast)
            local cv = CompareOpsBox.eval(kcmp_fast, a, b)
            regs.setField(StringHelpers.int_to_str(kdst_fast), StringHelpers.int_to_str(cv))
            last_cmp_dst = kdst_fast
            last_cmp_val = cv
          }
        }
        else if op == "mir_call" {
          me._handle_mir_call(seg, regs)
        }
        else if op == "branch" {
          local c = JsonFragBox.get_int(seg, "cond")
          local t = JsonFragBox.get_int(seg, "then")
          local e = JsonFragBox.get_int(seg, "else")
          local cv = 0
          if c != null {
            if c == last_cmp_dst { cv = last_cmp_val } else { cv = me._load_reg(regs, c) }
          }
          prev_bb = bb
          if cv != 0 { bb = t } else { bb = e }
          moved = 1
          // GC v0 safepoint: back-edge or control transfer
          GcHooks.safepoint()
          if gc_trace == 1 { print("[GC] mark=0 sweep=0 survivors=0") }
          scan_pos = obj_end
          break
        }
        else if op == "jump" {
          local tgt = JsonFragBox.get_int(seg, "target")
          if tgt == null { return 0 }
          prev_bb = bb
          bb = tgt
          moved = 1
          // GC v0 safepoint: back-edge or control transfer
          GcHooks.safepoint()
          if gc_trace == 1 { print("[GC] mark=0 sweep=0 survivors=0") }
          scan_pos = obj_end
          break
        }
        else if op == "phi" {
          // Mini‑VM v0: skip PHI (blocks we exercise should not rely on PHI semantics)
        }
        if op == "throw" {
          me._tprint("[ERROR] Throw terminator encountered")
          return -2
        }
        if op == "ret" {
          me._d("[DEBUG] ret seg=" + seg, trace)
          local r = me._handle_ret_op(seg, regs, last_cmp_dst, last_cmp_val, gc_trace)
          return r
        }
        // advance
        inst_count = inst_count + 1
        scan_pos = obj_end
      }
      if moved == 1 { continue }
      return me._resolve_block_ret(inst_seg, regs, last_cmp_dst, last_cmp_val, gc_trace)
    }
    return 0
  }

}
