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


/////////////////////// Catch2 includes
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>


/////////////////////// Local includes
#include "TestUtils.hpp"
#include "MsXpS/libXpertMassCore/CleavageAgent.hpp"
#include "MsXpS/libXpertMassCore/Sequence.hpp"
#include "MsXpS/libXpertMassCore/Monomer.hpp"

namespace MsXpS
{
namespace libXpertMassCore
{

TestUtils test_utils_cleavage_agent_1_letter("protein-1-letter", 1);
TestUtils test_utils_cleavage_agent_3_letters("protein-3-letters", 1);

ErrorList list_cleavage_agent;

SCENARIO(
  "CleavageAgent objects can be constructed empty and then initialized "
  "piecemeal -until they are valid",
  "[CleavageAgent]")
{
  test_utils_cleavage_agent_1_letter.initializeXpertmassLibrary();
  PolChemDefCstSPtr pol_chem_def_csp =
    test_utils_cleavage_agent_1_letter.msp_polChemDef;

  GIVEN("A CleavageAgent constructed with no parameter at all")
  {
    CleavageAgent cleavage_agent;
    ErrorList error_list_cleavage_agent;

    REQUIRE_FALSE(cleavage_agent.isValid());
    REQUIRE_FALSE(cleavage_agent.validate(&error_list_cleavage_agent));

    WHEN("The PolChemDef is set with the setter")
    {
      cleavage_agent.setPolchemDefCstSPtr(pol_chem_def_csp);

      THEN(
        "The CleavageAgent is still invalid and does not validate successfully")
      {
        REQUIRE(cleavage_agent.getPolchemDefCstSPtr() == pol_chem_def_csp);
        REQUIRE_FALSE(cleavage_agent.isValid());
        REQUIRE_FALSE(cleavage_agent.validate(&error_list_cleavage_agent));
      }

      AND_WHEN("The name is set with the setter")
      {
        cleavage_agent.setName("Trypsin");
        REQUIRE(cleavage_agent.getName().toStdString() == "Trypsin");

        THEN(
          "The CleavageAgent is still invalid and does not validate "
          "successfully")
        {
          REQUIRE_FALSE(cleavage_agent.isValid());
          REQUIRE_FALSE(cleavage_agent.validate(&error_list_cleavage_agent));
        }

        AND_WHEN("The pattern is set")
        {
          cleavage_agent.setPattern("K/;-K/P;R/");

          THEN(
            "The CleavageAgent is fully initialized with proper CleavageMotif "
            "objects (although no CleavageRule objects)")
          {
            QString expected_text = "Trypsin - K/;-K/P;R/ - 3 motifs - 0 rules";
            REQUIRE(cleavage_agent.toString().toStdString() ==
                    expected_text.toStdString());

            REQUIRE(cleavage_agent.isValid());
            REQUIRE(cleavage_agent.validate(&error_list_cleavage_agent));

            REQUIRE(cleavage_agent.getMotifsCstRef().size() == 3);

            REQUIRE(
              cleavage_agent.getMotifsRef().at(0)->getMotif().toStdString() ==
              "K");
            REQUIRE(cleavage_agent.getMotifsCstRef()
                      .at(0)
                      ->getMotif()
                      .toStdString() == "K");

            REQUIRE(
              cleavage_agent.getMotifsCstRef().at(0)->getCleavageAction() ==
              Enums::CleavageAction::CLEAVE);
            REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->getOffset() == 1);
            REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->isValid());
            REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->validate(
              &error_list_cleavage_agent));

            REQUIRE(cleavage_agent.getMotifsCstRef()
                      .at(1)
                      ->getMotif()
                      .toStdString() == "KP");
            REQUIRE(
              cleavage_agent.getMotifsCstRef().at(1)->getCleavageAction() ==
              Enums::CleavageAction::NO_CLEAVE);
            REQUIRE(cleavage_agent.getMotifsCstRef().at(1)->getOffset() == 1);
            REQUIRE(cleavage_agent.getMotifsCstRef().at(1)->isValid());
            REQUIRE(cleavage_agent.getMotifsCstRef().at(1)->validate(
              &error_list_cleavage_agent));

            REQUIRE(cleavage_agent.getMotifsCstRef()
                      .at(2)
                      ->getMotif()
                      .toStdString() == "R");
            REQUIRE(
              cleavage_agent.getMotifsCstRef().at(2)->getCleavageAction() ==
              Enums::CleavageAction::CLEAVE);
            REQUIRE(cleavage_agent.getMotifsCstRef().at(2)->getOffset() == 1);
            REQUIRE(cleavage_agent.getMotifsCstRef().at(2)->isValid());
            REQUIRE(cleavage_agent.getMotifsCstRef().at(2)->validate(
              &error_list_cleavage_agent));
          }
        }
      }
    }
  }
}

