#include <gtest/gtest.h>
#include <cstdint>
#include <memory>
#include <mutex>
#include <vector>
#include "renderive/renderable/Renderable.hpp"
#include "renderive/renderable/Renderable_Test_Harness.hpp"
#include "renderive/scene/Scene.hpp"
struct Scene2D_Render_Order_State {
    void append(std::uint64_t id) {
        std::lock_guard lock(mutex);
        render_order.push_back(id);
    }
    std::vector<std::uint64_t> snapshot() const {
        std::lock_guard lock(mutex);
        return render_order;
    }
    mutable std::mutex mutex;
    std::vector<std::uint64_t> render_order;
};
struct Scene2D_Render_Order_Renderable : Renderable_Test_Harness {
    Scene2D_Render_Order_Renderable(Scene2D_Render_Order_State& state, std::uint64_t id)
        : Renderable_Test_Harness({.cache_enabled = false}), state(&state), id(id) {}
    void prepare_for_test(const Prepare_Render_Context&) override {
        state->append(id);
    }
    void build_paint_graph_for_test(Renderable_Graph_Builder& graph) override {
        graph.emplace("paint", "Paint",
                      [this](const Paint_Render_Context& context) {
            dynamic_cast<Recording_Color_Cache&>(context.color_cache).append(id);
        });
    }
    Scene2D_Render_Order_State* state;
    std::uint64_t id;
};
TEST(scene2d_render_order_test, separates_dependency_order_from_display_order) {
    Scene2D_Context<> scene;
    Scene2D_Render_Order_State state;
    auto spectrum = renderive_Owner<Scene2D_Render_Order_Renderable>::make(state, 2);
    auto axis = renderive_Owner<Scene2D_Render_Order_Renderable>::make(state, 1);
    {
        auto attach = scene.attach_builder();
        attach.attach(spectrum);
        attach.attach(axis);
        attach.set_dependency_parent(spectrum, axis);
        attach.set_display_parent(axis, spectrum);
    }
    scene.render();
    scene.wait_for_render();
    EXPECT_EQ(state.snapshot(), (std::vector<std::uint64_t>{1, 2}));
    std::vector<std::uint64_t> final_values;
    scene.with_final_color_cache([&final_values](const Recording_Color_Cache& cache) {
        final_values.assign(cache.values.begin(), cache.values.end());
    });
    EXPECT_EQ(final_values.size(), 2);
    EXPECT_EQ(final_values.at(0), 2);
    EXPECT_EQ(final_values.at(1), 1);
}
TEST(scene2d_render_order_test, shared_overlay_waits_for_every_display_parent) {
    Scene2D_Context<> scene;
    Scene2D_Render_Order_State state;
    auto first_plot = renderive_Owner<Scene2D_Render_Order_Renderable>::make(state, 2);
    auto axis = renderive_Owner<Scene2D_Render_Order_Renderable>::make(state, 1);
    auto second_plot = renderive_Owner<Scene2D_Render_Order_Renderable>::make(state, 3);
    {
        auto attach = scene.attach_builder();
        attach.attach(first_plot);
        attach.attach(axis);
        attach.attach(second_plot);
        attach.add_dependency_parent(first_plot, axis);
        attach.add_dependency_parent(second_plot, axis);
        attach.add_display_parent(axis, first_plot);
        attach.add_display_parent(axis, second_plot);
    }
    scene.render();
    scene.wait_for_render();
    const auto render_order = state.snapshot();
    ASSERT_FALSE(render_order.empty());
    EXPECT_EQ(render_order.front(), 1);
    std::vector<std::uint64_t> final_values;
    scene.with_final_color_cache([&final_values](const Recording_Color_Cache& cache) {
        final_values.assign(cache.values.begin(), cache.values.end());
    });
    ASSERT_EQ(final_values.size(), 3);
    EXPECT_EQ(final_values.back(), 1);
}
