///|
/// Kind of subkey list record stored in a hive.
pub(all) enum SubkeyListKind {
  // lf: leaf list, 8-byte entries of key offset plus a name hint
  FastLeaf
  // lh: hash leaf list, same layout as lf but the hint is a real hash
  HashLeaf
  // li: index list, 4-byte key offsets
  IndexLeaf
  // ri: root index, 4-byte offsets of nested lf/lh/li lists
  RootIndex
  // Anything else, keeping the raw signature for diagnostics
  Unknown(String)
}

///|
/// Short label for a subkey list kind.
pub fn SubkeyListKind::label(self : SubkeyListKind) -> String {
  match self {
    FastLeaf => "lf"
    HashLeaf => "lh"
    IndexLeaf => "li"
    RootIndex => "ri"
    Unknown(name) => name
  }
}

///|
/// A subkey list record: the direct children of one key.
///
/// For root index (ri) records the child offsets of the nested lists are
/// flattened, so key_offsets always holds key node offsets in stored order.
pub(all) struct HiveSubkeyList {
  offset : UInt32
  kind : SubkeyListKind
  count : Int32
  key_offsets : Array[UInt32]
}

///|
/// One entry of a flattened key tree.
pub(all) struct HiveKeyEntry {
  path : String
  offset : UInt32
  subkey_count : Int32
  value_count : Int32
}

///|
/// Renders the two bytes at start as lowercase hex, for diagnostics.
fn signature_hex_at(bytes : Bytes, start : Int32) -> String {
  "0x" +
  bytes[start].to_uint().to_string(radix=16) +
  bytes[start + 1].to_uint().to_string(radix=16)
}

///|
/// Reads the subkey list stored at offset, expanding nested ri records.
pub fn read_subkey_list(
  bytes : Bytes,
  offset : UInt32,
) -> Result[HiveSubkeyList, RegfError] {
  read_subkey_list_at(bytes, offset, 0)
}

///|
/// Recursive worker for read_subkey_list; depth guards nested ri records.
fn read_subkey_list_at(
  bytes : Bytes,
  offset : UInt32,
  depth : Int32,
) -> Result[HiveSubkeyList, RegfError] {
  if depth > 8 {
    return Err(BadSubkeyList(offset.reinterpret_as_int()))
  }
  let cell = match read_cell(bytes, offset) {
    Err(error) => return Err(error)
    Ok(value) => value
  }
  let relative = cell.offset.reinterpret_as_int()
  if cell.payload_size < 4 {
    return Err(BadSubkeyList(relative))
  }
  let start = hive_bins_base + relative + 4
  let first = bytes[start].to_int()
  let second = bytes[start + 1].to_int()
  let mut kind : SubkeyListKind = Unknown(signature_hex_at(bytes, start))
  let mut width = 0
  let mut is_root = false
  if first == 0x6C && second == 0x66 {
    kind = FastLeaf
    width = 8
  } else if first == 0x6C && second == 0x68 {
    kind = HashLeaf
    width = 8
  } else if first == 0x6C && second == 0x69 {
    kind = IndexLeaf
    width = 4
  } else if first == 0x72 && second == 0x69 {
    kind = RootIndex
    width = 4
    is_root = true
  }
  if width == 0 {
    return Err(BadSubkeyList(relative))
  }
  let count = read_u16_le(bytes, start + 2).reinterpret_as_int()
  if 4 + count * width > cell.payload_size {
    return Err(BadSubkeyList(relative))
  }
  let key_offsets : Array[UInt32] = []
  let mut index = 0
  while index < count {
    let raw = read_u32_le(bytes, start + 4 + index * width)
    if is_root {
      match read_subkey_list_at(bytes, raw, depth + 1) {
        Err(error) => return Err(error)
        Ok(nested) => {
          let mut inner = 0
          while inner < nested.key_offsets.length() {
            key_offsets.push(nested.key_offsets[inner])
            inner = inner + 1
          }
        }
      }
    } else {
      key_offsets.push(raw)
    }
    index = index + 1
  }
  let list : HiveSubkeyList = { offset, kind, count, key_offsets, }
  Ok(list)
}

///|
/// Reads the direct children of a key.
///
/// This is the strict view: any unreadable child record fails the whole call.
pub fn list_subkeys(
  bytes : Bytes,
  key : HiveKeyNode,
) -> Result[Array[HiveKeyNode], RegfError] {
  let children : Array[HiveKeyNode] = []
  if key.subkey_count.reinterpret_as_int() <= 0 {
    return Ok(children)
  }
  if key.subkey_list_offset.reinterpret_as_int() == -1 {
    return Ok(children)
  }
  let offsets = match read_subkey_list(bytes, key.subkey_list_offset) {
    Err(error) => return Err(error)
    Ok(list) => list.key_offsets
  }
  let mut index = 0
  while index < offsets.length() {
    match parse_key_node(bytes, offsets[index]) {
      Err(error) => return Err(error)
      Ok(child) => children.push(child)
    }
    index = index + 1
  }
  Ok(children)
}

///|
/// Walks the key tree rooted at root_offset, descending max_depth levels.
///
/// The lenient view used for offline hives: children whose records cannot be
/// parsed are skipped, because hives can reference volatile or freed cells
/// that are absent from the file on disk.
pub fn walk_key_tree(
  bytes : Bytes,
  root_offset : UInt32,
  max_depth : Int32,
) -> Array[HiveKeyEntry] {
  let entries : Array[HiveKeyEntry] = []
  match parse_key_node(bytes, root_offset) {
    Err(_) => ()
    Ok(root) => walk_into(bytes, root, root.name, 0, max_depth, entries)
  }
  entries
}

///|
/// Depth first worker for walk_key_tree.
fn walk_into(
  bytes : Bytes,
  key : HiveKeyNode,
  path : String,
  depth : Int32,
  max_depth : Int32,
  entries : Array[HiveKeyEntry],
) -> Unit {
  let entry : HiveKeyEntry = {
    path,
    offset: key.offset,
    subkey_count: key.subkey_count.reinterpret_as_int(),
    value_count: key.value_count.reinterpret_as_int(),
  }
  entries.push(entry)
  if depth >= max_depth {
    return
  }
  if key.subkey_count.reinterpret_as_int() <= 0 {
    return
  }
  if key.subkey_list_offset.reinterpret_as_int() == -1 {
    return
  }
  let offsets = match read_subkey_list(bytes, key.subkey_list_offset) {
    Err(_) => return
    Ok(list) => list.key_offsets
  }
  let mut index = 0
  while index < offsets.length() {
    match parse_key_node(bytes, offsets[index]) {
      Err(_) => ()
      Ok(child) => {
        let child_path = if path == "" {
          child.name
        } else {
          path + "/" + child.name
        }
        walk_into(bytes, child, child_path, depth + 1, max_depth, entries)
      }
    }
    index = index + 1
  }
}