#pragma once

#include <entt/entt.hpp>
#include <flecs.h>
#include <glm/glm.hpp>

#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/quaternion.hpp>

struct Transform
{
    glm::vec3 translation{};
    glm::quat orientation{};
    glm::vec3 scale{1.0, 1.0, 1.0};
};

struct GlobalTransform
{
    GlobalTransform() = default;
    GlobalTransform(Transform const& transform)
    {
        // Translate * Rotate * Scale * vertex_vec;
        // First scaling, then rotation, then translation

        // Translate
        glm::mat4 translation_matrix = glm::translate(glm::mat4(1.0F), transform.translation);

        // Rotate
        glm::mat4 rotation_matrix = glm::toMat4(transform.orientation);

        // Scale
        glm::mat4 scale_matrix = glm::scale(glm::mat4(1.0F), transform.scale);

        this->transform = translation_matrix * rotation_matrix * scale_matrix;
    }

    glm::mat4 transform{};

    [[nodiscard]] auto position() const -> glm::vec3 { return transform[3]; };

    static void update(entt::registry& registry);
};

// namespace Transform {

struct TransformModule
{
    TransformModule(flecs::world& world)
    {
        world.system<GlobalTransform, Transform const>("PropagateTransform")
            .kind(flecs::PostUpdate)
            .each([](flecs::entity e,
                     GlobalTransform& global_transform,
                     Transform const& local_transform) {
                // There is no guarantee that the parent is iterated first. So there could be a two
                // frame delay.
                if (e.parent() == flecs::entity::null()) {
                    global_transform.transform = GlobalTransform(local_transform).transform;
                } else {
                    auto parent_global_transform = e.parent().get_mut<GlobalTransform>();
                    global_transform.transform = parent_global_transform->transform *
                                                 GlobalTransform(local_transform).transform;
                }
            });
    }
};

// } // namespace Transform
