/////////////////////// 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/CleavageConfig.hpp"
#include "MsXpS/libXpertMassCore/Sequence.hpp"
#include "MsXpS/libXpertMassCore/Monomer.hpp"

namespace MsXpS
{
namespace libXpertMassCore
{

TestUtils test_utils_cleavage_config_1_letter("protein-1-letter", 1);
TestUtils test_utils_cleavage_config_3_letters("protein-3-letters", 1);

ErrorList error_list_cleavage_config;

SCENARIO("Construction of a CleavageConfig object", "[CleavageConfig]")
{
  test_utils_cleavage_config_1_letter.initializeXpertmassLibrary();
  PolChemDefCstSPtr pol_chem_def_csp =
    test_utils_cleavage_config_1_letter.msp_polChemDef;

  WHEN("A CleavageConfig constructed with all params")
  {
    CleavageConfig cleavage_config(pol_chem_def_csp,
                                   "Trypsin",
                                   "K/;-K/P;R/",
                                   0,
                                   /*is_sequence_embedded*/ true);

    cleavage_config.setStartIonizeLevel(1);
    cleavage_config.setStopIonizeLevel(10);

    THEN("All the members are set accordingly")
    {
      REQUIRE(cleavage_config.getPartials() == 0);
      REQUIRE(cleavage_config.getStartIonizeLevel() == 1);
      REQUIRE(cleavage_config.getStopIonizeLevel() == 10);
      REQUIRE(cleavage_config.isSequenceEmbedded());
    }

    AND_WHEN("Some data members are modified")
    {
      cleavage_config.setPartials(1);
      cleavage_config.setSequenceEmbedded(false);

      THEN("The corresponding members are updated")
      {
        REQUIRE(cleavage_config.getPartials() == 1);
        REQUIRE(cleavage_config.getStartIonizeLevel() == 1);
        REQUIRE(cleavage_config.getStopIonizeLevel() == 10);
        REQUIRE_FALSE(cleavage_config.isSequenceEmbedded());
      }

      AND_WHEN(
        "Another CleavageConfig instance is created by copy-construction or "
        "assignment")
      {
        CleavageConfig other_cleavage_config(cleavage_config);
        CleavageConfig another_cleavage_config(other_cleavage_config,
                                               Q_NULLPTR);

        THEN(
          "The other CleavageConfig instances are identical to the initial one")
        {
          REQUIRE(another_cleavage_config.getPartials() == 1);
          REQUIRE(another_cleavage_config.getStartIonizeLevel() == 1);
          REQUIRE(another_cleavage_config.getStopIonizeLevel() == 10);
          REQUIRE_FALSE(another_cleavage_config.isSequenceEmbedded());
        }
      }
    }
  }
}

SCENARIO("CleavageConfig objects can be compared with operators ==() and !=().",
         "[CleavageConfig]")
{
  test_utils_cleavage_config_1_letter.initializeXpertmassLibrary();
  PolChemDefCstSPtr pol_chem_def_csp =
    test_utils_cleavage_config_1_letter.msp_polChemDef;

  WHEN("Two identical CleavageConfig objects are constructed with all params")
  {
    CleavageConfig cleavage_config_1(pol_chem_def_csp,
                                     "Trypsin",
                                     "K/;-K/P;R/",
                                     0 /*partials*/,
                                     /*is_sequence_embedded*/ true);

    cleavage_config_1.setStartIonizeLevel(1);
    cleavage_config_1.setStopIonizeLevel(10);

    CleavageConfig cleavage_config_2(pol_chem_def_csp,
                                     "Trypsin",
                                     "K/;-K/P;R/",
                                     0,
                                     /*is_sequence_embedded*/ true);

    // Choose on purpose this variant :-)
    cleavage_config_2.setIonizeLevels(1, 10);


    THEN("All the members are set accordingly")
    {
      REQUIRE(cleavage_config_1.getName().toStdString() == "Trypsin");
      REQUIRE(cleavage_config_1.getPattern().toStdString() == "K/;-K/P;R/");
      REQUIRE(cleavage_config_1.isValid());

      REQUIRE(cleavage_config_1.getPartials() == 0);
      REQUIRE(cleavage_config_1.getStartIonizeLevel() == 1);
      REQUIRE(cleavage_config_1.getStopIonizeLevel() == 10);
      REQUIRE(cleavage_config_1.isSequenceEmbedded());

      REQUIRE(cleavage_config_2.getName().toStdString() == "Trypsin");
      REQUIRE(cleavage_config_2.getPattern().toStdString() == "K/;-K/P;R/");
      REQUIRE(cleavage_config_2.isValid());

      REQUIRE(cleavage_config_2.getPartials() == 0);
      REQUIRE(cleavage_config_2.getStartIonizeLevel() == 1);
      REQUIRE(cleavage_config_2.getStopIonizeLevel() == 10);
      REQUIRE(cleavage_config_2.isSequenceEmbedded());
    }

    AND_THEN("The comparison operators return proper results")
    {
      REQUIRE(cleavage_config_1 == cleavage_config_2);
      REQUIRE_FALSE(cleavage_config_1 != cleavage_config_2);
    }
  }
}

SCENARIO("CleavageConfig objects can be copy- or assignment- constructed",
         "[CleavageConfig]")
{
  test_utils_cleavage_config_1_letter.initializeXpertmassLibrary();
  PolChemDefCstSPtr pol_chem_def_csp =
    test_utils_cleavage_config_1_letter.msp_polChemDef;

  WHEN("A CleavageConfig constructed with all params")
  {
    CleavageConfig cleavage_config(pol_chem_def_csp,
                                   "Trypsin",
                                   "K/;-K/P;R/",
                                   0,
                                   /*is_sequence_embedded*/ true);

    cleavage_config.setStartIonizeLevel(1);
    cleavage_config.setStopIonizeLevel(10);

    THEN("All the members are set accordingly")
    {
      REQUIRE(cleavage_config.getPartials() == 0);
      REQUIRE(cleavage_config.getStartIonizeLevel() == 1);
      REQUIRE(cleavage_config.getStopIonizeLevel() == 10);
      REQUIRE(cleavage_config.isSequenceEmbedded());
    }

    AND_WHEN("New CleavageConfig objects are instantiated")
    {
      CleavageConfig another_cleavage_config(cleavage_config);
      CleavageConfig other_cleavage_config(another_cleavage_config, Q_NULLPTR);

      THEN("The copies must be identical to the original object")
      {
        REQUIRE(other_cleavage_config.getPartials() == 0);
        REQUIRE(other_cleavage_config.getStartIonizeLevel() == 1);
        REQUIRE(other_cleavage_config.getStopIonizeLevel() == 10);
        REQUIRE(other_cleavage_config.isSequenceEmbedded());

        REQUIRE(other_cleavage_config == cleavage_config);
        REQUIRE_FALSE(other_cleavage_config != cleavage_config);
      }
    }
  }
}

SCENARIO("Construction of an empty CleavageConfig object", "[CleavageConfig]")
{
  test_utils_cleavage_config_1_letter.initializeXpertmassLibrary();
  PolChemDefCstSPtr pol_chem_def_csp =
    test_utils_cleavage_config_1_letter.msp_polChemDef;

  WHEN("An empty CleavageConfig constructed")
  {
    CleavageConfig cleavage_config;

    AND_WHEN("A Cleavage agent is used to initialize it")
    {
      CleavageAgent cleavage_agent(pol_chem_def_csp, "Trypsin", "K/;-K/P;R/");

      cleavage_config.setCleavageAgent(cleavage_agent);

      AND_WHEN("The setter functions are used to configure the CleavageConfig")
      {
        cleavage_config.setPartials(0);
        cleavage_config.setSequenceEmbedded(false);
        cleavage_config.setStartIonizeLevel(1);
        cleavage_config.setStopIonizeLevel(10);

        THEN("The member data are correctly set")
        {
          REQUIRE(cleavage_config.getPartials() == 0);
          REQUIRE(cleavage_config.getStartIonizeLevel() == 1);
          REQUIRE(cleavage_config.getStopIonizeLevel() == 10);
          REQUIRE_FALSE(cleavage_config.isSequenceEmbedded());
        }
      }
    }
  }
}

} // namespace libXpertMassCore
} // namespace MsXpS
