// ./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/matchers/catch_matchers_floating_point.hpp>


/////////////////////// libXpertMassCore includes
#include "MsXpS/libXpertMassCore/Isotope.hpp"
#include "MsXpS/libXpertMassCore/IsotopicData.hpp"
#include "MsXpS/libXpertMassCore/IsotopicDataLibraryHandler.hpp"
#include "MsXpS/libXpertMassCore/IsotopicDataUserConfigHandler.hpp"

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


namespace MsXpS
{
namespace libXpertMassCore
{

SCENARIO("Construction of an empty IsotopicDataUserConfigHandler",
         "[IsotopicDataUserConfigHandler]")
{

  TestUtils test_utils;

  test_utils.initializeXpertmassLibrary();

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

  GIVEN("An entirely empty IsotopicDataUserConfigHandler instance")
  {
    IsotopicDataUserConfigHandler isotopic_data_user_config_handler;

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

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

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

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

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

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

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

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

      THEN("The data cannot be loaded")
      {
        REQUIRE(loaded_isotope_count == 0);
        REQUIRE(isotopic_data_user_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,
                 "isotopes",
                 test_utils.m_naturalIsotopicDataFileName);

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

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

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

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

  WHEN("Constructed, the data are empty")
  {
    IsotopicDataUserConfigHandler isotopic_data_user_config_handler(
      isotopic_data_file_path);

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

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

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

SCENARIO(
  "IsotopicDataUserConfigHandler loads data from file previously "
  "written by writeDate test for IsotopicDataLibraryHandler")
{
  QString isotopic_data_file_path =
    QString("%1/%2/%3").arg(TESTS_OUTPUT_DIR, "isotopes", "isospec-tables.dat");

  WHEN("Constructed with a correct file name")
  {
    IsotopicDataUserConfigHandler isotopic_data_user_config_handler(
      isotopic_data_file_path);

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

    AND_WHEN("Loading isotopic data from a user config file")
    {
      std::size_t loaded_isotope_count =
        isotopic_data_user_config_handler.loadData(isotopic_data_file_path);

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

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

        IsotopicDataUserConfigHandler isotopic_data_user_config_handler_1(
          isotopic_data_user_config_handler);

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

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

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

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

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

  WHEN("Constructed with a correct file name")
  {
    IsotopicDataUserConfigHandler isotopic_data_user_config_handler(
      isotopic_data_file_path);

    WHEN("Data have been loaded")
    {
      std::size_t loaded_isotope_count =
        isotopic_data_user_config_handler.loadData(isotopic_data_file_path);

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

      AND_WHEN("Isotopic data are written to file")
      {
        QString isotopes_output_dir =
          QString("%1/%2").arg(TESTS_OUTPUT_DIR, "isotopes");

        QDir dir;
        bool result = dir.mkpath(isotopes_output_dir);
        REQUIRE(result == true);

        QString output_file_name = QString("%1/%2").arg(
          isotopes_output_dir, "output_isotopic_data_user_config.dat");

        // qDebug() << "The write file name: " << output_file_name;

        std::size_t written_isotope_count =
          isotopic_data_user_config_handler.writeData(output_file_name);

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


} // namespace libXpertMassCore
} // namespace MsXpS
