#include <gtest/gtest.h>
#include <atomic>
#include <memory>
#include "renderive/renderable/Renderable.hpp"
#include "renderive/renderable/Renderable_Test_Harness.hpp"
#include "renderive/scene/Scene.hpp"
#include "Scene_Test_Helpers.hpp"
struct Scene3D_Context_Test_Renderable : Renderable_Test_Harness {
    explicit Scene3D_Context_Test_Renderable() = default;
    void prepare_for_test(const Prepare_Render_Context&) override {
        scene_is_3d = dynamic_cast<Scene_3D_Base*>(&scene_for_test()) != nullptr;
        ++render_count;
    }
    std::atomic<int> render_count{};
    bool scene_is_3d{};
};
TEST(scene3d_context_test, uses_scene_3d_base_contract) {
    Scene3D_Context<> scene;
    auto renderable = renderive_Owner<Scene3D_Context_Test_Renderable>::make();
    attach_initial(scene, renderable);
    scene.render();
    scene.wait_for_render();
    EXPECT_EQ(renderable->render_count.load(), 1);
    EXPECT_TRUE(renderable->scene_is_3d);
}
struct Scene3D_Dependency_Cache_Test_Renderable : Renderable_Test_Harness {
    explicit Scene3D_Dependency_Cache_Test_Renderable()
        : Renderable_Test_Harness({.cache_enabled = true}) {}
    void prepare_for_test(const Prepare_Render_Context&) override {
        ++render_count;
    }
    int render_count{};
};
TEST(scene3d_context_test, propagates_cache_invalidation_through_unsorted_dependency_chain) {
    Scene3D_Context<> scene;
    auto leaf = renderive_Owner<Scene3D_Dependency_Cache_Test_Renderable>::make();
    auto middle = renderive_Owner<Scene3D_Dependency_Cache_Test_Renderable>::make();
    auto root = renderive_Owner<Scene3D_Dependency_Cache_Test_Renderable>::make();
    with_attach_builder(scene, [&](auto& attach) {
        attach.attach(leaf);
        attach.attach(middle);
        attach.attach(root);
        attach.set_dependency_parent(leaf, middle);
        attach.set_dependency_parent(middle, root);
    });
    scene.render();
    scene.wait_for_render();
    EXPECT_EQ(root->render_count, 1);
    EXPECT_EQ(middle->render_count, 1);
    EXPECT_EQ(leaf->render_count, 1);
    root->invalidate_prepare_for_test();
    scene.render();
    scene.wait_for_render();
    EXPECT_EQ(root->render_count, 2);
    EXPECT_EQ(middle->render_count, 2);
    EXPECT_EQ(leaf->render_count, 2);
}
TEST(scene3d_context_test, runtime_edit_explicitly_reparents_children_before_detach) {
    Scene3D_Context<> scene;
    auto grandparent = renderive_Owner<Scene3D_Dependency_Cache_Test_Renderable>::make();
    auto parent = renderive_Owner<Scene3D_Dependency_Cache_Test_Renderable>::make();
    auto child = renderive_Owner<Scene3D_Dependency_Cache_Test_Renderable>::make();
    with_attach_builder(scene, [&](auto& attach) {
        attach.attach(grandparent);
        attach.attach(parent);
        attach.attach(child);
        attach.set_display_parent(parent, grandparent);
        attach.set_display_parent(child, parent);
        attach.set_dependency_parent(parent, grandparent);
        attach.set_dependency_parent(child, parent);
    });
    scene.render();
    scene.wait_for_render();
    EXPECT_EQ(child->render_count, 1);
    wait_renderable_edit(scene, [grandparent, parent, child](auto& editor) {
        editor.set_display_parent(child, grandparent);
        editor.set_dependency_parent(child, grandparent);
        editor.clear_display_parent(parent);
        editor.clear_dependency_parent(parent);
        editor.detach(parent);
    });
    const auto topology = scene.topology_snapshot();
    bool display_checked{};
    bool dependency_checked{};
    for (const auto& relation : topology.display) {
        if (relation.child.get() == child.get()) {
            EXPECT_EQ(relation.parent.get(), grandparent.get());
            display_checked = true;
        }
    }
    for (const auto& relation : topology.dependency) {
        if (relation.child.get() == child.get()) {
            EXPECT_EQ(relation.parent.get(), grandparent.get());
            dependency_checked = true;
        }
    }
    EXPECT_TRUE(display_checked);
    EXPECT_TRUE(dependency_checked);
    scene.render();
    scene.wait_for_render();
    EXPECT_EQ(parent->render_count, 1);
    EXPECT_EQ(child->render_count, 2);
}