SCENARIO(
  "CleavageAgent objects can be copy- or assignment-constructed and compared",
  "[CleavageAgent]")
{
  test_utils_cleavage_agent_1_letter.initializeXpertmassLibrary();
  PolChemDefCstSPtr pol_chem_def_csp =
    test_utils_cleavage_agent_1_letter.msp_polChemDef;

  GIVEN("A CleavageAgent constructed with full parameters (with CleavageRules)")
  {
    CleavageAgent cleavage_agent(pol_chem_def_csp, "Trypsin", "K/;-K/P;R/");

    CleavageRuleSPtr cleavage_rule_sp = std::make_shared<CleavageRule>(
      pol_chem_def_csp, "Homoseryl", "A", "+CH3COOH", "M", "-CH2S+O");
    cleavage_agent.getRulesRef().push_back(cleavage_rule_sp);

    ErrorList error_list_cleavage_agent;

    cleavage_rule_sp = std::make_shared<CleavageRule>(pol_chem_def_csp,
                                                      "Heteroseryl",
                                                      "C",
                                                      "+CH3COOH+CH3COOH",
                                                      "N",
                                                      "-CH2S+O-CH2S+O");
    cleavage_agent.getRulesRef().push_back(cleavage_rule_sp);

    THEN("The CleavageAgent is valid and validates successfully")
    {
      REQUIRE(cleavage_agent.isValid());
      REQUIRE(cleavage_agent.validate(&error_list_cleavage_agent));

      REQUIRE(cleavage_agent.getMotifsCstRef().size() == 3);

      REQUIRE(
        cleavage_agent.getMotifsCstRef().at(0)->getMotif().toStdString() ==
        "K");
      REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->getCleavageAction() ==
              Enums::CleavageAction::CLEAVE);
      REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->getOffset() == 1);
      REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->isValid());
      REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->validate(
        &error_list_cleavage_agent));

      REQUIRE(
        cleavage_agent.getMotifsCstRef().at(1)->getMotif().toStdString() ==
        "KP");
      REQUIRE(cleavage_agent.getMotifsCstRef().at(1)->getCleavageAction() ==
              Enums::CleavageAction::NO_CLEAVE);
      REQUIRE(cleavage_agent.getMotifsCstRef().at(1)->getOffset() == 1);
      REQUIRE(cleavage_agent.getMotifsCstRef().at(1)->isValid());
      REQUIRE(cleavage_agent.getMotifsCstRef().at(1)->validate(
        &error_list_cleavage_agent));

      REQUIRE(
        cleavage_agent.getMotifsCstRef().at(2)->getMotif().toStdString() ==
        "R");
      REQUIRE(cleavage_agent.getMotifsCstRef().at(2)->getCleavageAction() ==
              Enums::CleavageAction::CLEAVE);
      REQUIRE(cleavage_agent.getMotifsCstRef().at(2)->getOffset() == 1);
      REQUIRE(cleavage_agent.getMotifsCstRef().at(2)->isValid());
      REQUIRE(cleavage_agent.getMotifsCstRef().at(2)->validate(
        &error_list_cleavage_agent));

      REQUIRE(cleavage_agent.getRulesCstRef().size() == 2);
    }

    WHEN("Two other CleavageAgent are pseudo-copy-constructed")
    {
      CleavageAgent other_cleavage_agent(cleavage_agent, Q_NULLPTR);

      THEN("They are identical to the original one")
      {
        REQUIRE(other_cleavage_agent.isValid());
        REQUIRE(other_cleavage_agent.validate(&error_list_cleavage_agent));

        REQUIRE(other_cleavage_agent.getMotifsCstRef().size() == 3);

        REQUIRE(other_cleavage_agent.getMotifsCstRef()
                  .at(0)
                  ->getMotif()
                  .toStdString() == "K");
        REQUIRE(
          other_cleavage_agent.getMotifsCstRef().at(0)->getCleavageAction() ==
          Enums::CleavageAction::CLEAVE);
        REQUIRE(other_cleavage_agent.getMotifsCstRef().at(0)->getOffset() == 1);
        REQUIRE(other_cleavage_agent.getMotifsCstRef().at(0)->isValid());
        REQUIRE(other_cleavage_agent.getMotifsCstRef().at(0)->validate(
          &error_list_cleavage_agent));

        REQUIRE(other_cleavage_agent.getMotifsCstRef()
                  .at(1)
                  ->getMotif()
                  .toStdString() == "KP");
        REQUIRE(
          other_cleavage_agent.getMotifsCstRef().at(1)->getCleavageAction() ==
          Enums::CleavageAction::NO_CLEAVE);
        REQUIRE(other_cleavage_agent.getMotifsCstRef().at(1)->getOffset() == 1);
        REQUIRE(other_cleavage_agent.getMotifsCstRef().at(1)->isValid());
        REQUIRE(other_cleavage_agent.getMotifsCstRef().at(1)->validate(
          &error_list_cleavage_agent));

        REQUIRE(other_cleavage_agent.getMotifsCstRef()
                  .at(2)
                  ->getMotif()
                  .toStdString() == "R");
        REQUIRE(
          other_cleavage_agent.getMotifsCstRef().at(2)->getCleavageAction() ==
          Enums::CleavageAction::CLEAVE);
        REQUIRE(other_cleavage_agent.getMotifsCstRef().at(2)->getOffset() == 1);
        REQUIRE(other_cleavage_agent.getMotifsCstRef().at(2)->isValid());
        REQUIRE(other_cleavage_agent.getMotifsCstRef().at(2)->validate(
          &error_list_cleavage_agent));

        REQUIRE(other_cleavage_agent.getRulesCstRef().size() == 2);
      }

      THEN("The comparison operators return correct results")
      {
        REQUIRE(other_cleavage_agent == cleavage_agent);
        REQUIRE_FALSE(other_cleavage_agent != cleavage_agent);
      }

      AND_WHEN("One CleavageAgent's name is modified")
      {
        cleavage_agent.setName("OtherName");

        THEN("The comparison operators return correct results")
        {
          REQUIRE_FALSE(other_cleavage_agent == cleavage_agent);
          REQUIRE(other_cleavage_agent != cleavage_agent);
        }
      }

      AND_WHEN("One CleavageAgent's motifs is removed")
      {
        cleavage_agent.setName("Trypsin");

        cleavage_agent.getMotifsRef().erase(
          cleavage_agent.getMotifsRef().begin());

        THEN("The comparison operators return correct results")
        {

          REQUIRE_FALSE(other_cleavage_agent == cleavage_agent);
          REQUIRE(other_cleavage_agent != cleavage_agent);
        }
      }

      AND_WHEN("One CleavageAgent's motif is modified")
      {
        cleavage_agent.initialize(other_cleavage_agent);
        REQUIRE(other_cleavage_agent == cleavage_agent);

        cleavage_agent.getMotifsRef().front()->setMotif("M");

        REQUIRE(
          cleavage_agent.getMotifsRef().front()->getMotif().toStdString() ==
          "M");

        THEN(
          "The two CleavageAgent instances are the same because the motif is a "
          "vector of MonomerSPtr and the modification is done to the managed "
          "object")
        {
          // The cleavage agents are still the same, because the
          // Motif is actually MonomerSPtr which are shared.
          REQUIRE(other_cleavage_agent == cleavage_agent);
          REQUIRE_FALSE(other_cleavage_agent != cleavage_agent);
        }
      }

      AND_WHEN("One CleavageAgent's cleavage rule is modified")
      {
        cleavage_agent.initialize(other_cleavage_agent);
        REQUIRE(other_cleavage_agent == cleavage_agent);

        cleavage_agent.getRulesRef().front()->setLeftCode("G");

        REQUIRE(
          cleavage_agent.getRulesRef().front()->getLeftCode().toStdString() ==
          "G");

        THEN(
          "The two CleavageAgent instances are the same because the rules are "
          "stored as a vector of CleavageRuleSPtr and the modification is done "
          "to the managed object")
        {
          REQUIRE(other_cleavage_agent == cleavage_agent);
          REQUIRE_FALSE(other_cleavage_agent != cleavage_agent);
        }
      }

      AND_WHEN("One CleavageAgent's cleavage rule is removed")
      {
        cleavage_agent.initialize(other_cleavage_agent);
        REQUIRE(other_cleavage_agent == cleavage_agent);

        cleavage_agent.getRulesRef().erase(
          cleavage_agent.getRulesRef().begin());

        THEN("The two CleavageAgent instances are different")
        {
          REQUIRE_FALSE(other_cleavage_agent == cleavage_agent);
          REQUIRE(other_cleavage_agent != cleavage_agent);
        }
      }
    }
  }
}

