// NormalizeRefBox — rewrite ref_get/ref_set → boxcall getField/setField

using selfhost.shared.json.utils.json_frag as JsonFragBox
using selfhost.shared.common.string_helpers as StringHelpers

static box NormalizeRefBox {
  _seek_object_end(s, start) {
    if s == null { return -1 }
    if start < 0 || start >= s.length() { return -1 }
    if s.substring(start, start+1) != "{" { return -1 }
    local i = start
    local depth = 0
    local in_str = 0
    local esc = 0
    loop (i < s.length()) {
      local ch = s.substring(i, i+1)
      if in_str == 1 {
        if esc == 1 { esc = 0 }
        else if ch == "\\" { esc = 1 }
        else if ch == "\"" { in_str = 0 }
      } else {
        if ch == "\"" { in_str = 1 }
        else if ch == "{" { depth = depth + 1 }
        else if ch == "}" { depth = depth - 1  if depth == 0 { return i } }
      }
      i = i + 1
    }
    return -1
  }
  run(json) {
    if json == null { return null }
    local out = json
    local pos = 0
    loop(true) {
      // ref_get
      local k = JsonFragBox.index_of_from(out, "\"op\":\"ref_get\"", pos)
      if k < 0 { break }
      local obj_start = out.substring(0, k).lastIndexOf("{")
      if obj_start < 0 { pos = k + 1; continue }
      local obj_end = me._seek_object_end(out, obj_start)
      if obj_end < 0 { pos = k + 1; continue }
      local kdst = JsonFragBox.index_of_from(out, "\"dst\":", obj_start)
      local dsts = (kdst>=0 && kdst<obj_end ? StringHelpers.read_digits(out, kdst+6) : "")
      local kref = JsonFragBox.index_of_from(out, "\"reference\":", obj_start)
      local refv = (kref>=0 && kref<obj_end ? StringHelpers.read_digits(out, kref+12) : "")
      // field is JSON string; read raw token between quotes after "field":"
      local kf = JsonFragBox.index_of_from(out, "\"field\":\"", obj_start)
      local field = (kf>=0 && kf<obj_end ? JsonFragBox.read_string_after(out, kf+9) : "")
      if dsts != "" && refv != "" && field != "" {
        // emit: const field + boxcall getField(field)
        local c = "{\"op\":\"const\",\"dst\":__FID__,\"value\":{\"type\":\"string\",\"value\":\"__FLD__\"}}"
        // allocate a temporary dst id for field: reuse dst+1000000 to avoid collision (text-level best effort)
        local fid = dsts + "000000"
        c = StringHelpers.replace_all(c, "__FID__", fid)
        c = StringHelpers.replace_all(c, "__FLD__", field)
        local repl = c + ",{" + (dsts!=""?"\"dst\":"+dsts+",":"") + "\"op\":\"boxcall\",\"box\":" + refv + ",\"method\":\"getField\",\"args\":[" + fid + "]}"
        out = out.substring(0, obj_start) + repl + out.substring(obj_end+1, out.length())
        pos = obj_start + repl.length()
      } else {
        pos = k + 1
      }
    }
    // ref_set
    pos = 0
    loop(true) {
      local k = JsonFragBox.index_of_from(out, "\"op\":\"ref_set\"", pos)
      if k < 0 { break }
      local obj_start = out.substring(0, k).lastIndexOf("{")
      if obj_start < 0 { pos = k + 1; continue }
      local obj_end = me._seek_object_end(out, obj_start)
      if obj_end < 0 { pos = k + 1; continue }
      local kref = JsonFragBox.index_of_from(out, "\"reference\":", obj_start)
      local refv = (kref>=0 && kref<obj_end ? StringHelpers.read_digits(out, kref+12) : "")
      local kval = JsonFragBox.index_of_from(out, "\"value\":", obj_start)
      local valv = (kval>=0 && kval<obj_end ? StringHelpers.read_digits(out, kval+8) : "")
      local kf = JsonFragBox.index_of_from(out, "\"field\":\"", obj_start)
      local field = (kf>=0 && kf<obj_end ? JsonFragBox.read_string_after(out, kf+9) : "")
      if refv != "" && valv != "" && field != "" {
        local c = "{\"op\":\"const\",\"dst\":__FID__,\"value\":{\"type\":\"string\",\"value\":\"__FLD__\"}}"
        local fid = refv + "000000"
        c = StringHelpers.replace_all(c, "__FID__", fid)
        c = StringHelpers.replace_all(c, "__FLD__", field)
        local repl = c + ",{" + "\"op\":\"boxcall\",\"box\":" + refv + ",\"method\":\"setField\",\"args\":[" + fid + "," + valv + "]}"
        out = out.substring(0, obj_start) + repl + out.substring(obj_end+1, out.length())
        pos = obj_start + repl.length()
      } else {
        pos = k + 1
      }
    }
    return out
  }
}
