struct Byte_Range {
  u64 start, len;
  inline u64 end() const { return start + len; }
};

// Used to store location information about stuff like checksums, page lists, etc
struct Range_Seq {
  Range_Seq *next;
  Byte_Range range;
};

struct Page_Info_Node {
  Page_Info_Node *next;
  Page_Info_Node *prev;

  Byte_Range range; // len includes checksum
  i32 n_elems; // negative = page has checksum
  u32 cluster_id;
  b8 is_first_in_cluster;

  u64 page_id;

  u8 bits_per_elem;
  String8 elem_type_name;
  String8 owner_field_name;

  inline u64 checksum_size() const {
    return (n_elems < 0) * 8;
  }
};

static const Page_Info_Node invalid_pinfo {};

struct Page_Info_Group {
  Byte_Range range;
  Page_Info_Node *first;
};

struct Page_Info_Chunk {
  Page_Info_Chunk *next;
  Byte_Range range;
  u32 first_group;
};

struct Cluster_Info {
  Page_Info_Node *first_page;
  u64 first_page_idx; // index in the pages linked list
};

struct Cluster_Group_Info {
  Byte_Range rng_page_list;
};

// A Section represents an interesting sequence of bytes in the file.
// It is associated to stuff such as a color, some metadata, etc.
enum Section_Id {
  Sec_None,
  Sec_TFile_Header,
  Sec_TFile_Object,
  Sec_TFile_Info,
  Sec_TFile_FreeList,
  Sec_TKey_List,
  Sec_RNTuple_Anchor,
  Sec_RNTuple_Header,
  Sec_RNTuple_Footer,
  Sec_Page_List,
  Sec_Page, // keep last, or change logic in find_section()

  Sec_COUNT
};

struct Section {
  Section_Id id;
  Byte_Range range;
  u64 pre_size;  // usually the TKey header, excluded from `range`
  u64 post_size; // usually the checksum, included in `range`
  b8 highlighted;

  // Optional pointer to the specific information for the section.
  // e.g. if the section is a Page this points to the relative Page_Info_Node
  const void *info;
};

internal const String8 section_names[Sec_COUNT] = {
  str8("None"),
  str8("TFile Header"),
  str8("TFile Object"),
  str8("TFile Streamer Info"),
  str8("TFile FreeList"),
  str8("TKey List"),
  str8("RNTuple Anchor"),
  str8("RNTuple Header"),
  str8("RNTuple Footer"),
  str8("Page List"),
  str8("Page"),
};

struct Byte_Range_Node {
  Byte_Range_Node *next;
  Byte_Range rng;
};

struct RNTuple_Anchor_Info {
  RNTuple_Anchor_Info *next;
  String8 name;
  u64 offset_in_file;
};

struct TKeys_Data {
  Section sections[Sec_COUNT];

  RNTuple_Anchor_Info *rntuples;
  Byte_Range_Node *rblob_keys;
};

struct TFile_Data {
  u16 root_version_major;
  u16 root_version_minor;
  u16 root_version_patch;
  u32 compression;

  TKeys_Data tkeys_data;
  ROOT::Experimental::RNTuple rntuple_anchor;
};

struct RNTuple_Data {
  Page_Info_Node *pages;
  u64 n_pages;
  // total number of elements of all pages
  u64 n_elems;
  u64 tot_page_comp_size;
  u64 tot_page_uncomp_size;

  // TODO
  Range_Seq *checksums;
  u64        n_checksums;

  Cluster_Group_Info *cluster_groups;
  u64 n_cluster_groups;
  u64 tot_page_list_size;

  Cluster_Info *clusters;
  u64 n_clusters;
  // total number of entries of all clusters
  u64 n_entries;

  Page_Info_Group *page_groups;
  u64 n_page_groups;

  // grouping of consecutive pages
  Page_Info_Chunk *page_chunks;
  u64 n_page_chunks;
};