#include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_predicate.hpp>

#include <utils/pugs_config.hpp>

#include <dev/ParallelChecker.hpp>
#include <language/ast/ASTBuilder.hpp>
#include <language/ast/ASTExecutionStack.hpp>
#include <language/ast/ASTModulesImporter.hpp>
#include <language/ast/ASTNodeDataTypeBuilder.hpp>
#include <language/ast/ASTNodeDeclarationToAffectationConverter.hpp>
#include <language/ast/ASTNodeExpressionBuilder.hpp>
#include <language/ast/ASTNodeTypeCleaner.hpp>
#include <language/ast/ASTSymbolTableBuilder.hpp>
#include <language/modules/MathModule.hpp>
#include <language/utils/ASTCheckpointsInfo.hpp>
#include <language/utils/CheckpointResumeRepository.hpp>
#include <utils/ExecutionStatManager.hpp>

class ASTCheckpointsInfoTester
{
 private:
  ASTCheckpointsInfo m_ast_checkpoint_info;

 public:
  ASTCheckpointsInfoTester(const ASTNode& root_node) : m_ast_checkpoint_info(root_node) {}
  ~ASTCheckpointsInfoTester() = default;
};

// clazy:excludeall=non-pod-global-static

TEST_CASE("ASTCheckpointsInfo", "[utils/checkpointing]")
{
#ifndef NDEBUG
  REQUIRE_THROWS_WITH(ASTCheckpointsInfo::getInstance(), "ASTCheckpointsInfo is not defined!");
#endif   // NDEBUG

  std::string data = R"(
for(let i:N, i=0; i<7; ++i) {
  checkpoint();

  if (i == 2) {
    checkpoint();
  }

  if (i == 5) {
    checkpoint_and_exit();
  }
}
)";

  ExecutionStatManager::create();

  TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
  auto ast = ASTBuilder::build(input);

  ASTModulesImporter{*ast};
  ASTNodeTypeCleaner<language::import_instruction>{*ast};

  ASTSymbolTableBuilder{*ast};
  ASTNodeDataTypeBuilder{*ast};

  ASTNodeDeclarationToAffectationConverter{*ast};
  ASTNodeTypeCleaner<language::var_declaration>{*ast};
  ASTNodeTypeCleaner<language::fct_declaration>{*ast};

  ASTNodeExpressionBuilder{*ast};
  ExecutionPolicy exec_policy;
  ASTExecutionStack::create();
  ASTCheckpointsInfoTester ast_cp_info_tester{*ast};
  ASTExecutionStack::destroy();
  ExecutionStatManager::destroy();
  ast->m_symbol_table->clearValues();

  REQUIRE(ASTCheckpointsInfo::getInstance().getASTCheckpoint(0).getASTLocation() == std::vector<size_t>{0, 3, 0});
  REQUIRE(ASTCheckpointsInfo::getInstance().getASTCheckpoint(1).getASTLocation() == std::vector<size_t>{0, 3, 1, 1});
  REQUIRE(ASTCheckpointsInfo::getInstance().getASTCheckpoint(2).getASTLocation() == std::vector<size_t>{0, 3, 2, 1});

#ifndef NDEBUG
  REQUIRE_THROWS_WITH(ASTCheckpointsInfoTester(*ast), "Can only define one ASTCheckpointsInfo");
#endif   // NDEBUG
}