//
// Created by sebastian on 30.07.26.
//

#include "Model.h"

#include "spdlog/spdlog.h"
using namespace engine;
Model::Model(std::unique_ptr<Vao> vao, int vertexCount, bool isIndexed) : m_vao(std::move(vao)), m_vertexCount(vertexCount), m_isIndexed(isIndexed) {}

Model::Model(std::unique_ptr<Vao> vao, int vertexCount) : Model(std::move(vao), vertexCount, false) {}

void Model::bind() const {
    m_vao->bind();
}

void Model::unbind() const {
    m_vao->unbind();
}

Model::Model(std::unique_ptr<Vao> vao, int vertexCount, std::vector<MeshSection> sections)
    : m_vao(std::move(vao)), m_vertexCount(vertexCount), m_isIndexed(true), m_sections(std::move(sections)) {
    if (!sections.empty()) {
        spdlog::info("Calculate AABB for model {}", vao->getId());
        AABB aabb = sections[0].aabb;
        for (const auto& section : sections) {
            aabb.expandToInclude(section.aabb);
        }
        m_aabb = aabb;
    }
}

Model::Model(std::unique_ptr<Vao> vao, int vertexCount, std::vector<MeshSection> sections, AABB aabb) : m_vao(std::move(vao)), m_vertexCount(vertexCount), m_isIndexed(true), m_sections(std::move(sections)), m_aabb(aabb) {
}

void Model::draw() const {
    bind();
    if (m_sections.empty()) {
        // Legacy-Pfad: unverändert wie bisher, z.B. für CubeFactory-Primitive
        if (m_isIndexed) glDrawElements(GL_TRIANGLES, m_vertexCount, GL_UNSIGNED_INT, nullptr);
        else             glDrawArrays(GL_TRIANGLES, 0, m_vertexCount);
    } else {
        for (const auto& section : m_sections) {
            section.material->bind();
            glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(section.indexCount), GL_UNSIGNED_INT,
                            reinterpret_cast<const void*>(section.indexOffset * sizeof(unsigned int)));
        }
    }
    unbind();
}

void Model::drawSection(const std::string& name) const {
    auto it = std::ranges::find_if(m_sections,
                                   [&](const MeshSection& s) { return s.name == name; });
    if (it == m_sections.end()) {
        spdlog::warn("MeshSection '{}' nicht gefunden", name);
        return;
    }
    it->material->bind();
    bind();
    glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(it->indexCount), GL_UNSIGNED_INT,
                    reinterpret_cast<const void*>(it->indexOffset * sizeof(unsigned int)));
    unbind();
}

void Model::drawSectionRange(unsigned int indexOffset, unsigned int indexCount) const {
    glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(indexCount), GL_UNSIGNED_INT,
                    reinterpret_cast<const void*>(indexOffset * sizeof(unsigned int)));
}