SCENARIO(
  "CleavageAgent objects can be initialized from the polymer chemistry "
  "definition",
  "[CleavageAgent]")
{
  PolChemDefCstSPtr pol_chem_def_csp =
    test_utils_cleavage_agent_1_letter.msp_polChemDef;

  WHEN("A CleavageAgent is initialized with name")
  {
    CleavageAgent cleavage_agent(pol_chem_def_csp, "Trypsin", "NOT_SET");

    THEN("The matching CleavageAgent can be retrieved from PolChemDef")
    {

      CleavageAgentCstSPtr cleavage_agent_csp =
        cleavage_agent.getFromPolChemDefByName();

      REQUIRE(cleavage_agent_csp != nullptr);
      REQUIRE(cleavage_agent_csp->getName().toStdString() == "Trypsin");
    }

    WHEN("A CleavageAgent is initialized with no name")
    {
      CleavageAgent cleavage_agent(pol_chem_def_csp, "", "NOT_SET");

      THEN("The matching CleavageAgent can not be retrieved from PolChemDef")
      {
        CleavageAgentCstSPtr cleavage_agent_csp =
          cleavage_agent.getFromPolChemDefByName();

        REQUIRE(cleavage_agent_csp == nullptr);
      }
    }

    WHEN("A CleavageAgent is initialized with proper name")
    {
      CleavageAgent cleavage_agent(pol_chem_def_csp, "Trypsin", "NOT_SET");

      THEN("The matching CleavageAgent is not known to the PolChemDef")
      {
        REQUIRE(cleavage_agent.isKnownByNameInPolChemDef() ==
                Enums::PolChemDefEntityStatus::ENTITY_KNOWN);
      }
    }

    WHEN("A CleavageAgent is initialized with no name")
    {
      CleavageAgent cleavage_agent(pol_chem_def_csp, "", "NOT_SET");

      THEN("The matching CleavageAgent is not known to the PolChemDef")
      {
        REQUIRE(cleavage_agent.isKnownByNameInPolChemDef() ==
                Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN);
      }
    }
  }
}

