#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_all.hpp>

#include <utils/Timer.hpp>

#include <chrono>
#include <sstream>
#include <thread>

// clazy:excludeall=non-pod-global-static

TEST_CASE("Timer", "[utils]")
{
  SECTION("auto start")
  {
    Timer t;

    REQUIRE(t.status() == Timer::Status::running);

    double seconds = t.seconds();
    std::this_thread::sleep_for(std::chrono::microseconds(5));
    REQUIRE(t.seconds() > seconds);

    t.start();
    seconds = t.seconds();
    std::this_thread::sleep_for(std::chrono::microseconds(5));
    REQUIRE(t.status() == Timer::Status::running);

    REQUIRE(t.seconds() > seconds);
  }

  SECTION("pause/start")
  {
    Timer t1;

    REQUIRE(t1.status() == Timer::Status::running);
    t1.pause();

    const double seconds = t1.seconds();
    REQUIRE(t1.status() == Timer::Status::paused);
    std::this_thread::sleep_for(std::chrono::microseconds(5));
    REQUIRE(t1.seconds() == seconds);

    std::stringstream os1;
    os1 << t1;

    Timer t2 = t1;
    std::stringstream os2;
    os2 << t2.seconds() << 's';

    REQUIRE(os1.str() == os2.str());

    REQUIRE(t1.seconds() == t2.seconds());
    t1.start();
    std::this_thread::sleep_for(std::chrono::microseconds(5));
    REQUIRE(t1.status() == Timer::Status::running);
    REQUIRE(t1.seconds() > t2.seconds());

    t2.reset();
    REQUIRE(t2.status() == Timer::Status::paused);
    REQUIRE(t2.seconds() == 0);

    using namespace std::chrono_literals;
    Timer t3{std::chrono::high_resolution_clock::now() - 24h};
    REQUIRE(t3.status() == Timer::Status::running);
    REQUIRE(t3.seconds() > 24 * 3600);
  }

  SECTION("stop/start")
  {
    Timer t;
    REQUIRE(t.status() == Timer::Status::running);

    std::this_thread::sleep_for(std::chrono::microseconds(5));
    const double seconds = t.seconds();

    REQUIRE(seconds > 0);

    t.stop();
    REQUIRE(t.status() == Timer::Status::stopped);
    REQUIRE(t.seconds() == 0);

    t.start();
    std::this_thread::sleep_for(std::chrono::microseconds(5));
    REQUIRE(t.status() == Timer::Status::running);
    REQUIRE(t.seconds() > 0);
  }
}