#include <gfx/mesh.hpp>

#include <filein/obj_loader.hpp>
#include <filein/texture_loader.hpp>

#include <vector>

Mesh::Mesh(std::vector<float> const &_vertices, 
           std::vector<float> const &_normals,
           std::vector<unsigned int> const &_indices, 
           glm::vec3 _color,
           std::string _shader_type)
 : material{_color}, shader_type{_shader_type}, 
   aabb{_vertices}, custom_aabb{glm::vec3(-0.5f, -0.5f, 0.5f), glm::vec3(0.5f, 0.5f, 0.5f)}
{
    _VAO->bind();
    sendDataToVAO(_vertices, _normals, _indices, std::vector<float>{});
    _VAO->bindBuffers(_vertices, _normals, _indices, std::vector<float>{});
}

Mesh::Mesh(std::vector<float> const &_vertices, 
           std::vector<float> const &_normals, 
           std::vector<unsigned int> const &_indices, 
           std::vector<float> const &_texture_coordinates, 
           std::string _shader_type)
 : material{Material{}}, shader_type{_shader_type},
   aabb{_vertices}, custom_aabb{glm::vec3(-0.5f, -0.5f, 0.5f), glm::vec3(0.5f, 0.5f, 0.5f)}
{
    _VAO->bind();
    sendDataToVAO(_vertices, _normals, _indices, _texture_coordinates);
    _VAO->bindBuffers(_vertices, _normals, _indices, _texture_coordinates);
}

void Mesh::sendDataToVAO(std::vector<float> const &_vertices, 
                         std::vector<float> const &_normals,
                         std::vector<unsigned int> const &_indices,
                         std::vector<float> const &_texture_coordinates)
{  
    _VAO->setVerticesSize(_vertices);
    _VAO->setNormalsSize(_normals);
    _VAO->setIndicesSize(_indices);
    _VAO->setTextureCoordinatesSize(_texture_coordinates);
}

void Mesh::draw() const
{
    _VAO->bind();

    if (getIndicesSize() == 0)
    {
        glDrawArrays(getDrawMode(),
                     0,
                     static_cast<GLsizei>(getVerticesSize()));
        ASSUMPTION(glGetError() == GL_NO_ERROR);
        
    }
    else
    {
        glDrawElements(getDrawMode(),
                       static_cast<GLsizei>(getIndicesSize()),
                       GL_UNSIGNED_INT, 
                       0);
        ASSUMPTION(glGetError() == GL_NO_ERROR);
    }
}

void Mesh::setCustomAABB(glm::vec3 low, glm::vec3 high)
{
    custom_aabb = AABB(low, high);
}

mesh_ptr Mesh::loadMesh(const std::filesystem::path &obj_file, const std::filesystem::path texture_file)
{
    mesh_ptr mesh = load_object(obj_file);
    if (texture_file != "")
        mesh->texture = Texture::loadTexture(texture_file);

    return mesh;
}

mesh_ptr Mesh::loadMesh(const std::filesystem::path &obj_file, bool normalize, bool centralize, const std::filesystem::path texture_file)
{
    mesh_ptr mesh = load_object(obj_file, normalize, centralize);
    if (texture_file != "")
        mesh->texture = Texture::loadTexture(texture_file);

    return mesh;
}

mesh_ptr Mesh::loadMesh(const std::filesystem::path &obj_file, const std::vector<std::filesystem::path> &cubemap_texture_files)
{
    mesh_ptr mesh = load_object(obj_file);
    mesh->texture = Texture::loadTexture(cubemap_texture_files);

    return mesh;
}
