#include "catch2_common.h"

#include <type_traits>

#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/catch_template_test_macros.hpp>

SCENARIO("Event queue defaults to being empty")
{
    // it is also unlimited by default but we can't check that
    Tango::EventQueue eq;

    REQUIRE(eq.size() == 0);
    REQUIRE(eq.is_empty() == true);

    using namespace Catch::Matchers;
    using namespace TangoTest::Matchers;

    Tango::EventDataList event_data_list;
    eq.get_events(event_data_list);
    REQUIRE_THAT(event_data_list, IsEmpty());

    Tango::AttrConfEventDataList attr_conf_list;
    eq.get_events(attr_conf_list);
    REQUIRE_THAT(attr_conf_list, IsEmpty());

    Tango::DataReadyEventDataList data_ready_list;
    eq.get_events(data_ready_list);
    REQUIRE_THAT(data_ready_list, IsEmpty());

    Tango::DataReadyEventDataList dev_intr_change_list;
    eq.get_events(dev_intr_change_list);
    REQUIRE_THAT(dev_intr_change_list, IsEmpty());

    Tango::DataReadyEventDataList pipe_event_list;
    eq.get_events(pipe_event_list);
    REQUIRE_THAT(pipe_event_list, IsEmpty());

    REQUIRE_THROWS_MATCHES(
        eq.get_last_event_date(), Tango::DevFailed, FirstErrorMatches(Reason(Tango::API_EventQueues)));
}

// clang-format off
using EventList = std::tuple<
                             Tango::EventDataList,
                             Tango::AttrConfEventDataList,
                             Tango::DataReadyEventDataList,
                             Tango::DevIntrChangeEventDataList,
                             Tango::PipeEventDataList
                             >;
// clang-format on

TEMPLATE_LIST_TEST_CASE("catch2 event queue can be filled", "", EventList)
{
    using Element = std::remove_pointer_t<typename TestType::value_type>;

    Tango::EventQueue eq;
    auto elem = new Element();
    eq.insert_event(elem);

    REQUIRE(eq.size() == 1);
    REQUIRE(eq.is_empty() == false);
}

TEMPLATE_LIST_TEST_CASE("catch2 event queue can be filled and read out again", "", EventList)
{
    using namespace Catch::Matchers;

    using Element = std::remove_pointer_t<typename TestType::value_type>;

    Tango::EventQueue eq;
    auto elem = new Element();
    eq.insert_event(elem);

    TestType list;
    eq.get_events(list);
    REQUIRE_THAT(list, SizeIs(1));
    REQUIRE(list[0] == elem);
}

TEMPLATE_LIST_TEST_CASE("catch2 event queue last element can be retrieved (circular)", "", EventList)
{
    using namespace Catch::Matchers;
    using namespace TangoTest::Matchers;
    using namespace std::literals::chrono_literals;

    using Element = std::remove_pointer_t<typename TestType::value_type>;

    auto now = Tango::make_TimeVal(std::chrono::system_clock::now());

    Tango::EventQueue eq{1};
    auto elem0 = new Element();
    eq.insert_event(elem0);

    auto elem1 = new Element();
    elem1->reception_date = now;
    eq.insert_event(elem1);
    // maximum size of 1
    REQUIRE(eq.size() == 1);

    REQUIRE_THAT(eq.get_last_event_date(), WithinTimeAbsMatcher(now, 0ns));

    TestType list;
    eq.get_events(list);
    REQUIRE_THAT(list, SizeIs(1));
    REQUIRE(list[0] == elem1);
}

TEMPLATE_LIST_TEST_CASE("catch2 event queue last element can be retrieved (unlimited)", "", EventList)
{
    using namespace Catch::Matchers;
    using namespace TangoTest::Matchers;
    using namespace std::literals::chrono_literals;

    using Element = std::remove_pointer_t<typename TestType::value_type>;

    auto now = Tango::make_TimeVal(std::chrono::system_clock::now());

    Tango::EventQueue eq;
    auto elem0 = new Element();
    eq.insert_event(elem0);

    auto elem1 = new Element();
    elem1->reception_date = now;
    eq.insert_event(elem1);
    REQUIRE(eq.size() == 2);

    REQUIRE_THAT(eq.get_last_event_date(), WithinTimeAbsMatcher(now, 0ns));

    TestType list;
    eq.get_events(list);
    REQUIRE_THAT(list, SizeIs(2));
    REQUIRE(list[0] == elem0);
    REQUIRE(list[1] == elem1);
}