SCENARIO(
  "CleavageAgent objects can be initialized from a <cls> XML element and "
  "can export themselves as <cla>",
  "[CleavageAgent]")
{
  PolChemDefCstSPtr pol_chem_def_csp =
    test_utils_cleavage_agent_1_letter.msp_polChemDef;

  // int cls_element_index         = 0;
  // int name_element_index        = 1;
  // int name_text_index           = 2;
  // int pattern_element_index     = 3;
  // int pattern_text_index        = 4;
  // int clr_element_index         = 5;
  // int clr_name_element_index    = 6;
  // int clr_name_text_index       = 7;
  // int le_mnm_code_element_index = 8;
  // int le_mnm_code_text_index    = 9;
  // int le_formula_element_index  = 10;
  // int le_formula_text_index     = 11;
  // int re_mnm_code_element_index = 12;
  // int re_mnm_code_text_index    = 13;
  // int re_formula_element_index  = 14;
  // int re_formula_text_index     = 15;

  // FAKE cls!
  // <cls>
  //   <name>CyanogenBromide</name>
  //   <pattern>M/A</pattern>
  //   <clr>
  //     <le-mnm-code>M</le-mnm-code>
  //     <le-formula>-C1H2S1+O1</le-formula>
  //     <re-mnm-code>A</re-mnm-code>
  //     <re-formula>-CH3COOH</re-formula>
  //   </clr>
  // </cls>

  QStringList dom_strings{"cls",             // 0
                          "name",            // 1
                          "CyanogenBromide", // 2
                          "pattern",         // 3
                          "M/A",             // 4
                          "clr",             // 5
                          "name",            // 6
                          "Homoseryl",       // 7
                          "le-mnm-code",     // 8
                          "M",               // 9
                          "le-formula",      // 10
                          "-C1H2S1+O1",      // 11
                          "re-mnm-code",     // 12
                          "A",               // 13
                          "re-formula",      // 14
                          "-CH3COOH"};       // 15

  QDomDocument document =
    test_utils_cleavage_agent_1_letter.craftClsDomDocument(dom_strings);
  QDomElement cleavage_agent_element =
    document.elementsByTagName(dom_strings[0]).item(0).toElement();

  //  Use indentation 1 to mimick what happens in XpertMass.
  // qDebug().noquote() << "The document:\n'"
  //                    << document.toString(/*indentation*/ 1) << "'";

  //  We need to remove all spaces from the strings to be able to compare
  //  them. There must be a bug somewhere because with the original output, at
  //  the screen everything seems correct,  but test FAILED.

  QString document_string = document.toString(/*indentation*/ 0);
  document_string         = Utils::unspacify(document_string);

  //  We need two versions: cls for version 1 and cla for version 2.

  QString expected_cls_string =
    "<cls><name>CyanogenBromide</name><pattern>M/A</"
    "pattern><clr><name>Homoseryl</name><le-mnm-code>M</"
    "le-mnm-code><le-formula>-C1H2S1+O1</le-formula><re-mnm-code>A</"
    "re-mnm-code><re-formula>-CH3COOH</re-formula></clr></cls>";
  expected_cls_string = Utils::unspacify(expected_cls_string);

  QString expected_cla_string =
    "<cla><name>CyanogenBromide</name><pattern>M/A</"
    "pattern><clr><name>Homoseryl</name><le-mnm-code>M</"
    "le-mnm-code><le-formula>-C1H2S1+O1</le-formula><re-mnm-code>A</"
    "re-mnm-code><re-formula>-CH3COOH</re-formula></clr></cla>";
  expected_cla_string = Utils::unspacify(expected_cla_string);

  REQUIRE(document_string.toStdString() == expected_cls_string.toStdString());

  WHEN("Creating a CleavageAgent only with PolChemDef")
  {
    CleavageAgent cleavage_agent(pol_chem_def_csp);

    ErrorList error_list_cleavage_agent;

    AND_WHEN("Initializing it with an XML <cls> element")
    {
      REQUIRE(cleavage_agent.renderXmlClsElement(cleavage_agent_element,
                                                 /*version*/ 1));

      THEN("The initalized CleavageAgent is valid and validates successfully")
      {

        THEN("The CleavageAgent is valid and validates successfully")
        {
          REQUIRE(cleavage_agent.isValid());
          REQUIRE(cleavage_agent.validate(&error_list_cleavage_agent));
          REQUIRE(cleavage_agent.getName().toStdString() == "CyanogenBromide");
          REQUIRE(cleavage_agent.getPattern().toStdString() == "M/A");

          REQUIRE(cleavage_agent.getMotifsCstRef().size() == 1);

          REQUIRE(
            cleavage_agent.getMotifsCstRef().at(0)->getMotif().toStdString() ==
            "MA");
          REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->getCleavageAction() ==
                  Enums::CleavageAction::CLEAVE);
          REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->getOffset() == 1);
          REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->isValid());
          REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->validate(
            &error_list_cleavage_agent));

          REQUIRE(cleavage_agent.getRulesCstRef().size() == 1);

          REQUIRE(cleavage_agent.getCleavageRuleIndexByName("Homoseryl") == 0);

          CleavageRuleCstSPtr cleavage_rule_csp =
            cleavage_agent.getCleavageRuleCstSPtrByName("Homoseryl");
          REQUIRE(cleavage_rule_csp != nullptr);
          REQUIRE(cleavage_rule_csp->validate(&error_list_cleavage_agent));
          REQUIRE(cleavage_rule_csp->getName().toStdString() == "Homoseryl");
          REQUIRE(cleavage_rule_csp->getLeftCode().toStdString() == "M");
          REQUIRE(cleavage_rule_csp->getLeftFormula()
                    .getActionFormula()
                    .toStdString() == "-C1H2S1+O1");
          REQUIRE(cleavage_rule_csp->getRightCode().toStdString() == "A");
          REQUIRE(cleavage_rule_csp->getRightFormula()
                    .getActionFormula()
                    .toStdString() == "-CH3COOH");

          CleavageRuleSPtr cleavage_rule_sp =
            cleavage_agent.getCleavageRuleSPtrByName("Homoseryl");
          REQUIRE(cleavage_rule_sp != nullptr);
          REQUIRE(cleavage_rule_sp->validate(&error_list_cleavage_agent));
          REQUIRE(cleavage_rule_sp->getName().toStdString() == "Homoseryl");
          REQUIRE(cleavage_rule_sp->getLeftCode().toStdString() == "M");
          REQUIRE(cleavage_rule_sp->getLeftFormula()
                    .getActionFormula()
                    .toStdString() == "-C1H2S1+O1");
          REQUIRE(cleavage_rule_sp->getRightCode().toStdString() == "A");
          REQUIRE(cleavage_rule_sp->getRightFormula()
                    .getActionFormula()
                    .toStdString() == "-CH3COOH");
        }
      }

      AND_WHEN("Exporting itself as a <cla> XML element")
      {
        QString xml_text = cleavage_agent.formatXmlClaElement(/*offset*/ 1);
        xml_text         = Utils::unspacify(xml_text);

        THEN("The text must be identical to the expected XML <cla> string")
        {
          REQUIRE(xml_text.toStdString() == expected_cla_string.toStdString());
        }
      }
    }
  }
}

