#ifndef READ_TABLE_HPP
#define READ_TABLE_HPP

#include <mesh/CellType.hpp>
#include <mesh/ItemId.hpp>
#include <utils/HighFivePugsUtils.hpp>
#include <utils/Messenger.hpp>
#include <utils/Table.hpp>

namespace checkpointing
{

template <typename DataType>
PUGS_INLINE Table<DataType>
readTable(const HighFive::Group& group, const std::string& name)
{
  auto get_address = [](auto& t) { return (t.numberOfRows() * t.numberOfColumns() > 0) ? &(t(0, 0)) : nullptr; };

  using data_type = std::remove_const_t<DataType>;

  auto dataset = group.getDataSet(name);

  const size_t number_of_columns = dataset.getAttribute("number_of_columns").read<size_t>();
  const std::vector<size_t> number_of_rows_per_rank =
    dataset.getAttribute("number_of_rows_per_rank").read<std::vector<size_t>>();

  std::vector<size_t> offset = {0, 0ul};
  for (size_t i = 0; i < parallel::rank(); ++i) {
    offset[0] += number_of_rows_per_rank[i] * number_of_columns;   // LCOV_EXCL_LINE
  }
  std::vector<size_t> count = {number_of_rows_per_rank[parallel::rank()] * number_of_columns};

  Table<DataType> table(number_of_rows_per_rank[parallel::rank()], number_of_columns);
  if constexpr (std::is_same_v<CellType, data_type>) {
    using base_type = std::underlying_type_t<CellType>;
    dataset.select(offset, count).read_raw(reinterpret_cast<base_type*>(get_address(table)));
  } else if constexpr ((std::is_same_v<CellId, data_type>) or (std::is_same_v<FaceId, data_type>) or
                       (std::is_same_v<EdgeId, data_type>) or (std::is_same_v<NodeId, data_type>)) {
    using base_type = typename data_type::base_type;
    dataset.select(offset, count).read_raw<base_type>(reinterpret_cast<base_type*>(get_address(table)));
  } else {
    dataset.select(offset, count).read_raw<data_type>(get_address(table));
  }

  return table;
}

}   // namespace checkpointing

#endif   // READ_TABLE_HPP