/////////////////////// 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/FragmentationConfig.hpp"

namespace MsXpS
{
namespace libXpertMassCore
{

TestUtils test_utils_fragmentation_config_1_letter("protein-1-letter", 1);
TestUtils test_utils_fragmentation_config_3_letters("protein-3-letters", 1);

ErrorList error_list_fragmentation_config;

SCENARIO("FragmentationConfig objects can be constructed in various ways",
         "[FragmentationConfig]")
{
  test_utils_fragmentation_config_1_letter.initializeXpertmassLibrary();
  PolChemDefCstSPtr pol_chem_def_csp =
    test_utils_fragmentation_config_1_letter.msp_polChemDef;

  WHEN(
    "Constructing of a FragmentationConfig instances with or without explicit "
    "FragmentationPathway object use")
  {
    FragmentationConfig fragmentation_config_1(pol_chem_def_csp,
                                               "a",
                                               "-C1O1",
                                               Enums::FragEnd::LE,
                                               "frag_a_comment",
                                               /*sequence_embedded*/ true);
    fragmentation_config_1.setStartIndex(0);
    fragmentation_config_1.setStopIndex(120);
    fragmentation_config_1.setStartIonizeLevel(1);
    fragmentation_config_1.setStopIonizeLevel(3);

    FragmentationPathway fragmentation_pathway(
      pol_chem_def_csp, "a", "-C1O1", Enums::FragEnd::LE, "frag_a_comment");

    // Trick to test the setIonizeLevels() function.
    FragmentationConfig fragmentation_config_2(
      fragmentation_pathway, 5, 8, /*sequence_embedded*/ true);
    fragmentation_config_2.setStartIndex(0);
    fragmentation_config_2.setStopIndex(120);
    fragmentation_config_2.setIonizeLevels(1, 3);

    fragmentation_config_1.addFormula(Formula("-H2O"));
    fragmentation_config_1.addFormula(Formula("-NH3"));

    fragmentation_config_2.addFormula(Formula("-H2O"));
    fragmentation_config_2.addFormula(Formula("-NH3"));

    THEN("All the member data are set correctly for both instances")
    {
      REQUIRE(fragmentation_config_1.getPolchemDefCstSPtr() ==
              pol_chem_def_csp);
      REQUIRE(fragmentation_config_1.getName().toStdString() == "a");
      REQUIRE(fragmentation_config_1.getFormulaCstRef()
                .getActionFormula()
                .toStdString() == "-C1O1");
      REQUIRE(fragmentation_config_1.getFormulaRef()
                .getActionFormula()
                .toStdString() == "-C1O1");
      REQUIRE(fragmentation_config_1.getFragEnd() == Enums::FragEnd::LE);
      REQUIRE(fragmentation_config_1.getComment().toStdString() ==
              "frag_a_comment");
      REQUIRE(fragmentation_config_1.getRulesCstRef().size() == 0);
      REQUIRE(fragmentation_config_1.getRulesRef().size() == 0);
      REQUIRE(fragmentation_config_1.getStartIonizeLevel() == 1);
      REQUIRE(fragmentation_config_1.getStopIonizeLevel() == 3);
      REQUIRE(fragmentation_config_1.isSequenceEmbedded() == true);
      REQUIRE(fragmentation_config_1.getFormulasRef().size() == 2);
      REQUIRE(fragmentation_config_1.getFormulasCstRef().size() == 2);
      REQUIRE(fragmentation_config_1.getFormulasCstRef()
                .front()
                .getActionFormula()
                .toStdString() == "-H2O");
      REQUIRE(fragmentation_config_1.getFormulasCstRef()
                .back()
                .getActionFormula()
                .toStdString() == "-NH3");

      REQUIRE(fragmentation_config_2.getPolchemDefCstSPtr() ==
              pol_chem_def_csp);
      REQUIRE(fragmentation_config_2.getName().toStdString() == "a");
      REQUIRE(fragmentation_config_2.getFormulaCstRef()
                .getActionFormula()
                .toStdString() == "-C1O1");
      REQUIRE(fragmentation_config_2.getFormulaRef()
                .getActionFormula()
                .toStdString() == "-C1O1");
      REQUIRE(fragmentation_config_2.getFragEnd() == Enums::FragEnd::LE);
      REQUIRE(fragmentation_config_2.getComment().toStdString() ==
              "frag_a_comment");
      REQUIRE(fragmentation_config_2.getRulesCstRef().size() == 0);
      REQUIRE(fragmentation_config_2.getRulesRef().size() == 0);
      REQUIRE(fragmentation_config_2.getStartIonizeLevel() == 1);
      REQUIRE(fragmentation_config_2.getStopIonizeLevel() == 3);
      REQUIRE(fragmentation_config_2.isSequenceEmbedded() == true);
      REQUIRE(fragmentation_config_2.getFormulasRef().size() == 2);
      REQUIRE(fragmentation_config_2.getFormulasCstRef().size() == 2);
      REQUIRE(fragmentation_config_2.getFormulasCstRef()
                .front()
                .getActionFormula()
                .toStdString() == "-H2O");
      REQUIRE(fragmentation_config_2.getFormulasCstRef()
                .back()
                .getActionFormula()
                .toStdString() == "-NH3");

      AND_WHEN("The copy constructor or the assignment operator are used")
      {
        FragmentationConfig another_fragmentation_config(
          fragmentation_config_1);
        FragmentationConfig other_fragmentation_config(
          pol_chem_def_csp, "NOT_SET", "NOT_SET");
        other_fragmentation_config = another_fragmentation_config;

        THEN("All the FragmentationConfig need to be identical")
        {
          REQUIRE(other_fragmentation_config.getPolchemDefCstSPtr() ==
                  pol_chem_def_csp);
          REQUIRE(other_fragmentation_config.getName().toStdString() == "a");
          REQUIRE(other_fragmentation_config.getFormulaCstRef()
                    .getActionFormula()
                    .toStdString() == "-C1O1");
          REQUIRE(other_fragmentation_config.getFormulaRef()
                    .getActionFormula()
                    .toStdString() == "-C1O1");
          REQUIRE(other_fragmentation_config.getFragEnd() == Enums::FragEnd::LE);
          REQUIRE(other_fragmentation_config.getComment().toStdString() ==
                  "frag_a_comment");
          REQUIRE(other_fragmentation_config.getRulesCstRef().size() == 0);
          REQUIRE(other_fragmentation_config.getRulesRef().size() == 0);
          REQUIRE(other_fragmentation_config.getStartIonizeLevel() == 1);
          REQUIRE(other_fragmentation_config.getStopIonizeLevel() == 3);
          REQUIRE(other_fragmentation_config.isSequenceEmbedded() == true);
          REQUIRE(other_fragmentation_config.getFormulasRef().size() == 2);
          REQUIRE(other_fragmentation_config.getFormulasCstRef().size() == 2);
          REQUIRE(other_fragmentation_config.getFormulasCstRef()
                    .front()
                    .getActionFormula()
                    .toStdString() == "-H2O");
          REQUIRE(other_fragmentation_config.getFormulasCstRef()
                    .back()
                    .getActionFormula()
                    .toStdString() == "-NH3");

          AND_WHEN("The FragmentationConfig outputs itself as a string")
          {
            QString text = other_fragmentation_config.toString();
            text = text.remove(QRegularExpression("\\s"));

            QString expected_text = QString("FragmentationConfig:\nFragmentation pathway:\nName: a - Formula: -C1O1 - Frag end: LE - Monomer contribution: 0\nisValid: true\n\nIndices [0-120] -- ionization levels [1-3].\nFormulas:\n-H2O -\n-NH3 -\n");
            expected_text = expected_text.remove(QRegularExpression("\\s"));

            THEN("The string matches the expected string.")
            {
              REQUIRE(text.toStdString() == expected_text.toStdString());
            }
          }
        }
      }
    }
  }
}


} // namespace libXpertMassCore
} // namespace MsXpS
