#include <gtest/gtest.h>
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <vector>
#include "renderive/frame_control/Frame_Control.hpp"
struct Manual_Refresh_Test_Frame {
    int value{};
};
using Manual_Refresh_Test_Strategy = Manual_Refresh_Strategy<Manual_Refresh_Test_Frame>;
static_assert(Manual_Frame_Refresh_Strategy<Manual_Refresh_Test_Strategy>);
TEST(manual_refresh_strategy_test, publishes_only_after_manual_refresh) {
    Manual_Refresh_Test_Strategy strategy;
    {
        auto frame = strategy.acquire_painter();
        ASSERT_TRUE(frame);
        frame->value = 7;
    }
    {
        auto frame = strategy.acquire_renderer();
        EXPECT_FALSE(frame);
    }
    EXPECT_TRUE(strategy.refresh());
    {
        auto frame = strategy.acquire_renderer();
        ASSERT_TRUE(frame);
        EXPECT_EQ(frame->value, 7);
    }
}
TEST(manual_refresh_strategy_test, keeps_latest_prepared_frame) {
    Manual_Refresh_Test_Strategy strategy;
    {
        auto frame = strategy.acquire_painter();
        frame->value = 1;
    }
    {
        auto frame = strategy.acquire_painter();
        frame->value = 2;
    }
    EXPECT_EQ(strategy.state().replaced_prepared_frame_count, 1);
    ASSERT_TRUE(strategy.refresh());
    auto frame = strategy.acquire_renderer();
    ASSERT_TRUE(frame);
    EXPECT_EQ(frame->value, 2);
}
TEST(manual_refresh_strategy_test, reports_failed_refresh_without_pending_frame) {
    Manual_Refresh_Test_Strategy strategy;
    EXPECT_FALSE(strategy.refresh());
    EXPECT_EQ(strategy.state().failed_refresh_count, 1);
}
struct Manual_Refresh_Reentrant_Observer_Data {
    std::function<void(int)> callback;
};
struct Manual_Refresh_Reentrant_Observer {
    static constexpr bool enabled = true;
    std::shared_ptr<Manual_Refresh_Reentrant_Observer_Data> data{std::make_shared<Manual_Refresh_Reentrant_Observer_Data>()};
    template <class Observation>
    void observe(const Observation& observation) noexcept {
        if (data->callback) {
            data->callback(static_cast<int>(observation.event));
        }
    }
};
using Manual_Refresh_Reentrant_Observer_State = Observer_State<Manual_Refresh_Reentrant_Observer>;
using Manual_Refresh_Reentrant_Strategy = Manual_Refresh_Strategy<Manual_Refresh_Test_Frame, std::mutex, Manual_Refresh_Reentrant_Observer_State>;
TEST(manual_refresh_strategy_test, refresh_observer_can_acquire_renderer_without_deadlock) {
    Manual_Refresh_Reentrant_Observer observer;
    auto data = observer.data;
    Manual_Refresh_Reentrant_Strategy strategy{Manual_Refresh_Reentrant_Observer_State(observer)};
    {
        auto frame = strategy.acquire_painter();
        frame->value = 42;
    }
    std::atomic<int> observed_value{};
    data->callback = [&](int event) {
        if (event != static_cast<int>(Manual_Refresh_Reentrant_Strategy::Observation_Event::refresh_succeeded)) {
            return;
        }
        auto frame = strategy.acquire_renderer();
        if (frame) {
            observed_value.store(frame->value, std::memory_order_release);
        }
    };
    EXPECT_TRUE(strategy.refresh());
    EXPECT_EQ(observed_value.load(std::memory_order_acquire), 42);
}

TEST(manual_refresh_strategy_test, discard_is_counted_and_observed) {
    Manual_Refresh_Reentrant_Observer observer;
    auto data = observer.data;
    Manual_Refresh_Reentrant_Strategy strategy{Manual_Refresh_Reentrant_Observer_State(observer)};
    std::vector<int> events;
    data->callback = [&events](int event) { events.push_back(event); };
    {
        auto frame = strategy.acquire_painter();
        frame->value = 21;
    }
    ASSERT_TRUE(strategy.discard_pending_frame());
    EXPECT_FALSE(strategy.discard_pending_frame());
    const auto state = strategy.state();
    EXPECT_FALSE(state.pending_frame);
    EXPECT_EQ(state.discarded_prepared_frame_count, 1u);
    ASSERT_EQ(events.size(), 2u);
    EXPECT_EQ(events.back(), static_cast<int>(Manual_Refresh_Reentrant_Strategy::Observation_Event::manually_discarded));
}
