#ifndef CHECKPOINT_RESUME_REPOSITORY_HPP
#define CHECKPOINT_RESUME_REPOSITORY_HPP

#include <language/utils/SymbolTable.hpp>
#include <utils/Exceptions.hpp>
#include <utils/HighFivePugsUtils.hpp>
#include <utils/PugsAssert.hpp>
#include <utils/PugsUtils.hpp>
#include <utils/pugs_build_info.hpp>

#include <string>
#include <unordered_map>

class CheckpointResumeRepository
{
 public:
  using CheckpointFunction = std::function<void(const std::string& symbol_name,
                                                const EmbeddedData& embedded_data,
                                                HighFive::File& file,
                                                HighFive::Group& checkpoint_group,
                                                HighFive::Group& symbol_table_group)>;

  using ResumeFunction =
    std::function<EmbeddedData(const std::string& symbol_name, const HighFive::Group& symbol_table_group)>;

 private:
  std::unordered_map<std::string, CheckpointFunction> m_data_type_checkpointing;
  std::unordered_map<std::string, ResumeFunction> m_data_type_resuming;

 public:
  void
  addCheckpointResume(const ASTNodeDataType& ast_node_data_type,
                      CheckpointFunction&& checkpoint_function,
                      ResumeFunction&& resume_function)
  {
    const std::string& data_type_name = dataTypeName(ast_node_data_type);
    {
      const auto [i, inserted] =
        m_data_type_checkpointing.insert({data_type_name, std::forward<CheckpointFunction>(checkpoint_function)});
      if (not(inserted)) {
        // LCOV_EXCL_START
        std::ostringstream error_msg;
        error_msg << "checkpointing for type '" << rang::fgB::yellow << data_type_name << rang::fg::reset
                  << "' has already be defined";
        throw UnexpectedError(error_msg.str());
        // LCOV_EXCL_STOP
      }
    }
    {
      const auto [i, inserted] =
        m_data_type_resuming.insert({data_type_name, std::forward<ResumeFunction>(resume_function)});
      Assert(inserted);
    }
  }

#ifdef PUGS_HAS_HDF5
  void checkpoint(const ASTNodeDataType& data_type,
                  const std::string& symbol_name,
                  const EmbeddedData& embedded_data,
                  HighFive::File& file,
                  HighFive::Group& checkpoint_group,
                  HighFive::Group& symbol_table_group) const;

  EmbeddedData resume(const ASTNodeDataType& data_type,
                      const std::string& symbol_name,
                      const HighFive::Group& symbol_table_group) const;
#endif   // PUGS_HAS_HDF5

  static void create();

  static bool
  isCreated()
  {
    return m_instance != nullptr;
  }

  PUGS_INLINE
  static CheckpointResumeRepository&
  instance()
  {
    Assert(m_instance != nullptr);
    return *m_instance;
  }

  static void destroy();

 private:
  static CheckpointResumeRepository* m_instance;

  CheckpointResumeRepository() = default;

  ~CheckpointResumeRepository() = default;
};

#endif   // CHECKPOINT_RESUME_REPOSITORY_HPP