// pattern_util_box.hako — Shared utilities for MirBuilder lowers

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

static box PatternUtilBox {
  map_cmp(sym) { if sym=="<" {return "Lt"} if sym==">" {return "Gt"} if sym=="<=" {return "Le"} if sym==">=" {return "Ge"} if sym=="==" {return "Eq"} if sym=="!=" {return "Ne"} return null }
  map_binop(sym) { if sym=="+" {return "Add"} if sym=="-" {return "Sub"} if sym=="*" {return "Mul"} if sym=="/" {return "Div"} return null }
  // Normalize (op, swapped, limit) → (cmp, limit') where cmp in {Lt, Le, Gt, Ge}
  // swapped=0: i ? L, swapped=1: L ? i
  normalize_cmp_limit(op, swapped, limit_str) {
    local op1 = "" + op
    local ls = "" + limit_str
    if swapped == 0 {
      if op1 == "<" { return "Lt:" + ls }
      if op1 == "<=" { return "Lt:" + StringHelpers.int_to_str(JsonFragBox._str_to_int(ls) + 1) }
      if op1 == ">" { return "Gt:" + ls }
      if op1 == ">=" { return "Ge:" + ls }
      if op1 == "!=" { return "Lt:" + ls } // count(init=0,step=1) 前提の近似
      return null
    } else {
      if op1 == ">" { return "Lt:" + ls }
      if op1 == ">=" { return "Lt:" + StringHelpers.int_to_str(JsonFragBox._str_to_int(ls) + 1) }
      if op1 == "<" { return "Gt:" + ls }
      if op1 == "<=" { return "Ge:" + ls }
      if op1 == "!=" { return "Lt:" + ls } // 安全近似（入替側）
      return null
    }
  }
  // Normalize limit for canonical (i < limit) form.
  // When swapped==0, expects op in {'<','<='}; when swapped==1 (Int on lhs, Var on rhs), expects op in {'>','>='}.
  // Returns adjusted limit string or null if unsupported.
  normalize_limit_for_lt(op, swapped, limit_str) {
    local op1 = "" + op
    local ls = "" + limit_str
    if swapped == 0 {
      if op1 == "<" { return ls }
      if op1 == "<=" { return StringHelpers.int_to_str(JsonFragBox._str_to_int(ls) + 1) }
      if op1 == "!=" { return ls } // safe for count(init=0,step=1)
      return null
    } else {
      if op1 == ">" { return ls }
      if op1 == ">=" { return StringHelpers.int_to_str(JsonFragBox._str_to_int(ls) + 1) }
      return null
    }
  }
  find_local_int_before(s, name, before_pos) {
    local pos=0; local last=-1
    loop(true){
      local k=JsonFragBox.index_of_from(s, "\"type\":\"Local\"",pos); if k<0||k>=before_pos{break}
      local kn=JsonFragBox.index_of_from(s, "\"name\":\"",k)
      if kn>=0{
        local nm = JsonFragBox.read_string_after(s, kn + 7)
        if nm == name { last=k }
      }
      pos=k+1
    }
    if last<0 { return null }
    // Bound the search between this Local and the next Local/before_pos
    local next = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", last + 1)
    if next < 0 || next > before_pos { next = before_pos }
    local ki = JsonFragBox.index_of_from(s, "\"type\":\"Int\"", last)
    if ki < 0 || ki >= next { return null }
    local kv = JsonFragBox.index_of_from(s, "\"value\":", ki)
    if kv < 0 || kv >= next { return null }
    return JsonFragBox.read_int_after(s, kv+8)
  }
  find_local_bool_before(s, name, before_pos) {
    local pos=0; local last=-1
    loop(true){
      local k=JsonFragBox.index_of_from(s, "\"type\":\"Local\"",pos); if k<0||k>=before_pos{break}
      local kn=JsonFragBox.index_of_from(s, "\"name\":\"",k)
      if kn>=0{
        local nm = JsonFragBox.read_string_after(s, kn + 7)
        if nm == name { last=k }
      }
      pos=k+1
    }
    if last<0 { return null }
    local next = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", last + 1)
    if next < 0 || next > before_pos { next = before_pos }
    local kb = JsonFragBox.index_of_from(s, "\"type\":\"Bool\"", last)
    if kb < 0 || kb >= next { return null }
    local kv = JsonFragBox.index_of_from(s, "\"value\":", kb)
    if kv < 0 || kv >= next { return null }
    // JSON v0 uses 0/1 for bool values, not true/false literals
    return JsonFragBox.read_int_after(s, kv+8)
  }

  // Find the last Local(String) with given name before a given position.
  // Returns raw string or null when not found.
  find_local_string_before(s, name, before_pos) {
    local pos=0; local last=-1
    loop(true){
      local k=JsonFragBox.index_of_from(s, "\"type\":\"Local\"",pos)
      if k<0||k>=before_pos{break}
      local kn=JsonFragBox.index_of_from(s, "\"name\":\"",k)
      if kn>=0{
        local nm = JsonFragBox.read_string_after(s, kn + 7)
        if nm == name { last=k }
      }
      pos=k+1
    }
    if last<0 { return null }
    local next=JsonFragBox.index_of_from(s, "\"type\":\"Local\"", last+1)
    if next<0||next>before_pos{ next=before_pos }
    local ts=JsonFragBox.index_of_from(s, "\"type\":\"String\"", last)
    if ts<0||ts>=next { return null }
    local kv=JsonFragBox.index_of_from(s, "\"value\":\"", ts)
    if kv<0||kv>=next { return null }
    return JsonFragBox.read_string_after(s, kv+8)
  }

  // Find the last Local(Float) with given name before a given position.
  // Returns float string (as scanned) or null when not found.
  find_local_float_before(s, name, before_pos) {
    local pos=0; local last=-1
    loop(true){
      local k=JsonFragBox.index_of_from(s, "\"type\":\"Local\"",pos)
      if k<0||k>=before_pos{break}
      local kn=JsonFragBox.index_of_from(s, "\"name\":\"",k)
      if kn>=0{
        local nm = JsonFragBox.read_string_after(s, kn + 7)
        if nm == name { last=k }
      }
      pos=k+1
    }
    if last<0 { return null }
    local next=JsonFragBox.index_of_from(s, "\"type\":\"Local\"", last+1)
    if next<0||next>before_pos{ next=before_pos }
    local tf=JsonFragBox.index_of_from(s, "\"type\":\"Float\"", last)
    if tf<0||tf>=next { return null }
    local kv=JsonFragBox.index_of_from(s, "\"value\":", tf)
    if kv<0||kv>=next { return null }
    return JsonFragBox.read_float_after(s, kv+8)
  }

  // Find the last Local(Int) before a given position (name-agnostic).
  // Returns string digits or null when not found.
  find_any_local_int_before(s, before_pos) {
    local pos = 0; local last_k = -1; local n = ("" + s).length()
    loop(true) {
      local k = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", pos)
      if k < 0 || k >= before_pos { break }
      last_k = k
      pos = k + 1
    }
    if last_k < 0 { return null }
    // Limit search to the next Local or before_pos
    local next = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", last_k + 1)
    if next < 0 || next > before_pos { next = before_pos }
    // Constrain to Local.expr → Int
    local ti = JsonFragBox.index_of_from(s, "\"expr\":{\"type\":\"Int\"", last_k)
    if ti < 0 || ti >= next { return null }
    local kv = JsonFragBox.index_of_from(s, "\"value\":", ti)
    if kv < 0 || kv >= next { return null }
    return JsonFragBox.read_int_after(s, kv + 8)
  }

  // Find the last Local(String) before a given position (name-agnostic).
  // Returns raw string (unquoted) or null when not found.
  find_any_local_string_before(s, before_pos) {
    local pos = 0; local last_k = -1
    loop(true) {
      local k = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", pos)
      if k < 0 || k >= before_pos { break }
      last_k = k
      pos = k + 1
    }
    if last_k < 0 { return null }
    local next = JsonFragBox.index_of_from(s, "\"type\":\"Local\"", last_k + 1)
    if next < 0 || next > before_pos { next = before_pos }
    // Constrain to Local.expr → String
    local ts = JsonFragBox.index_of_from(s, "\"expr\":{\"type\":\"String\"", last_k)
    if ts < 0 || ts >= next { return null }
    local kv = JsonFragBox.index_of_from(s, "\"value\":", ts)
    if kv < 0 || kv >= next { return null }
    return JsonFragBox.read_string_after(s, kv + 8)
  }

  // Find [start,end] bounds for the then array of an If node starting at k_if.
  // Returns a pair encoded as "lb:rb" (caller compares with < and >), or null on failure.
  then_array_bounds(s, k_if) {
    if s == null { return null }
    local kth = JsonFragBox.index_of_from(s, "\"then\":", k_if)
    if kth < 0 { return null }
    local lb = JsonFragBox.index_of_from(s, "[", kth)
    if lb < 0 { return null }
    local rb = JsonFragBox._seek_array_end(s, lb)
    if rb < 0 { return null }
    return ("" + lb) + ":" + ("" + rb)
  }
  // Find [start,end] bounds for the else array that follows the then array of the same If node.
  // Returns a pair encoded as "lb:rb" or null when not found.
  else_array_bounds_after_then(s, k_if) {
    if s == null { return null }
    // locate then first to avoid matching outer/previous else
    local kth = JsonFragBox.index_of_from(s, "\"then\":", k_if)
    if kth < 0 { return null }
    local lb_then = JsonFragBox.index_of_from(s, "[", kth)
    if lb_then < 0 { return null }
    local rb_then = JsonFragBox._seek_array_end(s, lb_then)
    if rb_then < 0 { return null }
    local kel = JsonFragBox.index_of_from(s, "\"else\":", rb_then)
    if kel < 0 { return null }
    local lb = JsonFragBox.index_of_from(s, "[", kel)
    if lb < 0 { return null }
    local rb = JsonFragBox._seek_array_end(s, lb)
    if rb < 0 { return null }
    return ("" + lb) + ":" + ("" + rb)
  }
}
