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


/////////////////////// 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 "MsXpS/libXpertMassCore/CalcOptions.hpp"

namespace MsXpS
{
namespace libXpertMassCore
{


SCENARIO("CalcOptions object can be constructed empty", "[CalcOptions]")
{
  WHEN("Constructing an empty CalcOptions")
  {
    CalcOptions calc_options;

    THEN("The object is configured with default values")
    {
      REQUIRE_FALSE(calc_options.isDeepCalculation());

      REQUIRE(calc_options.getMassType() == Enums::MassType::BOTH);
      REQUIRE(calc_options.getCapType() == Enums::CapType::BOTH);
      REQUIRE(calc_options.getMonomerEntities() == Enums::ChemicalEntity::NONE);
      REQUIRE(calc_options.getPolymerEntities() == Enums::ChemicalEntity::NONE);
      REQUIRE(calc_options.getSelectionType() ==
              Enums::SelectionType::OLIGOMERS);

      REQUIRE(calc_options.getIndexRangeCollectionCstRef().size() == 0);
      REQUIRE(calc_options.getIndexRangeCollectionRef().size() == 0);
    }

    AND_WHEN("Manually setting the members to other values")
    {
      calc_options.setDeepCalculation(true);
      calc_options.setMassType(Enums::MassType::MONO);
      calc_options.setSelectionType(Enums::SelectionType::RESIDUAL_CHAINS);
      calc_options.setCapType(Enums::CapType::LEFT);
      calc_options.setMonomerEntities(
        Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER);
      calc_options.setPolymerEntities(Enums::ChemicalEntity::LEFT_END_MODIF);

      THEN("All the values are properly set.")
      {
        REQUIRE(calc_options.isDeepCalculation());

        REQUIRE(calc_options.getMassType() == Enums::MassType::MONO);
        REQUIRE(calc_options.getCapType() == Enums::CapType::LEFT);
        REQUIRE(calc_options.getMonomerEntities() ==
                Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER);
        REQUIRE(calc_options.getPolymerEntities() ==
                Enums::ChemicalEntity::LEFT_END_MODIF);
        REQUIRE(calc_options.getSelectionType() ==
                Enums::SelectionType::RESIDUAL_CHAINS);

        REQUIRE(calc_options.getIndexRangeCollectionCstRef().size() == 0);
        REQUIRE(calc_options.getIndexRangeCollectionRef().size() == 0);
      }
    }
  }
}

SCENARIO("CalcOptions object can be set with IndexRange instances",
         "[CalcOptions]")
{
  WHEN("Constructing empty CalcOptions and configuring IndexRange instances")
  {
    CalcOptions calc_options;
    CalcOptions calc_options_0;
    CalcOptions calc_options_1;
    CalcOptions calc_options_2;

    qsizetype index_range_start = 200;
    qsizetype index_range_stop  = 250;

    IndexRange index_range_0(0, 50);
    IndexRange index_range_1(51, 100);
    IndexRange index_range_2(101, 150);

    IndexRangeCollection index_range_collection;
    index_range_collection.appendIndexRange(index_range_0);
    index_range_collection.appendIndexRange(index_range_1);
    index_range_collection.appendIndexRange(index_range_2);

    calc_options.setIndexRange(index_range_start, index_range_stop);
    calc_options_0.setIndexRange(index_range_0);
    calc_options_1.setIndexRanges(index_range_collection);

    calc_options_2.setIndexRanges(index_range_collection);
    calc_options_2.setIndexRange(index_range_start, index_range_stop);

    THEN(
      "The member IndexRangeCollection must contain the right IndexRange "
      "instances")
    {
      REQUIRE(calc_options.getIndexRangeCollectionCstRef().size() == 1);
      REQUIRE(calc_options.getIndexRangeCollectionCstRef()
                .getRangesCstRef()
                .front()
                ->m_start == index_range_start);
      REQUIRE(calc_options.getIndexRangeCollectionCstRef()
                .getRangesCstRef()
                .front()
                ->m_stop == index_range_stop);

      REQUIRE(calc_options_0.getIndexRangeCollectionCstRef().size() == 1);
      REQUIRE(calc_options_0.getIndexRangeCollectionCstRef().getRangeCstRefAt(
                0) == index_range_0);

      REQUIRE(calc_options_1.getIndexRangeCollectionCstRef().size() == 3);
      REQUIRE(calc_options_1.getIndexRangeCollectionCstRef().getRangeCstRefAt(
                0) == index_range_0);
      REQUIRE(calc_options_1.getIndexRangeCollectionCstRef().getRangeCstRefAt(
                1) == index_range_1);
      REQUIRE(calc_options_1.getIndexRangeCollectionCstRef().getRangeCstRefAt(
                2) == index_range_2);

      REQUIRE(calc_options_2.getIndexRangeCollectionCstRef().size() == 1);
      REQUIRE(calc_options_2.getIndexRangeCollectionCstRef()
                .getRangesCstRef()
                .front()
                ->m_start == index_range_start);
      REQUIRE(calc_options_2.getIndexRangeCollectionCstRef()
                .getRangesCstRef()
                .front()
                ->m_stop == index_range_stop);
    }
  }
}

SCENARIO("CalcOptions objects can be pseudo-copy-constructed", "[CalcOptions]")
{
  WHEN("Construct a CalcOptions instance and configure it fully")
  {
    CalcOptions calc_options;

    calc_options.setDeepCalculation(true);
    calc_options.setMassType(Enums::MassType::MONO);
    calc_options.setSelectionType(Enums::SelectionType::RESIDUAL_CHAINS);
    calc_options.setCapType(Enums::CapType::LEFT);
    calc_options.setMonomerEntities(
      Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER);
    calc_options.setPolymerEntities(Enums::ChemicalEntity::LEFT_END_MODIF);

    IndexRange index_range_0(0, 50);
    IndexRange index_range_1(51, 100);
    IndexRange index_range_2(101, 150);

    IndexRangeCollection index_range_collection;
    index_range_collection.appendIndexRange(index_range_0);
    index_range_collection.appendIndexRange(index_range_1);
    index_range_collection.appendIndexRange(index_range_2);

    calc_options.setIndexRanges(index_range_collection);

    AND_WHEN("A new CalcOptions instance is copy-constructed")
    {
      CalcOptions other_calc_options(calc_options, Q_NULLPTR);

      THEN("The new instance must be identical to the first")
      {
        REQUIRE(other_calc_options.isDeepCalculation());

        REQUIRE(other_calc_options.getMassType() == Enums::MassType::MONO);
        REQUIRE(other_calc_options.getCapType() == Enums::CapType::LEFT);
        REQUIRE(other_calc_options.getMonomerEntities() ==
                Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER);
        REQUIRE(other_calc_options.getPolymerEntities() ==
                Enums::ChemicalEntity::LEFT_END_MODIF);
        REQUIRE(other_calc_options.getSelectionType() ==
                Enums::SelectionType::RESIDUAL_CHAINS);

        REQUIRE(other_calc_options.getIndexRangeCollectionCstRef().size() == 3);
        REQUIRE(
          other_calc_options.getIndexRangeCollectionCstRef().getRangeCstRefAt(
            0) == index_range_0);
        REQUIRE(
          other_calc_options.getIndexRangeCollectionCstRef().getRangeCstRefAt(
            1) == index_range_1);
        REQUIRE(
          other_calc_options.getIndexRangeCollectionCstRef().getRangeCstRefAt(
            2) == index_range_2);
      }
    }
  }
}

SCENARIO("CalcOptions instances can be initialized using another instance",
         "[CalcOptions]")
{
  WHEN("Construct a CalcOptions instance and configure it fully")
  {
    CalcOptions calc_options;

    calc_options.setDeepCalculation(true);
    calc_options.setMassType(Enums::MassType::MONO);
    calc_options.setSelectionType(Enums::SelectionType::RESIDUAL_CHAINS);
    calc_options.setCapType(Enums::CapType::LEFT);
    calc_options.setMonomerEntities(
      Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER);
    calc_options.setPolymerEntities(Enums::ChemicalEntity::LEFT_END_MODIF);

    IndexRange index_range_0(0, 50);
    IndexRange index_range_1(51, 100);
    IndexRange index_range_2(101, 150);

    IndexRangeCollection index_range_collection;
    index_range_collection.appendIndexRange(index_range_0);
    index_range_collection.appendIndexRange(index_range_1);
    index_range_collection.appendIndexRange(index_range_2);

    calc_options.setIndexRanges(index_range_collection);

    AND_WHEN("A new CalcOptions instance is initialized using the first one")
    {
      CalcOptions other_calc_options;
      other_calc_options.initialize(calc_options);

      THEN("The new instance must be identical to the first")
      {
        REQUIRE(other_calc_options.isDeepCalculation());

        REQUIRE(other_calc_options.getMassType() == Enums::MassType::MONO);
        REQUIRE(other_calc_options.getCapType() == Enums::CapType::LEFT);
        REQUIRE(other_calc_options.getMonomerEntities() ==
                Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER);
        REQUIRE(other_calc_options.getPolymerEntities() ==
                Enums::ChemicalEntity::LEFT_END_MODIF);
        REQUIRE(other_calc_options.getSelectionType() ==
                Enums::SelectionType::RESIDUAL_CHAINS);

        REQUIRE(other_calc_options.getIndexRangeCollectionCstRef().size() == 3);
        REQUIRE(
          other_calc_options.getIndexRangeCollectionCstRef().getRangeCstRefAt(
            0) == index_range_0);
        REQUIRE(
          other_calc_options.getIndexRangeCollectionCstRef().getRangeCstRefAt(
            1) == index_range_1);
        REQUIRE(
          other_calc_options.getIndexRangeCollectionCstRef().getRangeCstRefAt(
            2) == index_range_2);
      }
    }
  }
}

SCENARIO("CalcOptions instances can be cloned", "[CalcOptions]")
{
  WHEN("Construct a CalcOptions instance and configure it fully")
  {
    CalcOptions calc_options;

    calc_options.setDeepCalculation(true);
    calc_options.setMassType(Enums::MassType::MONO);
    calc_options.setSelectionType(Enums::SelectionType::RESIDUAL_CHAINS);
    calc_options.setCapType(Enums::CapType::LEFT);
    calc_options.setMonomerEntities(
      Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER);
    calc_options.setPolymerEntities(Enums::ChemicalEntity::LEFT_END_MODIF);

    IndexRange index_range_0(0, 50);
    IndexRange index_range_1(51, 100);
    IndexRange index_range_2(101, 150);

    IndexRangeCollection index_range_collection;
    index_range_collection.appendIndexRange(index_range_0);
    index_range_collection.appendIndexRange(index_range_1);
    index_range_collection.appendIndexRange(index_range_2);

    calc_options.setIndexRanges(index_range_collection);

    AND_WHEN("A new CalcOptions instance is cloned using the first one")
    {
      CalcOptions *other_calc_options_p = calc_options.clone();

      THEN("The new instance must be identical to the first")
      {
        REQUIRE(other_calc_options_p->isDeepCalculation());

        REQUIRE(other_calc_options_p->getMassType() == Enums::MassType::MONO);
        REQUIRE(other_calc_options_p->getCapType() == Enums::CapType::LEFT);
        REQUIRE(other_calc_options_p->getMonomerEntities() ==
                Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER);
        REQUIRE(other_calc_options_p->getPolymerEntities() ==
                Enums::ChemicalEntity::LEFT_END_MODIF);
        REQUIRE(other_calc_options_p->getSelectionType() ==
                Enums::SelectionType::RESIDUAL_CHAINS);

        REQUIRE(other_calc_options_p->getIndexRangeCollectionCstRef().size() ==
                3);
        REQUIRE(other_calc_options_p->getIndexRangeCollectionCstRef()
                  .getRangeCstRefAt(0) == index_range_0);
        REQUIRE(other_calc_options_p->getIndexRangeCollectionCstRef()
                  .getRangeCstRefAt(1) == index_range_1);
        REQUIRE(other_calc_options_p->getIndexRangeCollectionCstRef()
                  .getRangeCstRefAt(2) == index_range_2);
      }
    }

    AND_WHEN(
      "A new CalcOptions instance is cloned using the first one (static "
      "function)")
    {
      CalcOptions *other_calc_options_p = CalcOptions::clone(calc_options);

      THEN("The new instance must be identical to the first")
      {
        REQUIRE(other_calc_options_p->isDeepCalculation());

        REQUIRE(other_calc_options_p->getMassType() == Enums::MassType::MONO);
        REQUIRE(other_calc_options_p->getCapType() == Enums::CapType::LEFT);
        REQUIRE(other_calc_options_p->getMonomerEntities() ==
                Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER);
        REQUIRE(other_calc_options_p->getPolymerEntities() ==
                Enums::ChemicalEntity::LEFT_END_MODIF);
        REQUIRE(other_calc_options_p->getSelectionType() ==
                Enums::SelectionType::RESIDUAL_CHAINS);

        REQUIRE(other_calc_options_p->getIndexRangeCollectionCstRef().size() ==
                3);
        REQUIRE(other_calc_options_p->getIndexRangeCollectionCstRef()
                  .getRangeCstRefAt(0) == index_range_0);
        REQUIRE(other_calc_options_p->getIndexRangeCollectionCstRef()
                  .getRangeCstRefAt(1) == index_range_1);
        REQUIRE(other_calc_options_p->getIndexRangeCollectionCstRef()
                  .getRangeCstRefAt(2) == index_range_2);
      }
    }
  }
}

SCENARIO("CalcOptions instances can compared", "[CalcOptions]")
{
  WHEN("Construct a CalcOptions instance and configure it fully")
  {
    CalcOptions calc_options;

    calc_options.setDeepCalculation(true);
    calc_options.setMassType(Enums::MassType::MONO);
    calc_options.setSelectionType(Enums::SelectionType::RESIDUAL_CHAINS);
    calc_options.setCapType(Enums::CapType::LEFT);
    calc_options.setMonomerEntities(
      Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER);
    calc_options.setPolymerEntities(Enums::ChemicalEntity::LEFT_END_MODIF);

    IndexRange index_range_0(0, 50);
    IndexRange index_range_1(51, 100);
    IndexRange index_range_2(101, 150);

    IndexRangeCollection index_range_collection;
    index_range_collection.appendIndexRange(index_range_0);
    index_range_collection.appendIndexRange(index_range_1);
    index_range_collection.appendIndexRange(index_range_2);

    calc_options.setIndexRanges(index_range_collection);

    AND_WHEN("A new CalcOptions instance is cloned using the first one")
    {
      CalcOptions other_calc_options(calc_options);

      THEN("The new instance must be identical to the first")
      {
        REQUIRE(calc_options == other_calc_options);
        REQUIRE_FALSE(calc_options != other_calc_options);
      }
    }
  }
}

SCENARIO("CalcOptions objects can be documented as strings", "[CalcOptions]")
{
  WHEN("Construct a CalcOptions instance and configure it fully")
  {
    CalcOptions calc_options;

    calc_options.setDeepCalculation(true);
    calc_options.setMassType(Enums::MassType::MONO);
    calc_options.setSelectionType(Enums::SelectionType::RESIDUAL_CHAINS);
    calc_options.setCapType(Enums::CapType::LEFT);
    calc_options.setMonomerEntities(
      Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER);
    calc_options.setPolymerEntities(Enums::ChemicalEntity::LEFT_END_MODIF);

    IndexRange index_range_0(0, 50);
    IndexRange index_range_1(51, 100);
    IndexRange index_range_2(101, 150);

    IndexRangeCollection index_range_collection;
    index_range_collection.appendIndexRange(index_range_0);
    index_range_collection.appendIndexRange(index_range_1);
    index_range_collection.appendIndexRange(index_range_2);

    calc_options.setIndexRanges(index_range_collection);

    AND_WHEN("This CalcOptions instance outputs itself as a string")
    {
      QString calc_options_text = calc_options.toString();

      qDebug().noquote() << "The text:" << calc_options_text;
    }
  }
}


} // namespace libXpertMassCore
} // namespace MsXpS