SCENARIO(
  "CleavageAgent objects can be initialized from a <cla> XML element and "
  "can "
  "export themselves as such also",
  "[CleavageAgent]")
{
  PolChemDefCstSPtr pol_chem_def_csp =
    test_utils_cleavage_agent_1_letter.msp_polChemDef;

  // int cla_element_index         = 0;
  // int name_element_index        = 1;
  // int name_text_index           = 2;
  // int pattern_element_index     = 3;
  // int pattern_text_index        = 4;
  // int clr_element_index         = 5;
  // int clr_name_element_index    = 6;
  // int clr_name_text_index       = 7;
  // int le_mnm_code_element_index = 8;
  // int le_mnm_code_text_index    = 9;
  // int le_formula_element_index  = 10;
  // int le_formula_text_index     = 11;
  // int re_mnm_code_element_index = 12;
  // int re_mnm_code_text_index    = 13;
  // int re_formula_element_index  = 14;
  // int re_formula_text_index     = 15;

  // FAKE cla!
  // <cla>
  //   <name>CyanogenBromide</name>
  //   <pattern>M/A</pattern>
  //   <clr>
  //     <le-mnm-code>M</le-mnm-code>
  //     <le-formula>-C1H2S1+O1</le-formula>
  //     <re-mnm-code>A</re-mnm-code>
  //     <re-formula>-CH3COOH</re-formula>
  //   </clr>
  // </cla>

  QStringList dom_strings{"cla",             // 0
                          "name",            // 1
                          "CyanogenBromide", // 2
                          "pattern",         // 3
                          "M/A",             // 4
                          "clr",             // 5
                          "name",            // 6
                          "Homoseryl",       // 7
                          "le-mnm-code",     // 8
                          "M",               // 9
                          "le-formula",      // 10
                          "-C1H2S1+O1",      // 11
                          "re-mnm-code",     // 12
                          "A",               // 13
                          "re-formula",      // 14
                          "-CH3COOH"};       // 15

  QDomDocument document =
    test_utils_cleavage_agent_1_letter.craftClaDomDocument(dom_strings);
  QDomElement cleavage_agent_element =
    document.elementsByTagName(dom_strings[0]).item(0).toElement();

  //  Use indentation 1 to mimick what happens in XpertMass.
  // qDebug().noquote() << "The document:\n'"
  //                    << document.toString(/*indentation*/ 1) << "'";

  //  We need to remove all spaces from the strings to be able to compare
  //  them. There must be a bug somewhere because with the original output, at
  //  the screen everything seems correct,  but test FAILED.

  QString document_string = document.toString(/*indentation*/ 0);
  document_string         = Utils::unspacify(document_string);

  QString expected_cla_string =
    "<cla><name>CyanogenBromide</name><pattern>M/A</"
    "pattern><clr><name>Homoseryl</name><le-mnm-code>M</"
    "le-mnm-code><le-formula>-C1H2S1+O1</le-formula><re-mnm-code>A</"
    "re-mnm-code><re-formula>-CH3COOH</re-formula></clr></cla>";
  expected_cla_string = Utils::unspacify(expected_cla_string);

  ErrorList error_list_cleavage_agent;

  REQUIRE(document_string.toStdString() == expected_cla_string.toStdString());

  WHEN("Creating a CleavageAgent only with PolChemDef")
  {
    CleavageAgent cleavage_agent(pol_chem_def_csp);

    AND_WHEN("Initializing it with an XML <cls> element")
    {
      REQUIRE(cleavage_agent.renderXmlClaElement(cleavage_agent_element,
                                                 /*version*/ 1));

      THEN("The initalized CleavageAgent is valid and validates successfully")
      {

        THEN("The CleavageAgent is valid and validates successfully")
        {
          REQUIRE(cleavage_agent.isValid());
          REQUIRE(cleavage_agent.validate(&error_list_cleavage_agent));
          REQUIRE(cleavage_agent.getName().toStdString() == "CyanogenBromide");
          REQUIRE(cleavage_agent.getPattern().toStdString() == "M/A");

          REQUIRE(cleavage_agent.getMotifsCstRef().size() == 1);

          REQUIRE(
            cleavage_agent.getMotifsCstRef().at(0)->getMotif().toStdString() ==
            "MA");
          REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->getCleavageAction() ==
                  Enums::CleavageAction::CLEAVE);
          REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->getOffset() == 1);
          REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->isValid());
          REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->validate(
            &error_list_cleavage_agent));

          REQUIRE(cleavage_agent.getRulesCstRef().size() == 1);

          CleavageRuleSPtr cleavage_rule_sp =
            cleavage_agent.getCleavageRuleSPtrByName("Homoseryl");
          REQUIRE(cleavage_rule_sp != nullptr);
          REQUIRE(cleavage_rule_sp->validate(&error_list_cleavage_agent));
          REQUIRE(cleavage_rule_sp->getName().toStdString() == "Homoseryl");
          REQUIRE(cleavage_rule_sp->getLeftCode().toStdString() == "M");
          REQUIRE(cleavage_rule_sp->getLeftFormula()
                    .getActionFormula()
                    .toStdString() == "-C1H2S1+O1");
          REQUIRE(cleavage_rule_sp->getRightCode().toStdString() == "A");
          REQUIRE(cleavage_rule_sp->getRightFormula()
                    .getActionFormula()
                    .toStdString() == "-CH3COOH");
        }
      }

      AND_WHEN("Exporting itself as a <cla> XML element")
      {
        QString xml_text = cleavage_agent.formatXmlClaElement(/*offset*/ 1);
        xml_text         = Utils::unspacify(xml_text);

        THEN("The text must be identical to the expected XML <cla> string")
        {
          REQUIRE(xml_text.toStdString() == expected_cla_string.toStdString());
        }
      }
    }
  }
}

} // namespace libXpertMassCore
} // namespace MsXpS
