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


/////////////////////// Stdlib includes

/////////////////////// 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>


/////////////////////// Local includes
#include "TestUtils.hpp"
#include "MsXpS/libXpertMassCore/PolChemDefSpec.hpp"
#include "MsXpS/libXpertMassCore/PolChemDef.hpp"
#include "MsXpS/libXpertMassCore/globals.hpp"
#include "MsXpS/libXpertMassCore/IsotopicData.hpp"
#include "MsXpS/libXpertMassCore/Monomer.hpp"
#include "MsXpS/libXpertMassCore/Modif.hpp"
#include "MsXpS/libXpertMassCore/CrossLinker.hpp"
#include "MsXpS/libXpertMassCore/CleavageAgent.hpp"
#include "MsXpS/libXpertMassCore/FragmentationPathway.hpp"

namespace MsXpS
{

namespace libXpertMassCore
{

ErrorList error_list_pol_chem_def;


//  In the tests below,  we do not use TestUtils because we want
//  more granularity that what is -by design- offered by TestUtils.
SCENARIO(
  "PolChemDef instantiated using the specification in a PolChemDefSpec "
  "instance",
  "[PolChemdef]")
{
  PolChemDefSpec pol_chem_def_spec;
  PolChemDef pol_chem_def;

  // We use polymer chemistry definitions copied to the
  // tests/data/polymer-chemistry-definitions directory.
  QString test_pol_chem_defs_dir =
    QString("%1/polymer-chemistry-definitions").arg(TESTS_INPUT_DIR);

  QString pol_chem_def_name = "protein-1-letter";
  QString pol_chem_def_file_base_name =
    QString("%1.xml").arg(pol_chem_def_name);

  // Relative is to one of the polChemDefs directory (in system or user
  // directory)
  QString pol_chem_def_relative_dir_path = "protein-1-letter";

  QString pol_chem_def_relative_file_path =
    QString("%1/%2")
      .arg(pol_chem_def_relative_dir_path)
      .arg(pol_chem_def_file_base_name);

  QString pol_chem_def_absolute_dir_path =
    QString("%1/%2")
      .arg(test_pol_chem_defs_dir)
      .arg(pol_chem_def_relative_dir_path);

  QString pol_chem_def_absolute_file_path =
    QString("%1/%2")
      .arg(pol_chem_def_absolute_dir_path)
      .arg(pol_chem_def_file_base_name);


  GIVEN("Constructing an empty PolChemDefSpec")
  {

    THEN("The member data are empty")
    {
      REQUIRE(pol_chem_def_spec.getName() == "");
      REQUIRE(pol_chem_def_spec.getFilePath() == "");
    }
  }

  AND_GIVEN("Set name and file path")
  {
    pol_chem_def_spec.setName(pol_chem_def_file_base_name);
    pol_chem_def_spec.setFilePath(pol_chem_def_relative_file_path);

    THEN("The name and file path are set to the member data")
    {
      REQUIRE(pol_chem_def_spec.getName().toStdString() ==
              pol_chem_def_file_base_name.toStdString());
      REQUIRE(pol_chem_def_spec.getFilePath().toStdString() ==
              pol_chem_def_relative_file_path.toStdString());
    }

    AND_GIVEN("That PolChemDefSpec, allocate a PolChemDef")
    {
      PolChemDef pol_chem_def(pol_chem_def_spec);

      THEN("The PolChemDef name is set to the PolChemDefSpec name")
      {
        REQUIRE(pol_chem_def.getName().toStdString() ==
                pol_chem_def_spec.getName().toStdString());
      }

      AND_THEN("The file path is set also")
      {
        REQUIRE(pol_chem_def.getXmlDataFilePath().toStdString() ==
                pol_chem_def_spec.getFilePath().toStdString());
      }

      AND_THEN("Code length value is 1")
      {
        REQUIRE(pol_chem_def.getCodeLength() == -1);
      }
    }
  }
}

SCENARIO(
  "PolChemDef version 1 instantiated empty and then got to load the file",
  "[PolChemDef]")
{
  int version = 1;
  PolChemDef pol_chem_def;
  PolChemDefSPtr pol_chem_def_sp = nullptr;

  // We use polymer chemistry definitions copied to the
  // tests/data/polymer-chemistry-definitions directory.
  QString test_pol_chem_defs_dir =
    QString("%1/polymer-chemistry-definitions").arg(TESTS_INPUT_DIR);

  QString test_pol_chem_defs_versioned_dir =
    QString("%1/version-%2").arg(test_pol_chem_defs_dir).arg(version);

  QString pol_chem_def_name = "protein-1-letter";
  QString pol_chem_def_file_base_name =
    QString("%1.xml").arg(pol_chem_def_name);

  // Relative is to one of the polChemDefs directory (in system or user
  // directory)
  QString pol_chem_def_relative_dir_path = "protein-1-letter";

  QString pol_chem_def_relative_file_path =
    QString("%1/%2")
      .arg(pol_chem_def_relative_dir_path)
      .arg(pol_chem_def_file_base_name);

  QString pol_chem_def_isotopic_data_relative_file_path =
    QString("%1/%2/%3")
      .arg(pol_chem_def_relative_dir_path)
      .arg(pol_chem_def_file_base_name)
      .arg("isotopic-data.dat");

  QString pol_chem_def_absolute_dir_path =
    QString("%1/%2")
      .arg(test_pol_chem_defs_versioned_dir)
      .arg(pol_chem_def_relative_dir_path);

  // qDebug()  << "The dir: " << pol_chem_def_absolute_dir_path.toStdString()
  //           << std::endl;

  QString pol_chem_def_absolute_file_path =
    QString("%1/%2")
      .arg(pol_chem_def_absolute_dir_path)
      .arg(pol_chem_def_file_base_name);

  QString pol_chem_def_isotopic_data_absolute_file_path =
    QString("%1/%2")
      .arg(pol_chem_def_absolute_dir_path)
      .arg("isotopic-data.dat");

  GIVEN("Constructing an empty PolChemDef")
  {

    THEN("The member data are empty")
    {
      REQUIRE(pol_chem_def.getName() == "");
      REQUIRE(pol_chem_def.getXmlDataFilePath() == "");
    }
  }

  WHEN("Setting name and XML file path")
  {
    pol_chem_def.setName(pol_chem_def_file_base_name);
    pol_chem_def.setXmlDataFilePath(pol_chem_def_absolute_file_path);

    THEN("The name and file path are set to the member data")
    {
      REQUIRE(pol_chem_def.getName().toStdString() ==
              pol_chem_def_file_base_name.toStdString());
      REQUIRE(pol_chem_def.getXmlDataFilePath().toStdString() ==
              pol_chem_def_absolute_file_path.toStdString());
      REQUIRE(pol_chem_def.getXmlDataDirPath().toStdString() ==
              pol_chem_def_absolute_dir_path.toStdString());
    }

    AND_THEN("The path to the isotopic data can be deduced")
    {
      QString isotopic_data_file_path =
        pol_chem_def.deduceIsotopicDataFilePath();

      REQUIRE(isotopic_data_file_path.toStdString() ==
              pol_chem_def_isotopic_data_absolute_file_path.toStdString());
    }

    AND_WHEN("Loading isotopic data from disk file using static function")
    {

      // REQUIRE(pol_chem_def.getName().toStdString() ==
      //         pol_chem_def_file_base_name.toStdString());
      // REQUIRE(pol_chem_def.getXmlDataFilePath().toStdString() ==
      //         pol_chem_def_absolute_file_path.toStdString());
      // REQUIRE(pol_chem_def.getXmlDataDirPath().toStdString() ==
      //         pol_chem_def_absolute_dir_path.toStdString());

      pol_chem_def_sp = std::make_shared<PolChemDef>(pol_chem_def);
      REQUIRE(pol_chem_def_sp != nullptr);
      REQUIRE(pol_chem_def_sp.use_count() == 1);

      std::size_t isotope_count = PolChemDef::loadIsotopicData(pol_chem_def_sp);

      THEN("Count the isotopes that have been loaded")
      {
        REQUIRE(isotope_count > 0);
        // qDebug()  << "Loaded " << isotope_count << " isotopes" << std::endl;
      }

      WHEN("Loading the polymer chemistry definition using static function")
      {
        REQUIRE(pol_chem_def_sp != nullptr);

        bool result = PolChemDef::renderXmlPolChemDefFile(pol_chem_def_sp);

        THEN("That should not fail")
        {
          REQUIRE(result == true);
        }
      }
    }
  }
}

SCENARIO("Loading a PolChemDef version 1", "[PolChemDef]")
{
  int version = 1;
  PolChemDef pol_chem_def;
  PolChemDefSPtr pol_chem_def_sp = nullptr;

  // We use polymer chemistry definitions copied to the
  // tests/data/polymer-chemistry-definitions directory.
  QString test_pol_chem_defs_dir =
    QString("%1/polymer-chemistry-definitions").arg(TESTS_INPUT_DIR);

  QString test_pol_chem_defs_versioned_dir =
    QString("%1/version-%2").arg(test_pol_chem_defs_dir).arg(version);

  QString pol_chem_def_name = "protein-1-letter";
  QString pol_chem_def_file_base_name =
    QString("%1.xml").arg(pol_chem_def_name);

  // Relative is to one of the polChemDefs directory (in system or user
  // directory)
  QString pol_chem_def_relative_dir_path = "protein-1-letter";

  QString pol_chem_def_relative_file_path =
    QString("%1/%2")
      .arg(pol_chem_def_relative_dir_path)
      .arg(pol_chem_def_file_base_name);

  QString pol_chem_def_isotopic_data_relative_file_path =
    QString("%1/%2/%3")
      .arg(pol_chem_def_relative_dir_path)
      .arg(pol_chem_def_file_base_name)
      .arg("isotopic-data.dat");

  QString pol_chem_def_absolute_dir_path =
    QString("%1/%2")
      .arg(test_pol_chem_defs_versioned_dir)
      .arg(pol_chem_def_relative_dir_path);

  // qDebug()  << "The dir: " << pol_chem_def_absolute_dir_path.toStdString()
  //           << std::endl;

  QString pol_chem_def_absolute_file_path =
    QString("%1/%2")
      .arg(pol_chem_def_absolute_dir_path)
      .arg(pol_chem_def_file_base_name);

  QString pol_chem_def_isotopic_data_absolute_file_path =
    QString("%1/%2")
      .arg(pol_chem_def_absolute_dir_path)
      .arg("isotopic-data.dat");

  pol_chem_def_sp = std::make_shared<PolChemDef>(pol_chem_def);

  pol_chem_def_sp->setName(pol_chem_def_file_base_name);
  pol_chem_def_sp->setXmlDataFilePath(pol_chem_def_absolute_file_path);

  GIVEN("Loading isotopic data and polymer chemistry definition")
  {
    std::size_t isotope_count = PolChemDef::loadIsotopicData(pol_chem_def_sp);
    bool result = PolChemDef::renderXmlPolChemDefFile(pol_chem_def_sp);

    THEN(
      "Count the isotopes that have been loaded and check PolChemDef load was "
      "successful")
    {
      REQUIRE(isotope_count > 0);
      // qDebug()  << "Loaded " << isotope_count << " isotopes" << std::endl;
      REQUIRE(result == true);
    }
    AND_THEN("The singular chemical entities are checked (end cap chemistry")
    {
      REQUIRE(pol_chem_def_sp->getName().toStdString() ==
              pol_chem_def_name.toStdString());
      REQUIRE(pol_chem_def_sp->getLeftCap()
                .getActionFormula(/*with_title*/ true)
                .toStdString() == "\"N-term amine\"+H");
      REQUIRE(pol_chem_def_sp->getRightCap()
                .getActionFormula(/*with_title*/ true)
                .toStdString() == "\"C-term carboxylic acid\"+OH");
    }

    AND_THEN("And the ionization rule is checked")
    {
      Ionizer ionizer = pol_chem_def_sp->getIonizerCstRef();
      REQUIRE(ionizer.getFormulaCstRef().getActionFormula().toStdString() ==
              "+H");
      REQUIRE(ionizer.getNominalCharge() == 1);
      REQUIRE(ionizer.getLevel() == 1);
    }

    AND_THEN("And the code length is checked")
    {
      REQUIRE(pol_chem_def_sp->getCodeLength() == 1);
    }

    AND_THEN("The Monomer_s are checked")
    {
      MonomerSPtr monomer_csp = nullptr;

      // Because of diaminopimelic acid, not 20, but 21.
      REQUIRE(pol_chem_def_sp->getMonomersCstRef().size() == 21);

      monomer_csp = pol_chem_def_sp->getMonomerCstSPtrByCode("G");
      REQUIRE(monomer_csp->getName().toStdString() == "Glycine");
      REQUIRE(monomer_csp->getCode().toStdString() == "G");
      REQUIRE(monomer_csp->getFormula().toStdString() == "C2H3N1O1");
      REQUIRE(monomer_csp->validate(&error_list_pol_chem_def));
      double mono;
      double avg;
      REQUIRE(monomer_csp->calculateMasses(
        pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg));

      monomer_csp = pol_chem_def_sp->getMonomerCstSPtrByCode("O");
      REQUIRE(monomer_csp->getName().toStdString() == "DiaminoPimelic");
      REQUIRE(monomer_csp->getCode().toStdString() == "O");
      REQUIRE(monomer_csp->getFormula().toStdString() == "C7H12N2O3");
      REQUIRE(monomer_csp->validate(&error_list_pol_chem_def) == true);
      REQUIRE(monomer_csp->calculateMasses(
        pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg));

      monomer_csp = pol_chem_def_sp->getMonomerCstSPtrByCode("D");
      REQUIRE(monomer_csp->getName().toStdString() == "Aspartate");
      REQUIRE(monomer_csp->getCode().toStdString() == "D");
      REQUIRE(monomer_csp->getFormula().toStdString() == "C4H5N1O3");
      REQUIRE(monomer_csp->validate(&error_list_pol_chem_def) == true);
      REQUIRE(monomer_csp->calculateMasses(
        pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg));

      monomer_csp = pol_chem_def_sp->getMonomerCstSPtrByCode("P");
      REQUIRE(monomer_csp->getName().toStdString() == "Proline");
      REQUIRE(monomer_csp->getCode().toStdString() == "P");
      REQUIRE(monomer_csp->getFormula().toStdString() == "C5H7N1O1");
      REQUIRE(monomer_csp->validate(&error_list_pol_chem_def) == true);
      REQUIRE(monomer_csp->calculateMasses(
        pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg));
    }

    AND_THEN("The Modif_s are checked")
    {
      ModifCstSPtr modif_cspp = nullptr;

      REQUIRE(pol_chem_def_sp->getModifsCstRef().size() == 26);

      modif_cspp = pol_chem_def_sp->	getModifCstSPtrByName("Acetylation");
      REQUIRE(modif_cspp->getName().toStdString() == "Acetylation");
      REQUIRE(modif_cspp->getFormula().toStdString() == "C2H2O1");
      REQUIRE(modif_cspp->getTargets().toStdString() == ";K;");
      REQUIRE(modif_cspp->getMaxCount() == 1);
      REQUIRE(modif_cspp->validate(&error_list_pol_chem_def) == true);
      double mono;
      double avg;
      REQUIRE(modif_cspp->calculateMasses(
                pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg) == true);

      modif_cspp = pol_chem_def_sp->	getModifCstSPtrByName("CarboxyMethylation");
      REQUIRE(modif_cspp->getName().toStdString() == "CarboxyMethylation");
      REQUIRE(modif_cspp->getFormula().toStdString() == "C2H2O2");
      REQUIRE(modif_cspp->getTargets().toStdString() == ";C;");
      REQUIRE(modif_cspp->getMaxCount() == 1);
      REQUIRE(modif_cspp->validate(&error_list_pol_chem_def) == true);
      REQUIRE(modif_cspp->calculateMasses(
                pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg) == true);

      modif_cspp = pol_chem_def_sp->	getModifCstSPtrByName("Chromo-H3");
      REQUIRE(modif_cspp->getName().toStdString() == "Chromo-H3");
      REQUIRE(modif_cspp->getFormula().toStdString() == "-H3");
      REQUIRE(modif_cspp->getTargets().toStdString() == ";Y;F;W;");
      REQUIRE(modif_cspp->getMaxCount() == 1);
      REQUIRE(modif_cspp->validate(&error_list_pol_chem_def) == true);
      REQUIRE(modif_cspp->calculateMasses(
                pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg) == true);

      modif_cspp = pol_chem_def_sp->	getModifCstSPtrByName("Formylation");
      REQUIRE(modif_cspp->getName().toStdString() == "Formylation");
      REQUIRE(modif_cspp->getFormula().toStdString() == "C1O1");
      REQUIRE(modif_cspp->getTargets().toStdString() == "*");
      REQUIRE(modif_cspp->getMaxCount() == 1);
      REQUIRE(modif_cspp->validate(&error_list_pol_chem_def) == true);
      REQUIRE(modif_cspp->calculateMasses(
                pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg) == true);

      modif_cspp = pol_chem_def_sp->	getModifCstSPtrByName("Glycylation");
      REQUIRE(modif_cspp->getName().toStdString() == "Glycylation");
      REQUIRE(modif_cspp->getFormula().toStdString() == "C2H3N1O1");
      REQUIRE(modif_cspp->getTargets().toStdString() == ";E;");
      REQUIRE(modif_cspp->getMaxCount() == 34);
      REQUIRE(modif_cspp->validate(&error_list_pol_chem_def) == true);
      REQUIRE(modif_cspp->calculateMasses(
                pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg) == true);

      modif_cspp = pol_chem_def_sp->	getModifCstSPtrByName("Phosphorylation");
      REQUIRE(modif_cspp->getName().toStdString() == "Phosphorylation");
      REQUIRE(modif_cspp->getFormula().toStdString() == "H1O3P1");
      REQUIRE(modif_cspp->getTargets().toStdString() == ";S;T;Y;");
      REQUIRE(modif_cspp->getMaxCount() == 1);
      REQUIRE(modif_cspp->validate(&error_list_pol_chem_def) == true);
      REQUIRE(modif_cspp->calculateMasses(
                pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg) == true);
    }

    AND_THEN("The CrossLinker_s are checked")
    {
      CrossLinkerCstSPtr crossLinker_csp = nullptr;
      ModifCstSPtr modif_csp             = nullptr;

      REQUIRE(pol_chem_def_sp->getCrossLinkersCstRef().size() == 2);

      crossLinker_csp =
        pol_chem_def_sp->	getCrossLinkerCstSPtrByName("CFP-chromophore");
      REQUIRE(crossLinker_csp->getName().toStdString() == "CFP-chromophore");
      REQUIRE(crossLinker_csp->getFormula().toStdString() == "");

      REQUIRE(crossLinker_csp->getModifsCstRef().size() == 3);

      modif_csp = crossLinker_csp->getModifsCstRef().at(0);
      REQUIRE(modif_csp->getName().toStdString() == "Chromo-O");
      modif_csp = crossLinker_csp->getModifsCstRef().at(1);
      REQUIRE(modif_csp->getName().toStdString() == "Chromo-H3");
      modif_csp = crossLinker_csp->getModifsCstRef().at(2);
      REQUIRE(modif_csp->getName().toStdString() == "Chromo-H");

      crossLinker_csp =
        pol_chem_def_sp->	getCrossLinkerCstSPtrByName("DisulfideBond");
      REQUIRE(crossLinker_csp->getName().toStdString() == "DisulfideBond");
      REQUIRE(crossLinker_csp->getFormula().toStdString() == "");

      REQUIRE(crossLinker_csp->getModifsCstRef().size() == 2);

      modif_csp = crossLinker_csp->getModifsCstRef().at(0);
      REQUIRE(modif_csp->getName().toStdString() == "ProtonLoss");
      modif_csp = crossLinker_csp->getModifsCstRef().at(1);
      REQUIRE(modif_csp->getName().toStdString() == "ProtonLoss");
    }

    AND_THEN("The CleavageAgent_s are checked (and the CleavageRules_ if any)")
    {
      CleavageAgentCstSPtr cleavage_agent_csp = nullptr;

      REQUIRE(pol_chem_def_sp->getCleavageAgentsCstRef().size() == 8);

      cleavage_agent_csp =
        pol_chem_def_sp->	getCleavageAgentCstSPtrByName("EndoAspN+GluN");
      REQUIRE(cleavage_agent_csp->getName().toStdString() == "EndoAspN+GluN");
      REQUIRE(cleavage_agent_csp->getPattern().toStdString() == "/D;/E");

      cleavage_agent_csp =
        pol_chem_def_sp->	getCleavageAgentCstSPtrByName("EndoLysC");
      REQUIRE(cleavage_agent_csp->getName().toStdString() == "EndoLysC");
      REQUIRE(cleavage_agent_csp->getPattern().toStdString() == "K/");

      cleavage_agent_csp =
        pol_chem_def_sp->	getCleavageAgentCstSPtrByName("CyanogenBromide");
      REQUIRE(cleavage_agent_csp->getName().toStdString() == "CyanogenBromide");
      REQUIRE(cleavage_agent_csp->getPattern().toStdString() == "M/");

      CleavageRuleSPtr cleavage_rule_sp =
        cleavage_agent_csp->getCleavageRuleSPtrByName("Homoseryl");
      REQUIRE(cleavage_rule_sp->getName().toStdString() == "Homoseryl");
      REQUIRE(cleavage_rule_sp->getRightCode().toStdString() == "M");
      REQUIRE(
        cleavage_rule_sp->getRightFormula().getActionFormula().toStdString() ==
        "-CH2S+O");
    }

    AND_THEN("The FragmentationPathway_s are checked")
    {
      FragmentationPathwayCstSPtr fragmentation_pathway_csp = nullptr;

      REQUIRE(pol_chem_def_sp->getFragmentationPathwaysCstRef().size() == 7);

      fragmentation_pathway_csp =
        pol_chem_def_sp->	getFragmentationPathwayCstSPtrByName("a");
      REQUIRE(fragmentation_pathway_csp->getName().toStdString() == "a");
      REQUIRE(fragmentation_pathway_csp->getFragEnd() == Enums::FragEnd::LE);
      REQUIRE(fragmentation_pathway_csp->getFormulaCstRef()
                .getActionFormula()
                .toStdString() == "-C1O1H1");
      REQUIRE(fragmentation_pathway_csp->getMonomerContribution() == 0);
      REQUIRE(fragmentation_pathway_csp->getComment().toStdString() == "");

      fragmentation_pathway_csp =
        pol_chem_def_sp->	getFragmentationPathwayCstSPtrByName("z");
      REQUIRE(fragmentation_pathway_csp->getName().toStdString() == "z");
      REQUIRE(fragmentation_pathway_csp->getFragEnd() == Enums::FragEnd::RE);
      REQUIRE(fragmentation_pathway_csp->getFormulaCstRef()
                .getActionFormula()
                .toStdString() == "-N1H1");
      REQUIRE(fragmentation_pathway_csp->getMonomerContribution() == 0);
      REQUIRE(fragmentation_pathway_csp->getComment().toStdString() ==
              "Typically in ECD of protonated ions");
    }
  }
}

SCENARIO(
  "Loading the same PolChemDef version 1, writing it as version 2 and checking "
  "the chemical identity of the two versions",
  "[PolChemDef]")
{
  int input_version_1 = 1;

  PolChemDefSPtr pol_chem_def_sp = nullptr;

  // We use polymer chemistry definitions copied to the
  // tests/data/polymer-chemistry-definitions directory.
  QString test_pol_chem_defs_dir =
    QString("%1/polymer-chemistry-definitions").arg(TESTS_INPUT_DIR);

  QString test_pol_chem_defs_versioned_dir =
    QString("%1/version-%2").arg(test_pol_chem_defs_dir).arg(input_version_1);

  QString pol_chem_def_name = "protein-1-letter";
  QString pol_chem_def_file_base_name =
    QString("%1.xml").arg(pol_chem_def_name);

  // Relative is to one of the polChemDefs directory (in system or user
  // directory)
  QString pol_chem_def_relative_dir_path = "protein-1-letter";

  QString pol_chem_def_relative_file_path =
    QString("%1/%2")
      .arg(pol_chem_def_relative_dir_path)
      .arg(pol_chem_def_file_base_name);

  QString pol_chem_def_isotopic_data_relative_file_path =
    QString("%1/%2/%3")
      .arg(pol_chem_def_relative_dir_path)
      .arg(pol_chem_def_file_base_name)
      .arg("isotopic-data.dat");

  QString pol_chem_def_absolute_dir_path =
    QString("%1/%2")
      .arg(test_pol_chem_defs_versioned_dir)
      .arg(pol_chem_def_relative_dir_path);

  // qDebug()  << "The dir: " << pol_chem_def_absolute_dir_path.toStdString()
  //           << std::endl;

  QString pol_chem_def_absolute_file_path =
    QString("%1/%2")
      .arg(pol_chem_def_absolute_dir_path)
      .arg(pol_chem_def_file_base_name);

  QString pol_chem_def_isotopic_data_absolute_file_path =
    QString("%1/%2")
      .arg(pol_chem_def_absolute_dir_path)
      .arg("isotopic-data.dat");

  pol_chem_def_sp = std::make_shared<PolChemDef>();

  pol_chem_def_sp->setName(pol_chem_def_file_base_name);
  pol_chem_def_sp->setXmlDataFilePath(pol_chem_def_absolute_file_path);

  std::size_t isotope_count = PolChemDef::loadIsotopicData(pol_chem_def_sp);
  REQUIRE(isotope_count == 288);

  REQUIRE(PolChemDef::renderXmlPolChemDefFile(pol_chem_def_sp));

  GIVEN("A fully loaded PolChemDef version 1 along with its IsotopicData")
  {
    QString pol_chem_def_output_dir = QString("%1/%2/%3")
                                        .arg(TESTS_OUTPUT_DIR)
                                        .arg("polymer-chemistry-definitions")
                                        .arg(pol_chem_def_name);

    // qDebug() << "The output directory:" << pol_chem_def_output_dir;

    QString pol_chem_def_output_file_name = QString("%1/%2")
                                              .arg(pol_chem_def_output_dir)
                                              .arg(pol_chem_def_file_base_name);

    // qDebug() << "The output file:" << pol_chem_def_output_file_name;

    QDir dir;
    REQUIRE(dir.mkpath(pol_chem_def_output_dir));

    WHEN("Writing that PolChemDef back to file")
    {
      pol_chem_def_sp->setXmlDataFilePath(pol_chem_def_output_file_name);

      THEN("The writing should be succesful")
      {
        REQUIRE(pol_chem_def_sp->writeXmlFile());
      }
    }

    AND_WHEN("Loading the written PolChemDef")
    {
      PolChemDefSPtr new_pol_chem_def_sp = nullptr;

      // We use polymer chemistry definitions copied to the
      // tests/data/polymer-chemistry-definitions directory.
      QString test_pol_chem_defs_dir =
        QString("%1/polymer-chemistry-definitions").arg(TESTS_OUTPUT_DIR);

      QString pol_chem_def_name = "protein-1-letter";
      QString pol_chem_def_file_base_name =
        QString("%1.xml").arg(pol_chem_def_name);

      // Relative is to one of the polChemDefs directory (in system or user
      // directory)
      QString pol_chem_def_relative_dir_path = "protein-1-letter";

      QString pol_chem_def_relative_file_path =
        QString("%1/%2")
          .arg(pol_chem_def_relative_dir_path)
          .arg(pol_chem_def_file_base_name);

      QString pol_chem_def_isotopic_data_relative_file_path =
        QString("%1/%2/%3")
          .arg(pol_chem_def_relative_dir_path)
          .arg(pol_chem_def_file_base_name)
          .arg("isotopic-data.dat");

      QString pol_chem_def_absolute_dir_path =
        QString("%1/%2")
          .arg(test_pol_chem_defs_dir)
          .arg(pol_chem_def_relative_dir_path);

      qDebug() << "The dir: " << pol_chem_def_absolute_dir_path;

      QString pol_chem_def_absolute_file_path =
        QString("%1/%2")
          .arg(pol_chem_def_absolute_dir_path)
          .arg(pol_chem_def_file_base_name);

      QString pol_chem_def_isotopic_data_absolute_file_path =
        QString("%1/%2")
          .arg(pol_chem_def_absolute_dir_path)
          .arg("isotopic-data.dat");

      new_pol_chem_def_sp = std::make_shared<PolChemDef>();

      new_pol_chem_def_sp->setName(pol_chem_def_file_base_name);
      new_pol_chem_def_sp->setXmlDataFilePath(pol_chem_def_absolute_file_path);

      qDebug() << "Now loading PolChemDef:" << pol_chem_def_absolute_file_path;

      std::size_t isotope_count =
        PolChemDef::loadIsotopicData(new_pol_chem_def_sp);
      REQUIRE(isotope_count == 288);

      REQUIRE(PolChemDef::renderXmlPolChemDefFile(new_pol_chem_def_sp));

      // qDebug() << "Loaded pol chem def:" <<
      // new_pol_chem_def_sp->getXmlDataFilePath();

      REQUIRE(new_pol_chem_def_sp->getIsotopicDataCstSPtr()->size() == 288);

      THEN(
        "The first and the second PolChemDef need to be identical in a deep "
        "comparison way")
      {
        REQUIRE(
          pol_chem_def_sp->isChemicallyEquivalent(*new_pol_chem_def_sp.get()));
      }
    }
  }
}


} // namespace libXpertMassCore
} // namespace MsXpS
