#ifndef OBJECT_INCLUDED
#define OBJECT_INCLUDED

#include <common/aabb.hpp>
#include <common/objectbase.hpp>
#include <gfx/material.hpp>
#include <gfx/texture.hpp>
#include <gfx/vao.hpp>

#include <osi/opengl.hpp>
#include <utils/math.hpp>

#include <filesystem>
#include <memory>
#include <string>
#include <vector>

using VAO_ptr = std::shared_ptr<VAO>;

class Mesh;
using mesh_ptr = std::shared_ptr<Mesh>;

class Mesh : public ObjectBase
{
    VAO_ptr _VAO = std::make_shared<VAO>();

    Material material;
    texture_ptr texture = std::make_shared<Texture>();

    AABB aabb;
    AABB custom_aabb;

    std::string shader_type = "lit";
    GLenum draw_mode = GL_TRIANGLES;

    bool visible = true;
    bool animated = false;
    bool lightsource = false;
    bool selectable = true;

    void sendDataToVAO(std::vector<float> const &_vertices, 
                       std::vector<float> const &_normals,
                       std::vector<unsigned int> const &_indices,
                       std::vector<float> const &_texture_coordinates);
    

public:
    /* LEGACY CONSTRUCTOR */
    Mesh(std::vector<float> const &_vertices,
         std::vector<float> const &_normals = {},
         std::vector<unsigned int> const &_indices = {},
         glm::vec3 _color = glm::vec3(1.0f, 0.5f, 0.31f),
         std::string _shader_type = "lit");
    /*--------------------*/

    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 = "lit");

    // Mesh(std::vector<float> const &_vertices,
    //      std::vector<float> const &_normals = {},
    //      std::vector<unsigned int> const &_indices = {},
    //      Material _material = Material{},
    //      std::filesystem::path path = "");

    const VAO* getVAO() const { return _VAO.get(); }

    void draw() const;

    const std::size_t getVerticesSize() const { return _VAO->getVerticesSize(); }
    const std::size_t getIndicesSize() const { return _VAO->getIndicesSize(); }
    const std::size_t getNormalsSize() const { return _VAO->getNormalsSize(); }

    std::string const &getShaderType() const { return shader_type; }
    void setShaderType(std::string _shader_type) { shader_type = _shader_type; }

    const GLenum getDrawMode() const { return draw_mode; }
    void setDrawMode(GLenum _draw_mode) { draw_mode = _draw_mode; }

    bool hasTexture() const { return texture->getID() != 0; }

    bool isVisible() const { return visible; }
    void setVisible(bool _visible) { visible = _visible; }

    bool isAnimated() const { return animated; }
    void setAnimated(bool _animated) { animated = _animated; }

    bool isLightsource() const { return lightsource; }
    void setLightsource(bool _lightsource) { lightsource = _lightsource; }

    bool isSelectable() const { return selectable; }
    void setSelectability(bool _selectable) { selectable = _selectable; }

    glm::vec4 getColor() const { return material.diffuse_color; }
    void setColor(glm::vec3 color) { material.diffuse_color = glm::vec4(color, material.diffuse_color.w); }

    const Material &getMaterial() const { return material; }
    void setMaterial(Material _material) { material = _material; }

    texture_ptr getTexture() const { return texture; }
    void setTexture(GLuint id) { texture = std::make_shared<Texture>(id); }
    void setTexture(texture_ptr _texture) { texture = _texture; }
    void setTexture(const std::filesystem::path &texture_file) { setTexture(Texture::loadTexture(texture_file)); }

    const AABB &getAABB() const { return aabb; }
    const AABB &getCustomAABB() const { return custom_aabb; }
    void setCustomAABB(glm::vec3 low, glm::vec3 high);

    static mesh_ptr loadMesh(const std::filesystem::path &obj_file, const std::filesystem::path texture_file = "");
    static mesh_ptr loadMesh(const std::filesystem::path &obj_file, bool normalize, bool centralize, const std::filesystem::path texture_file = "");
    static mesh_ptr loadMesh(const std::filesystem::path &obj_file, const std::vector<std::filesystem::path> &cubemap_texture_files);
};

#endif