///|
pub(all) enum PageType {
  TableLeaf
  TableInterior
  IndexLeaf
  IndexInterior
} derive(Debug, Eq)

///|
pub extend PageType with @debug.Debug::{to_repr}

///|
pub extend PageType with Eq::{equal, not_equal}

///|
pub(all) struct Page {
  number : Int
  kind : PageType
  first_freeblock : Int
  cell_count : Int
  content_start : Int
  fragmented_bytes : Int
  right_child : Int?
  cell_offsets : Array[Int]
} derive(Debug)

///|
pub extend Page with @debug.Debug::{to_repr}

///|
pub fn Database::page(self : Database, number : Int) -> Page raise SqliteError {
  self.page_from_bytes(number, self.read_page(number))
}

///|
fn Database::page_from_bytes(
  self : Database,
  number : Int,
  bytes : Bytes,
) -> Page raise SqliteError {
  let offset = if number == 1 { 100 } else { 0 }
  let kind = match bytes[offset].to_int() {
    13 => TableLeaf
    5 => TableInterior
    10 => IndexLeaf
    2 => IndexInterior
    other => raise Invalid("页 \{number} 不是 B-tree 页:type=\{other}")
  }
  let interior = kind == TableInterior || kind == IndexInterior
  let header_size = if interior { 12 } else { 8 }
  let cell_count = read_u16(bytes, offset + 3)
  let pointers_end = offset + header_size + cell_count * 2
  let usable = self.header.usable_size
  if pointers_end > usable {
    raise Invalid("cell pointer array 越界")
  }
  let raw_start = read_u16(bytes, offset + 5)
  let content_start = if raw_start == 0 { 65536 } else { raw_start }
  if content_start < pointers_end || content_start > usable {
    raise Invalid("无效 cell content 起点")
  }
  let fragmented_bytes = bytes[offset + 7].to_int()
  if fragmented_bytes > 60 {
    raise Invalid("碎片字节数超过 60")
  }
  let first_freeblock = read_u16(bytes, offset + 1)
  if first_freeblock != 0 &&
    (first_freeblock < content_start || first_freeblock > usable - 4) {
    raise Invalid("freeblock 指针越界")
  }
  let seen : Map[Int, Bool] = Map([])
  let cell_offsets : Array[Int] = []
  for i = 0; i < cell_count; i = i + 1 {
    let pointer = read_u16(bytes, offset + header_size + i * 2)
    if pointer < content_start || pointer >= usable || seen.contains(pointer) {
      raise Invalid("cell 指针越界或重复")
    }
    seen[pointer] = true
    cell_offsets.push(pointer)
  }
  // 空闲块必须按偏移递增且互不重叠;同时排除 cell 指针落入空闲块。
  let mut freeblock = first_freeblock
  while freeblock != 0 {
    if freeblock < content_start || freeblock > usable - 4 {
      raise Invalid("freeblock 链指针越界")
    }
    let next = read_u16(bytes, freeblock)
    let size = read_u16(bytes, freeblock + 2)
    if size < 4 || size > usable - freeblock {
      raise Invalid("freeblock 长度无效")
    }
    let block_end = freeblock + size
    for pointer in cell_offsets {
      if pointer >= freeblock && pointer < block_end {
        raise Invalid("cell 指针位于 freeblock 内")
      }
    }
    if next != 0 && next < block_end {
      raise Invalid("freeblock 链重叠或形成环")
    }
    freeblock = next
  }
  let right_child = if interior {
    let child = page_number(read_u32(bytes, offset + 8))
    if child < 1 || child > self.page_count {
      raise Invalid("右子页越界")
    }
    Some(child)
  } else {
    None
  }
  {
    number,
    kind,
    first_freeblock,
    cell_count,
    content_start,
    fragmented_bytes,
    right_child,
    cell_offsets,
  }
}