// FuncScannerBox — Function definition scanner for Stage-B compiler
// Policy: Extract method definitions from Hako source (conservative, minimal)
// Toggle: HAKO_STAGEB_FUNC_SCAN=1
// Scope: method <name>(params) { ... } outside of main (same box Main)
// Output: [{"name":"<name>","params":[...],"body_json":"<Program JSON>","box":"Main"}]

using "lang.compiler.parser.box" as ParserBox

static box FuncScannerBox {
  // Scan source for method definitions (excluding Main.main)
  method scan_functions(source, box_name) {
    return me.scan_methods(source, box_name, 1, 0)
  }

  // Scan all static box definitions and collect their methods.
  method scan_all_boxes(source) {
    // source が null の場合はスキャン対象がないので空配列を返すよ。
    if source == null { return new ArrayBox() }
    local defs = new ArrayBox()
    local s = "" + source
    local n = s.length()
    local i = 0
    // ────────────────────────────────────────────────────────────
    // State flags: コメント/文字列スキップ状態管理（4本）
    // ────────────────────────────────────────────────────────────
    // in_str:   文字列リテラル内（"..." の中）= 1, それ以外 = 0
    // esc:      文字列内エスケープ直後（次の文字をスキップ）= 1, それ以外 = 0
    // in_line:  行コメント内（// から改行まで）= 1, それ以外 = 0
    // in_block: ブロックコメント内（/* から */ まで）= 1, それ以外 = 0
    local in_str = 0
    local esc = 0
    local in_line = 0
    local in_block = 0

    // Region + next_i 形式に統一して、ループ本体での多重 continue による
    // SSA/PHI の複雑さを下げる。
    loop(i < n) {
      local next_i = i + 1
      local ch = s.substring(i, i + 1)

      // ────────────────────────────────────────────────────────────
      // State 1: 行コメントモード（// から改行まで）
      // ────────────────────────────────────────────────────────────
      if in_line == 1 {
        if ch == "\n" { in_line = 0 }  // 改行で行コメント終了
      // ────────────────────────────────────────────────────────────
      // State 2: ブロックコメントモード（/* から */ まで）
      // ────────────────────────────────────────────────────────────
      } else if in_block == 1 {
        if ch == "*" && i + 1 < n && s.substring(i + 1, i + 2) == "/" {
          in_block = 0  // */ でブロックコメント終了
          next_i = i + 2
        }
      // ────────────────────────────────────────────────────────────
      // State 3: 文字列リテラルモード（"..." の中）
      // ────────────────────────────────────────────────────────────
      } else if in_str == 1 {
        if esc == 1 {
          esc = 0  // エスケープ直後文字処理完了
        } else if ch == "\\" {
          esc = 1  // バックスラッシュでエスケープ開始
        } else if ch == "\"" {
          in_str = 0  // 閉じクォートで文字列終了
        }
      // ────────────────────────────────────────────────────────────
      // State 4: 通常モード（コメント/文字列の外）→ box キーワード検出
      // ────────────────────────────────────────────────────────────
      } else {
        // 文字列リテラル開始（" 検出）
        if ch == "\"" {
          in_str = 1  // 文字列モードへ遷移
        // コメント開始判定（/ の次の文字で行/ブロック判別）
        } else if ch == "/" && i + 1 < n {
          local ch2 = s.substring(i + 1, i + 2)
          if ch2 == "/" {
            in_line = 1  // 行コメントモードへ遷移（// 検出）
            next_i = i + 2
          } else if ch2 == "*" {
            in_block = 1  // ブロックコメントモードへ遷移（/* 検出）
            next_i = i + 2
          }
        // box キーワード検出（境界チェック付き）
        } else {
          if i + 3 <= n && s.substring(i, i + 3) == "box" && FuncScannerBox.kw_boundary_before(s, i) == 1 && FuncScannerBox.kw_boundary_after(s, i + 3) == 1 {
            local cursor = i + 3
            print("[DEBUG] box keyword found at i=" + ("" + i))
            cursor = FuncScannerBox.skip_whitespace(s, cursor)
            print("[DEBUG] after skip_whitespace: cursor=" + ("" + cursor))
            local name_start = cursor
            loop(cursor < n) {
              local ch_name = s.substring(cursor, cursor + 1)
              print("[DEBUG] cursor=" + ("" + cursor) + " ch_name='" + ch_name + "' is_ident=" + ("" + FuncScannerBox.is_ident_char(ch_name)))
              if FuncScannerBox.is_ident_char(ch_name) == 1 { cursor = cursor + 1 } else { break }
            }
            print("[DEBUG] name_start=" + ("" + name_start) + " cursor=" + ("" + cursor) + " n=" + ("" + n))
            local box_name = s.substring(name_start, cursor)
            print("[DEBUG] box_name='" + box_name + "' len=" + ("" + box_name.length()))
            if box_name == "" {
              next_i = cursor
            } else {
              cursor = FuncScannerBox.skip_whitespace(s, cursor)
              if cursor >= n || s.substring(cursor, cursor + 1) != "{" {
                next_i = cursor
              } else {
                local close_idx = FuncScannerBox.find_matching_brace(s, cursor)
                if close_idx < 0 {
                  // マッチしない場合はこれ以上スキャンしても意味がないのでループ終了。
                  i = n
                  next_i = n
                } else {
                  local body = s.substring(cursor + 1, close_idx)
                  local skip_main = 0
                  if box_name == "Main" { skip_main = 1 }
                  local include_me = 0
                  if box_name != "Main" { include_me = 1 }
                  local defs_box = me._scan_methods(body, box_name, skip_main, include_me)
                  me._append_defs(defs, defs_box)
                  next_i = close_idx + 1
                }
              }
            }
          }
        }
      }

      i = next_i
    }
    return defs
  }

  method _scan_methods(source, box_name, skip_main, include_me) {
    local methods = new ArrayBox()
    local s = "" + source
    local n = s.length()
    local i = 0

    loop(i < n) {
      // Search for "method "
      local k = -1
      {
        local pat = "method "
        local m = pat.length()
        local j = i
        loop(j + m <= n) {
          if s.substring(j, j + m) == pat { k = j  break }
          j = j + 1
        }
      }
      if k < 0 { break }
      i = k + 7

      // Extract method name (alphanumeric until '(')
      local name_start = i
      local name_end = -1
      {
        local j = i
        loop(j < n) {
          local ch = s.substring(j, j + 1)
          if ch == "(" { name_end = j  break }
          j = j + 1
        }
      }
      if name_end < 0 { break }
      local method_name = s.substring(name_start, name_end)

      if skip_main == 1 && box_name == "Main" && method_name == "main" { i = name_end  continue }

      // Find '(' after name
      local lparen = name_end

      // Find matching ')' for params (skip strings)
      local rparen = -1
      {
        local j = lparen + 1
        local in_str = 0
        local esc = 0
        loop(j < n) {
          local ch = s.substring(j, j + 1)
          if in_str == 1 {
            if esc == 1 { esc = 0  j = j + 1  continue }
            if ch == "\\" { esc = 1  j = j + 1  continue }
            if ch == "\"" { in_str = 0  j = j + 1  continue }
            j = j + 1
            continue
          }
          if ch == "\"" { in_str = 1  j = j + 1  continue }
          if ch == ")" { rparen = j  break }
          j = j + 1
        }
      }
      if rparen < 0 { break }

      // Extract params (minimal: comma-separated names)
      local params_str = s.substring(lparen + 1, rparen)
      // static helper として扱う（me ではなく FuncScannerBox に直接委譲）。
      local params = FuncScannerBox._parse_params(params_str)
      if include_me == 1 {
        local first = ""
        if params.length() > 0 { first = "" + params.get(0) }
        if first != "me" {
          local merged = new ArrayBox()
          merged.push("me")
          local pi2 = 0
          local pn2 = params.length()
          loop(pi2 < pn2) {
            merged.push(params.get(pi2))
            pi2 = pi2 + 1
          }
          params = merged
        }
      }

      // Find opening '{' after ')'
      local lbrace = -1
      {
        local j = rparen + 1
        local in_str = 0
        local esc = 0
        loop(j < n) {
          local ch = s.substring(j, j + 1)
          if in_str == 1 {
            if esc == 1 { esc = 0  j = j + 1  continue }
            if ch == "\\" { esc = 1  j = j + 1  continue }
            if ch == "\"" { in_str = 0  j = j + 1  continue }
            j = j + 1
            continue
          }
          if ch == "\"" { in_str = 1  j = j + 1  continue }
          if ch == "{" { lbrace = j  break }
          j = j + 1
        }
      }
      if lbrace < 0 { break }

      // Find matching '}' (balanced)
      local rbrace = -1
      {
        local depth = 0
        local j = lbrace
        local in_str = 0
        local esc = 0
        loop(j < n) {
          local ch = s.substring(j, j + 1)
          if in_str == 1 {
            if esc == 1 { esc = 0  j = j + 1  continue }
            if ch == "\\" { esc = 1  j = j + 1  continue }
            if ch == "\"" { in_str = 0  j = j + 1  continue }
            j = j + 1
            continue
          }
          if ch == "\"" { in_str = 1  j = j + 1  continue }
          if ch == "{" { depth = depth + 1  j = j + 1  continue }
          if ch == "}" {
            depth = depth - 1
            j = j + 1
            if depth == 0 { rbrace = j - 1  break }
            continue
          }
          j = j + 1
        }
      }
      if rbrace < 0 { break }

      // Extract method body (inside braces)
      local method_body = s.substring(lbrace + 1, rbrace)

      // コメント除去と trim も static helper として扱う。
      method_body = FuncScannerBox._strip_comments(method_body)
      method_body = FuncScannerBox._trim(method_body)

      // Parse method body to JSON (statement list)
      local body_json = null
      if method_body.length() > 0 {
        local p = new ParserBox()
        p.stage3_enable(1)
        body_json = p.parse_program2(method_body)
      }

      if body_json != null && body_json != "" {
        local def = new MapBox()
        def.set("name", method_name)
        def.set("params", params)
        def.set("body_json", body_json)
        def.set("box", box_name)
        methods.push(def)
      }
      i = rbrace
    }

    return methods
  }

  method _append_defs(dst, defs_box) {
    if defs_box == null { return }
    local i = 0
    loop(i < defs_box.length()) {
      dst.push(defs_box.get(i))
      i = i + 1
    }
  }

  // ────────────────────────────────────────────────────────────
  // Core Text Scanning Helpers (SSOT: single source of truth)
  // Stage-B および他の箱からも参照される共通処理群
  // ────────────────────────────────────────────────────────────

  // Helper: 空白文字（space/tab/newline/CR）を idx 位置からスキップ
  // 戻り値: スキップ後の位置（空白でない文字の位置、または文字列末尾）
  method skip_whitespace(s, idx) {
    // Minimal, SSA-friendly whitespace skipper.
    __mir__.log("skip_ws/head", idx, s.length())
    local i = idx
    local n = s.length()
    loop(i < n) {
      local ch = s.substring(i, i + 1)
      if ch == " " { i = i + 1  continue }
      if ch == "\t" { i = i + 1  continue }
      if ch == "\n" { i = i + 1  continue }
      if ch == "\r" { i = i + 1  continue }
      break
    }
    __mir__.log("skip_ws/exit", i, n)
    return i
  }

  // Helper: キーワード前の境界チェック（"box" などが識別子の一部でないことを確認）
  // 戻り値: 1=境界OK（キーワードとして有効）, 0=境界NG（識別子の一部）
  method kw_boundary_before(s, idx) {
    print("🎯 SENTINEL_KW_BOUNDARY_BEFORE_CALLED!!!")
    if idx <= 0 { return 1 }
    local ch = s.substring(idx - 1, idx)
    if FuncScannerBox.is_ident_char(ch) == 1 { return 0 }
    return 1
  }

  // Helper: キーワード後の境界チェック（"box" などが識別子の一部でないことを確認）
  // 戻り値: 1=境界OK（キーワードとして有効）, 0=境界NG（識別子の一部）
  method kw_boundary_after(s, idx) {
    print("🎯 SENTINEL_KW_BOUNDARY_AFTER_CALLED!!!")
    if idx >= s.length() { return 1 }
    local ch = s.substring(idx, idx + 1)
    if FuncScannerBox.is_ident_char(ch) == 1 { return 0 }
    return 1
  }

  // Helper: 識別子として有効な文字かチェック（0-9, A-Z, a-z, _）
  // 戻り値: 1=識別子文字, 0=非識別子文字
  method is_ident_char(ch) {
    print("🔤 SENTINEL_IS_IDENT_CHAR_CALLED!!!")
    if ch == null || ch == "" { return 0 }
    if ch >= "0" && ch <= "9" { return 1 }
    if ch >= "A" && ch <= "Z" { return 1 }
    if ch >= "a" && ch <= "z" { return 1 }
    if ch == "_" { return 1 }
    return 0
  }

  method find_matching_brace(s, open_idx) {
    local n = s.length()
    if open_idx < 0 || open_idx >= n { return -1 }
    // open_idx 以降だけを対象に走査することで、前方の構造に依存しないようにする。
    local subs = s.substring(open_idx, n)
    local m = subs.length()
    local i = 0
    local depth = 0
    local in_str = 0
    local esc = 0
    local in_line = 0
    local in_block = 0
    loop(i < m) {
      local ch = subs.substring(i, i + 1)
      if in_line == 1 {
        if ch == "\n" { in_line = 0 }
        i = i + 1
        continue
      }
      if in_block == 1 {
        if ch == "*" && i + 1 < n && s.substring(i + 1, i + 2) == "/" {
          in_block = 0
          i = i + 2
          continue
        }
        i = i + 1
        continue
      }
      if in_str == 1 {
        if esc == 1 { esc = 0  i = i + 1  continue }
        if ch == "\\" { esc = 1  i = i + 1  continue }
        if ch == "\"" { in_str = 0  i = i + 1  continue }
        i = i + 1
        continue
      }
      if ch == "\"" { in_str = 1  i = i + 1  continue }
      if ch == "/" && i + 1 < m {
        local ch2 = subs.substring(i + 1, i + 2)
        if ch2 == "/" { in_line = 1  i = i + 2  continue }
        if ch2 == "*" { in_block = 1  i = i + 2  continue }
      }
      if ch == "{" {
        depth = depth + 1
        i = i + 1
        continue
      }
      if ch == "}" {
        depth = depth - 1
        i = i + 1
        if depth == 0 { return open_idx + i - 1 }
        continue
      }
      i = i + 1
    }
    return -1
  }

  // Helper: カンマ区切りパラメータリストをパース（例: "a, b, c" → ["a", "b", "c"]）
  // FuncScannerBox._trim を使用（static helper パターン）
  // 戻り値: ArrayBox（トリム済みパラメータ名のリスト）
  method parse_params(params_str) {
    // NOTE: SSA/PHI が崩れにくいよう、1 ループ＋単純状態でスキャンする。
    if params_str == null { return new ArrayBox() }

    // 状態変数は params / cur / i のみを更新する。
    local params = new ArrayBox()
    local s = "" + params_str
    local n = s.length()
    local cur = ""
    local i = 0

    // Scan characters plus a sentinel comma at the end to reuse the same branch.
    loop(i <= n) {
      // When i == n we synthesize a trailing comma to flush the last token.
      local ch = ","
      if i < n { ch = s.substring(i, i + 1) }

      if ch == "," {
        // push current token if non-empty after trim, then reset
        // Use static helper to avoid capturing instance state (SSA-friendly).
        local tok = FuncScannerBox._trim(cur)
        if tok.length() > 0 { params.push(tok) }
        cur = ""
        i = i + 1
        continue
      } else {
        cur = cur + ch
      }
      i = i + 1
    }

    return params
  }

	  // Helper: "//" と "/* */" コメントを削除（文字列リテラル内は保持、改行は保持）
	  // 戻り値: コメント削除後のソースコード（null の場合は空文字）
	  method strip_comments(source) {
	    // source が null の場合はそのまま空文字として扱う（コメント除去する対象がないだけ）
	    if source == null { return "" }
	    local s = "" + source
    local out = ""
    local i = 0
    local n = s.length()
    local in_str = 0
    local esc = 0
    local in_line = 0
    local in_block = 0
    loop(i < n) {
      local ch = s.substring(i, i + 1)
      if in_line == 1 {
        if ch == "\n" { in_line = 0  out = out + ch }
        i = i + 1
        continue
      }
      if in_block == 1 {
        if ch == "*" && i + 1 < n && s.substring(i + 1, i + 2) == "/" { in_block = 0  i = i + 2  continue }
        i = i + 1
        continue
      }
      if in_str == 1 {
        if esc == 1 { out = out + ch  esc = 0  i = i + 1  continue }
        if ch == "\\" { out = out + ch  esc = 1  i = i + 1  continue }
        if ch == "\"" { out = out + ch  in_str = 0  i = i + 1  continue }
        out = out + ch
        i = i + 1
        continue
      }
      if ch == "\"" { out = out + ch  in_str = 1  i = i + 1  continue }
      if ch == "/" && i + 1 < n {
        local ch2 = s.substring(i + 1, i + 2)
        if ch2 == "/" { in_line = 1  i = i + 2  continue }
        if ch2 == "*" { in_block = 1  i = i + 2  continue }
      }
      out = out + ch
      i = i + 1
    }
    return out
  }

  // Helper: 前後の空白文字（space/tab/newline/CR）を削除
  // 戻り値: トリム済み文字列（null の場合は空文字）
  method trim(s) {
    if s == null { return "" }
    __mir__.log("trim/entry", s)
    local str = "" + s
    local n = str.length()
    __mir__.log("trim/pre", n)

    // Leading whitespace removal is delegated to skip_whitespace to keep SSA simple.
    local b = FuncScannerBox.skip_whitespace(str, 0)
    if b >= n { return "" }

    // Trailing whitespace: walk backwards until a non-space is found.
    local e = n
    loop(e > b) {
      local ch = str.substring(e - 1, e)
      if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { e = e - 1 } else { break }
    }

    __mir__.log("trim/exit", b, e)
    if e > b { return str.substring(b, e) }
    return ""
  }

  // ────────────────────────────────────────────────────────────
  // Static Helper Aliases (external API with underscore prefix)
  // Stage-B および他の箱から static 呼び出しで使える薄いラッパー
  // ────────────────────────────────────────────────────────────

  // Static helper: パラメータリストパース（外部 API）
  method _parse_params(params_str) {
    return FuncScannerBox.parse_params(params_str)
  }

  // Static helper: コメント削除（外部 API）
  method _strip_comments(source) {
    return FuncScannerBox.strip_comments(source)
  }

  // Static helper: 前後空白削除（外部 API）
  method _trim(s) {
    // NOTE: keep in sync with trim/1. 静的 alias でも SSA が崩れないよう、本体を薄くインラインする。
    if s == null { return "" }

    local n = s.length()

    __mir__.log("trim/pre", n)

    // Leading whitespace removal is delegated to skip_whitespace to keep SSA simple.
    local b = FuncScannerBox.skip_whitespace(s, 0)
    if b >= n { return "" }

    // Trailing whitespace: walk backwards until a non-space is found.
    local e = n
    loop(e > b) {
      local ch = s.substring(e - 1, e)
      if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { e = e - 1 } else { break }
    }

    __mir__.log("trim/exit", b, e)
    if e > b { return s.substring(b, e) }
    return ""
  }
}
