// ./tests/catch2-tests [section] -s


/////////////////////// Qt includes
#include <QDebug>
#include <QString>
#include <QDir>

/////////////////////// IsoSpec
#include <IsoSpec++/isoSpec++.h>
#include <IsoSpec++/element_tables.h>


/////////////////////// Catch2 includes
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>


/////////////////////// libXpertMassCore includes
#include "MsXpS/libXpertMassCore/Isotope.hpp"
#include "MsXpS/libXpertMassCore/IsotopicData.hpp"
#include "MsXpS/libXpertMassCore/IsotopicDataManualConfigHandler.hpp"


/////////////////////// Local includes
#include "tests-config.h"
#include "TestUtils.hpp"


namespace MsXpS
{
namespace libXpertMassCore
{

SCENARIO("Construction of an empty IsotopicDataManualConfigHandler",
         "[IsotopicDataManualConfigHandler]")
{
  TestUtils test_utils;

  test_utils.initializeXpertmassLibrary();

  QString isotopic_data_file_path =
    QString("%1/%2/%3")
      .arg(TESTS_INPUT_DIR)
      .arg("isotopes", test_utils.m_manualUserIsotopicDataFileName);

  GIVEN("An entirely empty IsotopicDataManualConfigHandler instance")
  {
    IsotopicDataManualConfigHandler isotopic_data_manual_config_handler;

    WHEN("Constructed like so")
    {
      THEN("The isotopic data are empty but they are allocated")
      {
        REQUIRE(isotopic_data_manual_config_handler.getIsotopicData() !=
                nullptr);
        REQUIRE(isotopic_data_manual_config_handler.getIsotopicData()->size() ==
                0);
        REQUIRE(
          isotopic_data_manual_config_handler.getFileName().toStdString() ==
          "");
      }

      AND_WHEN("The file name is set using the setter")
      {
        isotopic_data_manual_config_handler.setFileName(
          isotopic_data_file_path);

        THEN("The file name is set")
        {
          REQUIRE(
            isotopic_data_manual_config_handler.getFileName().toStdString() ==
            isotopic_data_file_path.toStdString());
        }
      }
    }
  }

  GIVEN(
    "An IsotopicDataManualConfigHandler instance constructed with aan empty "
    "file name")
  {
    IsotopicDataManualConfigHandler isotopic_data_manual_config_handler("");

    WHEN("Constructed like so")
    {
      THEN("The isotopic data are empty but they are allocated")
      {
        REQUIRE(isotopic_data_manual_config_handler.getIsotopicData() !=
                nullptr);
        REQUIRE(isotopic_data_manual_config_handler.getIsotopicData()->size() ==
                0);
        REQUIRE(
          isotopic_data_manual_config_handler.getFileName().toStdString() ==
          "");
      }

      AND_WHEN("The file name is set using the setter")
      {
        isotopic_data_manual_config_handler.setFileName(
          isotopic_data_file_path);

        THEN("The file name is set")
        {
          REQUIRE(
            isotopic_data_manual_config_handler.getFileName().toStdString() ==
            isotopic_data_file_path.toStdString());
        }
      }
    }
  }
}

SCENARIO(
  "Construction of an IsotopicDataManualConfigHandler with an inexistent "
  "file's "
  "name",
  "[IsotopicDataManualConfigHandler]")
{
  TestUtils test_utils;

  QString inexistent_isotopic_data_file_path =
    QString("%1/%2/%3").arg(TESTS_INPUT_DIR, "isotopes", "inexistent.dat");

  GIVEN(
    "A IsotopicDataManualConfigHandler instance constructed with an inexistent "
    "file's name")
  {
    IsotopicDataManualConfigHandler isotopic_data_manual_config_handler(
      inexistent_isotopic_data_file_path);

    THEN("All the data are set to nothing, but the IsotopicData")
    {
      REQUIRE(isotopic_data_manual_config_handler.getIsotopicData() != nullptr);
      REQUIRE(isotopic_data_manual_config_handler.getIsotopicData()->size() ==
              0);
      REQUIRE(isotopic_data_manual_config_handler.getFileName().toStdString() ==
              inexistent_isotopic_data_file_path.toStdString());
    }

    WHEN("Trying to load the data")
    {
      std::size_t loaded_isotope_count =
        isotopic_data_manual_config_handler.loadData();

      THEN("The data cannot be loaded")
      {
        REQUIRE(loaded_isotope_count == 0);
        REQUIRE(isotopic_data_manual_config_handler.getIsotopicData()->size() ==
                loaded_isotope_count);
      }

      AND_WHEN("Trying to load the data specifying an existing file's name")
      {
        QString isotopic_data_file_path =
          QString("%1/%2/%3")
            .arg(TESTS_INPUT_DIR)
            .arg("isotopes", test_utils.m_manualUserIsotopicDataFileName);

        isotopic_data_manual_config_handler.setFileName(
          isotopic_data_file_path);
        std::size_t loaded_isotope_count =
          isotopic_data_manual_config_handler.loadData();

        THEN("The number of isotopes loaded is checked")
        {
          REQUIRE(loaded_isotope_count > 0);
          REQUIRE(
            isotopic_data_manual_config_handler.getIsotopicData()->size() ==
            loaded_isotope_count);
        }
      }
    }
  }

  GIVEN(
    "A IsotopicDataManualConfigHandler instance constructed with an inexistent "
    "file's name")
  {
    IsotopicDataManualConfigHandler isotopic_data_manual_config_handler(
      inexistent_isotopic_data_file_path);

    THEN("All the data are set to nothing, but the IsotopicData")
    {
      REQUIRE(isotopic_data_manual_config_handler.getIsotopicData() != nullptr);
      REQUIRE(isotopic_data_manual_config_handler.getIsotopicData()->size() ==
              0);
      REQUIRE(isotopic_data_manual_config_handler.getFileName().toStdString() ==
              inexistent_isotopic_data_file_path.toStdString());
    }

    WHEN("Trying to load the data")
    {
      std::size_t loaded_isotope_count =
        isotopic_data_manual_config_handler.loadData();

      THEN("The data cannot be loaded")
      {
        REQUIRE(loaded_isotope_count == 0);
        REQUIRE(isotopic_data_manual_config_handler.getIsotopicData()->size() ==
                loaded_isotope_count);
      }

      AND_WHEN("Setting an existing file's name")
      {
        QString isotopic_data_file_path =
          QString("%1/%2/%3")
            .arg(TESTS_INPUT_DIR)
            .arg("isotopes", test_utils.m_manualUserIsotopicDataFileName);

        isotopic_data_manual_config_handler.setFileName(
          isotopic_data_file_path);

        AND_WHEN("Trying to load data")
        {
          std::size_t loaded_isotope_count =
            isotopic_data_manual_config_handler.loadData();

          THEN("The number of isotopes loaded is checked")
          {
            REQUIRE(loaded_isotope_count > 0);
            REQUIRE(
              isotopic_data_manual_config_handler.getIsotopicData()->size() ==
              loaded_isotope_count);
          }
        }
      }
    }
  }
}

SCENARIO(
  "Construction of an IsotopicDataManualConfigHandler with an existing file's "
  "name",
  "[IsotopicDataManualConfigHandler]")
{
  TestUtils test_utils;

  QString isotopic_data_file_path =
    QString("%1/%2/%3")
      .arg(TESTS_INPUT_DIR)
      .arg("isotopes", test_utils.m_manualUserIsotopicDataFileName);

  WHEN("Constructed, the data are empty")
  {
    IsotopicDataManualConfigHandler isotopic_data_manual_config_handler(
      isotopic_data_file_path);

    THEN("The isotopic data are empty but they are allocated")
    {
      REQUIRE(isotopic_data_manual_config_handler.getIsotopicData() != nullptr);
    }

    AND_WHEN("Loading isotopic data")
    {
      std::size_t loaded_isotope_count =
        isotopic_data_manual_config_handler.loadData();

      THEN("The number of isotopes loaded is checked")
      {
        REQUIRE(loaded_isotope_count > 0);
        REQUIRE(isotopic_data_manual_config_handler.getIsotopicData()->size() ==
                loaded_isotope_count);
      }
    }
  }
}

SCENARIO("IsotopicDataManualConfigHandler writes data to file",
         "[IsotopicDataManualConfigHandler]")
{
  TestUtils test_utils;

  QString isotopic_data_file_path =
    QString("%1/%2/%3")
      .arg(TESTS_INPUT_DIR)
      .arg("isotopes", test_utils.m_manualUserIsotopicDataFileName);

  QDir dir;
  QString isotopes_output_dir =
    QString("%1/%2").arg(TESTS_OUTPUT_DIR, "isotopes");

  QString isotopic_data_output_file_path = QString("%1/%2").arg(
    isotopes_output_dir, test_utils.m_manualUserIsotopicDataFileName);

  bool result = dir.mkpath(isotopes_output_dir);
  assert(result);

  IsotopicDataManualConfigHandler iso_data_user_config_handler(
    isotopic_data_file_path);

  WHEN("Data have been loaded, the data are checked")
  {
    std::size_t loaded_isotope_count =
      iso_data_user_config_handler.loadData(isotopic_data_file_path);

    THEN("The number of isotopes loaded is checked")
    {
      REQUIRE(iso_data_user_config_handler.getIsotopicData()->size() ==
              loaded_isotope_count);
    }

    AND_WHEN("Isotopic data are written to file")
    {
      std::size_t written_isotope_count =
        iso_data_user_config_handler.writeData(isotopic_data_output_file_path);

      THEN("The count of written isotopes is checked")
      {
        REQUIRE(written_isotope_count == loaded_isotope_count);
      }
    }
  }
}

SCENARIO(
  "IsotopicDataManualConfigHandler loads data from file previously "
  "written by writeData() test")
{
  TestUtils test_utils;

  QDir dir;
  QString isotopes_output_dir =
    QString("%1/%2").arg(TESTS_OUTPUT_DIR, "isotopes");

  QString isotopic_data_output_file_path = QString("%1/%2").arg(
    isotopes_output_dir, test_utils.m_manualUserIsotopicDataFileName);

  IsotopicDataManualConfigHandler iso_data_user_config_handler(
    isotopic_data_output_file_path);

  WHEN("Constructed, the data are empty")
  {

    THEN("The isotopic data are empty but they are allocated")
    {
      REQUIRE(iso_data_user_config_handler.getIsotopicData() != nullptr);
    }
  }

  AND_WHEN("Loading isotopic data from the file")
  {
    std::size_t loaded_isotope_count =
      iso_data_user_config_handler.loadData(isotopic_data_output_file_path);

    THEN("The number of isotopes loaded is checked")
    {
      REQUIRE(loaded_isotope_count > 0);
      REQUIRE(iso_data_user_config_handler.getIsotopicData()->size() ==
              loaded_isotope_count);
    }
  }

  AND_WHEN("A new IsotopicDataManualConfigHandler instance is copy constructed")
  {
    long use_count_before_copy =
      iso_data_user_config_handler.getIsotopicData().use_count();

    IsotopicDataManualConfigHandler iso_data_user_config_handler_1(
      iso_data_user_config_handler);

    THEN("The instances are checked and should be identical")
    {
      REQUIRE(iso_data_user_config_handler.getIsotopicData()->size() ==
              iso_data_user_config_handler_1.getIsotopicData()->size());

      long use_count_after_copy =
        iso_data_user_config_handler.getIsotopicData().use_count();

      REQUIRE(use_count_after_copy == use_count_before_copy + 1);
    }
  }
}


} // namespace libXpertMassCore
} // namespace MsXpS
