// JsonUtilsBox — JSON読み取りユーティリティの共通箱
// 責務: JSON値抽出・JSON構造パース・トップレベル配列分割
// Extracted from JsonProgramBox (480行 → 345行, 削減 ~135行)
using selfhost.shared.common.string_helpers as StringHelpers

static box JsonUtilsBox {
  // Extract JSON value by key (returns value with '@' + end position marker)
  extract_value(json, key) {
    if json == null { return null }
    local pattern = "\"" + key + "\""
    local idx = StringHelpers.index_of(json, 0, pattern)
    if idx < 0 { return null }
    idx = idx + pattern.length()
    idx = StringHelpers.skip_ws(json, idx)
    if json.substring(idx, idx + 1) != ":" { return null }
    idx = StringHelpers.skip_ws(json, idx + 1)
    local res = me.read_value(json, idx)
    local at = StringHelpers.last_index_of(res, "@")
    if at < 0 { return null }
    return res.substring(0, at)
  }

  // Extract string value by key with default fallback
  extract_string_value(json, key, default_value) {
    local raw = me.extract_value(json, key)
    if raw == null { return default_value }
    local trimmed = StringHelpers.trim(raw)
    if trimmed.length() >= 2 && trimmed.substring(0,1) == "\"" && trimmed.substring(trimmed.length()-1, trimmed.length()) == "\"" {
      return me.unescape_string(trimmed.substring(1, trimmed.length()-1))
    }
    return default_value
  }

  // Read JSON value (dispatch to appropriate reader)
  read_value(json, idx) {
    local n = json.length()
    if idx >= n { return "@" + StringHelpers.int_to_str(idx) }
    local ch = json.substring(idx, idx + 1)
    if ch == "\"" { return me.read_string(json, idx) }
    if ch == "{" { return me.read_object(json, idx) }
    if ch == "[" { return me.read_array(json, idx) }
    return me.read_literal(json, idx)
  }

  // Read JSON string (escape-aware) with position marker
  read_string(json, idx) {
    local i = idx + 1
    local n = json.length()
    local done = 0
    loop(done == 0 && i < n) {
      local ch = json.substring(i, i + 1)
      if ch == "\\" {
        i = i + 2
      } else {
        if ch == "\"" { done = 1 i = i + 1 } else { i = i + 1 }
      }
    }
    return json.substring(idx, i) + "@" + StringHelpers.int_to_str(i)
  }

  // Skip JSON string (returns end position)
  skip_string(json, idx) {
    local i = idx + 1
    local n = json.length()
    local done = 0
    loop(done == 0 && i < n) {
      local ch = json.substring(i, i + 1)
      if ch == "\\" { i = i + 2 }
      else { if ch == "\"" { done = 1 i = i + 1 } else { i = i + 1 } }
    }
    return i
  }

  // Read JSON object (bracket-aware) with position marker
  read_object(json, idx) {
    local depth = 0
    local i = idx
    local n = json.length()
    loop(i < n) {
      local ch = json.substring(i, i + 1)
      if ch == "\"" {
        i = me.skip_string(json, i)
      } else {
        if ch == "{" { depth = depth + 1 }
        else if ch == "}" {
          depth = depth - 1
          if depth == 0 { i = i + 1 break }
        }
        i = i + 1
      }
    }
    return json.substring(idx, i) + "@" + StringHelpers.int_to_str(i)
  }

  // Read JSON array (bracket-aware) with position marker
  read_array(json, idx) {
    local depth = 0
    local i = idx
    local n = json.length()
    loop(i < n) {
      local ch = json.substring(i, i + 1)
      if ch == "\"" {
        i = me.skip_string(json, i)
      } else {
        if ch == "[" { depth = depth + 1 }
        else if ch == "]" {
          depth = depth - 1
          if depth == 0 { i = i + 1 break }
        }
        i = i + 1
      }
    }
    return json.substring(idx, i) + "@" + StringHelpers.int_to_str(i)
  }

  // Read JSON literal (number/true/false/null) with position marker
  read_literal(json, idx) {
    local n = json.length()
    local i = idx
    loop(i < n) {
      local ch = json.substring(i, i + 1)
      if ch == "," || ch == "}" || ch == "]" { break }
      i = i + 1
    }
    return json.substring(idx, i) + "@" + StringHelpers.int_to_str(i)
  }

  // Split JSON array at top-level commas (depth-aware, escape-aware)
  split_top_level(array_json) {
    local out = new ArrayBox()
    local n = array_json.length()
    local i = 1
    local start = 1
    local depth = 0
    local in_string = 0
    loop(i < n - 1) {
      local ch = array_json.substring(i, i + 1)
      if in_string == 1 {
        if ch == "\\" {
          i = i + 2
        } else {
          if ch == "\"" { in_string = 0 }
          i = i + 1
        }
      } else {
        if ch == "\"" {
          in_string = 1
          i = i + 1
        } else {
          if ch == "{" || ch == "[" { depth = depth + 1 }
          else if ch == "}" || ch == "]" { depth = depth - 1 }
          else if ch == "," && depth == 0 {
            local part = array_json.substring(start, i)
            out.push(part)
            start = i + 1
          }
          i = i + 1
        }
      }
    }
    if start < n - 1 {
      out.push(array_json.substring(start, n - 1))
    }
    return out
  }

  // Unescape JSON string (convert \n, \t, \r, \\, \" to actual characters)
  unescape_string(s) {
    if s == null { return "" }
    local out = ""
    local i = 0
    local n = s.length()
    loop(i < n) {
      local ch = s.substring(i, i + 1)
      if ch == "\\" && i + 1 < n {
        local nx = s.substring(i + 1, i + 2)
        if nx == "\"" {
          out = out + "\""
          i = i + 2
        } else {
          if nx == "\\" {
            out = out + "\\"
            i = i + 2
          } else {
            if nx == "n" {
              out = out + "\n"
              i = i + 2
            } else {
              if nx == "r" {
                out = out + "\r"
                i = i + 2
              } else {
                if nx == "t" {
                  out = out + "\t"
                  i = i + 2
                } else {
                  out = out + ch
                  i = i + 1
                }
              }
            }
          }
        }
      } else {
        out = out + ch
        i = i + 1
      }
    }
    return out
  }
}
