///|
/// Reftable stack management: tables.list, loading, adding, compacting.

///|
/// Read the tables.list file and return table filenames.
pub fn read_tables_list(
  fs : &@bit.RepoFileSystem,
  git_dir : String,
) -> Array[String] raise @object.GitError {
  let path = git_dir + "/reftable/tables.list"
  if !fs.is_file(path) {
    return []
  }
  let data = fs.read_file(path)
  let content = bytes_to_string(data, 0, data.length())
  split_lines(content)
}

///|
fn split_lines(s : String) -> Array[String] {
  let lines : Array[String] = []
  let buf = StringBuilder::new()
  for i = 0; i < s.length(); i = i + 1 {
    let ch = s[i].to_int()
    if ch == 0x0a {
      // '\n'
      let line = buf.to_string()
      if line.length() > 0 {
        lines.push(line)
      }
      buf.reset()
    } else {
      buf.write_char(Int::unsafe_to_char(ch))
    }
  }
  let last = buf.to_string()
  if last.length() > 0 {
    lines.push(last)
  }
  lines
}

///|
/// Load all reftable files referenced by tables.list.
pub fn load_tables(
  fs : &@bit.RepoFileSystem,
  git_dir : String,
) -> Array[Bytes] raise @object.GitError {
  let names = read_tables_list(fs, git_dir)
  let tables : Array[Bytes] = []
  let reftable_dir = git_dir + "/reftable"
  for name in names {
    let path = reftable_dir + "/" + name
    if fs.is_file(path) {
      tables.push(fs.read_file(path))
    }
  }
  tables
}

///|
/// Resolve a ref through stacked reftable tables, following symrefs.
pub fn resolve_ref_reftable(
  fs : &@bit.RepoFileSystem,
  git_dir : String,
  refname : String,
) -> @object.ObjectId? raise @object.GitError {
  let tables = load_tables(fs, git_dir)
  resolve_ref_from_tables(tables, refname, 0)
}

///|
/// Internal resolve with depth limit (max 8 for symref chains).
fn resolve_ref_from_tables(
  tables : Array[Bytes],
  refname : String,
  depth : Int,
) -> @object.ObjectId? {
  if depth > 8 {
    return None
  }
  match lookup_ref_in_stack(tables, refname) {
    Some(rec) =>
      match rec.value {
        Val1(oid) => Some(oid)
        Val2(oid, _) => Some(oid)
        Symref(target) => resolve_ref_from_tables(tables, target, depth + 1)
        Deletion => None
      }
    None => None
  }
}

///|
/// Collect all refs from reftable (for show-ref, list-refs).
pub fn collect_reftable_refs(
  fs : &@bit.RepoFileSystem,
  git_dir : String,
) -> Array[(String, @object.ObjectId)] raise @object.GitError {
  let tables = load_tables(fs, git_dir)
  let refs = iter_refs(tables, "refs/")
  let result : Array[(String, @object.ObjectId)] = []
  for r in refs {
    match r.value {
      Val1(oid) => result.push((r.refname, oid))
      Val2(oid, _) => result.push((r.refname, oid))
      Symref(target) =>
        match resolve_ref_from_tables(tables, target, 0) {
          Some(oid) => result.push((r.refname, oid))
          None => ()
        }
      Deletion => ()
    }
  }
  result
}