// ./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 "TestUtils.hpp"
#include "MsXpS/libXpertMassCore/IndexRange.hpp"

namespace MsXpS
{
namespace libXpertMassCore
{

SCENARIO("Construction of IndexRange instances", "[IndexRange]")
{
  GIVEN("Construction with no argument")
  {
    IndexRange index_range;

    WHEN("Checking for its values")
    {
      qsizetype start = index_range.m_start;
      qsizetype stop  = index_range.m_stop;

      THEN("The values should be std::numeric_limits<qsizetype>::max()")
      {
        REQUIRE(start == std::numeric_limits<qsizetype>::max());
        REQUIRE(stop == std::numeric_limits<qsizetype>::max());
      }
    }
  }

  GIVEN("Construction with start and stop params")
  {
    IndexRange index_range(50, 150);

    WHEN("Checking for its values")
    {
      qsizetype start = index_range.m_start;
      qsizetype stop  = index_range.m_stop;

      THEN("The values should be correctly set")
      {
        REQUIRE(start == 50);
        REQUIRE(stop == 150);
      }
    }

    WHEN("Pseudo copy-constructing another IndexRange")
    {
      IndexRange other_index_range(index_range);

      WHEN("Checking for its values")
      {
        qsizetype start = other_index_range.m_start;
        qsizetype stop  = other_index_range.m_stop;

        THEN("The values should be correctly set")
        {
          REQUIRE(start == 50);
          REQUIRE(stop == 150);
        }
      }
    }
  }
}

SCENARIO("Initialization functions", "[IndexRange]")
{
  GIVEN(
    "Construction of two instances, the first with arguments and the second "
    "without")
  {
    IndexRange index_range(50, 150);
    IndexRange other_index_range;

    WHEN("Initializing the second with the first")
    {
      other_index_range.initialize(index_range);

      qsizetype start = other_index_range.m_start;
      qsizetype stop  = other_index_range.m_stop;

      THEN("The values should be set correctly")
      {
        REQUIRE(start == index_range.m_start);
        REQUIRE(stop == index_range.m_stop);
      }
    }
  }
}

SCENARIO("Cloning functions", "[IndexRange]")
{
  GIVEN("Construction of one instance with arguments")
  {
    IndexRange index_range(50, 150);

    WHEN("Cloning it into a heap-allocated instance")
    {
      IndexRange *other_index_range_p = index_range.clone();

      qsizetype start = other_index_range_p->m_start;
      qsizetype stop  = other_index_range_p->m_stop;

      THEN("The values should be set correctly")
      {
        REQUIRE(start == index_range.m_start);
        REQUIRE(stop == index_range.m_stop);
      }
    }

    WHEN(
      "Using it for cloning it into a heap-allocated instance with static "
      "function")
    {
      IndexRange *other_index_range_p = IndexRange::clone(index_range);

      qsizetype start = other_index_range_p->m_start;
      qsizetype stop  = other_index_range_p->m_stop;

      THEN("The values should be set correctly")
      {
        REQUIRE(start == index_range.m_start);
        REQUIRE(stop == index_range.m_stop);
      }
    }
  }
}

SCENARIO("Equality and inequality operators", "[IndexRange]")
{
  GIVEN("Construction of two instancse with identical arguments")
  {
    IndexRange index_range(50, 150);
    IndexRange other_index_range(50, 150);

    THEN("The values should be identical in both instances")
    {
      REQUIRE(index_range == other_index_range);
      REQUIRE_FALSE(index_range != other_index_range);
    }

    WHEN("Changing the start value of the other instance")
    {
      other_index_range.m_start = 51;

      THEN("The values should be different in both instances")
      {
        REQUIRE(index_range != other_index_range);
        REQUIRE_FALSE(index_range == other_index_range);
      }

      WHEN("Changing the stop value of the other instance")
      {
        other_index_range.m_start = 50;
        other_index_range.m_stop  = 151;

        THEN("The values should be different in both instances")
        {
          REQUIRE(index_range != other_index_range);
          REQUIRE_FALSE(index_range == other_index_range);
        }

        WHEN("Changing both the start and stop values of the other instance")
        {
          other_index_range.m_start = 51;
          other_index_range.m_stop  = 151;

          THEN("The values should be different in both instances")
          {
            REQUIRE(index_range != other_index_range);
            REQUIRE_FALSE(index_range == other_index_range);
          }
        }
      }
    }
  }
}

SCENARIO("Sorting functions", "[IndexRange]")
{
  GIVEN("Construction of one instance with arguments")
  {
    IndexRange index_range(50, 150);

    WHEN("Sorting ascending")
    {
      index_range.sortAscending();

      qsizetype start = index_range.m_start;
      qsizetype stop  = index_range.m_stop;

      THEN("Nothing should be changed")
      {
        REQUIRE(start == 50);
        REQUIRE(stop == 150);
      }
    }

    WHEN("Sorting descending")
    {
      IndexRange checker_index_range(index_range);
      index_range.sortDescending();

      qsizetype start = index_range.m_start;
      qsizetype stop  = index_range.m_stop;

      THEN("The values should be swapped")
      {
        REQUIRE(start == 150);
        REQUIRE(stop == 50);
      }
    }
  }

  GIVEN(
    "Construction of one instance without parameters and setting values in "
    "descending order")
  {
    IndexRange index_range;

    index_range.m_start = 150;
    index_range.m_stop  = 50;

    WHEN("Sorting descending")
    {
      index_range.sortDescending();

      qsizetype start = index_range.m_start;
      qsizetype stop  = index_range.m_stop;

      THEN("Nothing should be changed")
      {
        REQUIRE(start == 150);
        REQUIRE(stop == 50);
      }
    }

    WHEN("Sorting ascending")
    {
      index_range.sortAscending();

      qsizetype start = index_range.m_start;
      qsizetype stop  = index_range.m_stop;

      THEN("Nothing should be changed")
      {
        REQUIRE(start == 50);
        REQUIRE(stop == 150);
      }
    }
  }
}

SCENARIO("Validity assessment", "[IndexRange]")
{
  GIVEN("Construction of one instance with ascending-order arguments")
  {
    IndexRange index_range(50, 150);

    WHEN("Checking Validity")
    {
      bool is_valid = index_range.isValid();

      THEN("The instance should be valid")
      {
        REQUIRE(is_valid);
      }
    }
  }

  GIVEN("Construction of one instance with uninitialized arguments")
  {
    IndexRange index_range;

    WHEN("Checking Validity")
    {
      bool is_valid = index_range.isValid();

      THEN("The instance should be invalid")
      {
        REQUIRE_FALSE(is_valid);
      }
    }

    WHEN("Setting only the start value")
    {
      index_range.m_start = 50;

      bool is_valid = index_range.isValid();

      THEN("The instance should be invalid")
      {
        REQUIRE_FALSE(is_valid);
      }

      AND_WHEN("Setting also the stop value")
      {
        index_range.m_stop = 150;

        bool is_valid = index_range.isValid();

        THEN("The instance should be valid")
        {
          REQUIRE(is_valid);
        }

        AND_WHEN("Setting only the stop value")
        {
          index_range.m_start = std::numeric_limits<qsizetype>::max();
          index_range.m_stop  = 150;

          bool is_valid = index_range.isValid();

          THEN("The instance should be invalid")
          {
            REQUIRE_FALSE(is_valid);
          }
        }
      }
    }
  }
}

SCENARIO("Resetting to default values", "[IndexRange]")
{
  GIVEN("Construction of one instance with ascending-order arguments")
  {
    IndexRange index_range(50, 150);

    WHEN("Checking Validity")
    {
      bool is_valid = index_range.isValid();

      THEN("The instance should be valid")
      {
        REQUIRE(is_valid);
      }

      AND_WHEN("Resetting the values and checking validity")
      {
        index_range.reset();

        bool is_valid = index_range.isValid();

        THEN("The instance should be valid")
        {
          REQUIRE_FALSE(is_valid);
        }
      }
    }
  }
}

SCENARIO("Outputting text string representing IndexRange instances",
         "[IndexRange]")
{
  GIVEN("Construction of one instance with ascending-order arguments")
  {
    IndexRange index_range(50, 150);

    WHEN("Outputting as string as indices")
    {
      QString index_range_as_text = index_range.indicesAsText();

      THEN("The string should be correct")
      {
        REQUIRE(index_range_as_text.toStdString() == "[50-150]");
      }

      WHEN("Outputting as string as positions")
      {
        QString index_range_as_text = index_range.positionsAsText();

        THEN("The string should be correct")
        {
          REQUIRE(index_range_as_text.toStdString() == "[51-151]");
        }
      }
    }
  }
}

} // namespace libXpertMassCore
} // namespace MsXpS
