//
// Created by slinky on 5/7/26.
//

#include "Scene.h"

#include "Components.h"
#include "fmt/format.h"

Scene::Scene() {
    root = _registry.create();
    attach_component<Components::Transform>(root);
    attach_component<Components::Relationship>(root);
}

entt::entity Scene::create_game_object() {
    return create_game_object(root);
}

entt::entity Scene::create_game_object(entt::entity parent) {
    const entt::entity entity = _registry.create();
    attach_component<Components::Transform>(entity);
    attach_component<Components::Relationship>(entity);
    auto& tag = attach_component<Components::Tag>(entity);
    tag.name = fmt::format("entity {0}", (int)entity);

    add_child(parent, entity);

    return entity;
}

void Scene::update_transforms() {

}

void Scene::draw_scene(ShaderProgram *shader) {
    auto view = _registry.view<Components::Transform, Components::Drawable>();
    for (auto e : view) {
        const auto& transform = view.get<Components::Transform>(e);
        shader->setMat4("model", transform.model);

        const auto& drawable = view.get<Components::Drawable>(e);
        const Model* model = drawable.model.get();
        for (const auto& mesh: drawable.model->meshes) {
            unsigned int materialId = mesh->materialId;
            if (materialId >= model->materials.size()) {
                materialId = 0;
            }

            const std::shared_ptr<Material> mat = model->materials[materialId];

            shader->setVec3("phongAmbient",  mat->phong.ambient);
            shader->setVec3("phongDiffuse",  mat->phong.diffuse);
            shader->setVec3("phongSpecular", mat->phong.specular);
            shader->setFloat("phongShininess", mat->phong.shininess);

            glActiveTexture(GL_TEXTURE0);
            const auto diffuse = mat->diffuse;
            diffuse->bind();
            shader->setInt("diffuseMap", 0);

            glActiveTexture(GL_TEXTURE1);
            const auto specular = mat->specular;
            specular->bind();
            shader->setInt("specularMap", 1);

            glBindVertexArray(mesh.get()->vao);
            glDrawElements(GL_TRIANGLES, mesh->numIndices, GL_UNSIGNED_INT, 0);
        }
    }
}

entt::entity Scene::get_root() const {
    return root;
}

void Scene::add_child(entt::entity parent, entt::entity child) {
    auto& parentRelationship = fetch_component<Components::Relationship>(parent);

    if (parentRelationship.first_child == entt::null) {
        parentRelationship.first_child = child;
        auto& childRelationship = fetch_component<Components::Relationship>(child);
        childRelationship.parent = parent;
        return;
    }

    entt::entity lastSibling = parentRelationship.first_child;
    auto& lastSiblingRelationship = fetch_component<Components::Relationship>(lastSibling);
    while (lastSiblingRelationship.next_sibling != entt::null) {
        lastSibling = lastSiblingRelationship.next_sibling;
        lastSiblingRelationship = fetch_component<Components::Relationship>(lastSibling);
    }
    lastSiblingRelationship.next_sibling = child;

    auto& childRelationship = fetch_component<Components::Relationship>(child);
    childRelationship.prev_sibling = lastSibling;
    childRelationship.next_sibling = entt::null;
    childRelationship.parent = parent;
}
