// So NTFS (and most systems) sort the tree by default in lexicographical descending order.
// For lookups, this often isn't that useful if you only know substrings of the path.


// struct BTNode {
//   u16 key_count;
//   BTNode* keys;
//   u16 allocated;
// };

// struct B_Tree {
//   BTNode* root;
//   Allocator allocator;
// };

// A compact collection of data with sorting indices
// Maybe we can make B+ trees for sorting according to 
// size and modtime. 

// It really doesn't make sense to store data in memory as a B-tree except 
// if we need ordered insertions and deletes.
// 

// Returns offset
force_inline u32 AddString_NoCount (Serializer* serializer, u8* data, u8 count) { // #TODO: , bool null_terminate=false
  u8* current_point = &serializer->data[serializer->count];
  s64 final_count = serializer->allocated + (count * sizeof(u8));
  
  if (serializer->allocated < final_count) {
    array_reserve(*serializer, final_count);
  }
  
  memcpy(current_point, data, count * sizeof(u8));
  serializer->count += count * sizeof(u8);
  
  return (u32)serializer->count;
}

constexpr s64 DFS_Preallocation_Count = 4194304; // 2^22

// template <typename Length_Type> 
struct DFS_Array {
  Serializer* wstrings;
  // Serializer* strings;
  
  ArenaArray<u32>* offsets; // offsets into strings->data
  ArenaArray<u8>*  lengths; // this type may vary <hmmm> Not sure if I should make it a template argument. Seems yucky.
  ArenaArray<u64>* modtimes;
  ArenaArray<u64>* sizes;
  
  ArenaArray<s32>* parent_indices;
  
  // #Temporary arrays for linking files/dirs to their parent directory, if present.
  ArenaArray<u32>* record_ids;
  ArenaArray<u32>* parent_ids;
};

s64 item_count (DFS_Array* dfsa) {
  return dfsa->offsets->count;
}

void initialize (DFS_Array* dfsa) {
  Assert(dfsa != nullptr);
  dfsa->wstrings = new_serializer(Arena_Reserve::Size_2G);
  // dfsa->strings = new_serializer(Arena_Reserve::Size_2G);
  
  dfsa->offsets  = arena_array_new<u32>(DFS_Preallocation_Count, Arena_Reserve::Size_2G);
  dfsa->lengths  = arena_array_new<u8> (DFS_Preallocation_Count, Arena_Reserve::Size_2G);
  dfsa->modtimes = arena_array_new<u64>(DFS_Preallocation_Count, Arena_Reserve::Size_2G);
  dfsa->sizes    = arena_array_new<u64>(DFS_Preallocation_Count, Arena_Reserve::Size_2G);
  
  dfsa->record_ids = arena_array_new<u32>(DFS_Preallocation_Count, Arena_Reserve::Size_2G);
  dfsa->parent_ids = arena_array_new<u32>(DFS_Preallocation_Count, Arena_Reserve::Size_2G);
  
  dfsa->parent_indices = arena_array_new<s32>(DFS_Preallocation_Count, Arena_Reserve::Size_2G);
}

void release (DFS_Array* dfsa) {
  free_serializer(dfsa->wstrings);

  if (is_valid(dfsa->offsets))  { arena_array_free(*dfsa->offsets);  }
  if (is_valid(dfsa->lengths))  { arena_array_free(*dfsa->lengths);  }
  if (is_valid(dfsa->modtimes)) { arena_array_free(*dfsa->modtimes); }
  if (is_valid(dfsa->sizes))    { arena_array_free(*dfsa->sizes);    }

  if (is_valid(dfsa->record_ids)) { arena_array_free(*dfsa->record_ids); }
  if (is_valid(dfsa->parent_ids)) { arena_array_free(*dfsa->parent_ids); }

  if (is_valid(dfsa->parent_indices)) { arena_array_free(*dfsa->parent_indices); }
  
  zero_struct(dfsa);
}

struct Dense_FS { // Link to OS_Drive
  DFS_Array paths;
  DFS_Array files;
  
  ArenaTable<u32, s32> path_table; // <entry_id, array_offset>.
  
  OS_Drive* drive; // backlink for reference. 
};

force_inline void release (Dense_FS* dfs) {
  Timed_Block_Print("release: Dense_FS*");
  dfs->drive = nullptr; // just a link  
  
  release(&dfs->paths);
  release(&dfs->files);
  
  if (table_is_valid(&dfs->path_table)) {
    table_release(&dfs->path_table);
  }
}

void initialize (Dense_FS* dfs, OS_Drive* drive) {
  Assert(drive != nullptr); Assert(dfs != nullptr);
  // Is there a less stupid way of doing this?
  dfs->drive = drive;
  drive->data = dfs;
  initialize(&dfs->paths);
  initialize(&dfs->files);
  
  table_init(&dfs->path_table, 1048576); // 2^20
  // dfs->path_table.hash_function = table_hash_function_fnv1a; // default.
  dfs->path_table.hash_function = sdbm_hash;
  dfs->path_table.compare_function = u32_keys_match;
}

s32 find_previous_index (Dense_FS* dfs, u32 record_id, bool* success) {
  s32 result = -1;

  (*success) = table_find(&dfs->path_table, record_id, &result);

  return result;
}

void cleanup_after_enumeration(Dense_FS* dfs) {
  table_release(&dfs->path_table);
  reset_struct(&dfs->path_table);
  
  arena_array_free(*dfs->paths.record_ids);
  arena_array_free(*dfs->paths.parent_ids);
  arena_array_free(*dfs->files.record_ids);
  arena_array_free(*dfs->files.parent_ids);
  
  // Unfortunately, we have to do this whenever we free, because we're using raw pointers.
  dfs->paths.record_ids = nullptr;
  dfs->paths.parent_ids = nullptr;
  dfs->files.record_ids = nullptr;
  dfs->files.parent_ids = nullptr;
}
