#ifndef BOID_MODEL_HPP_
#define BOID_MODEL_HPP_

#include "abcg.hpp"

struct Vertex {
  glm::vec3 position{};
  glm::vec3 normal{};
  glm::vec2 texCoord{};

  bool operator==(const Vertex& other) const noexcept {
    static const auto epsilon{std::numeric_limits<float>::epsilon()};
    return glm::all(glm::epsilonEqual(position, other.position, epsilon)) &&
           glm::all(glm::epsilonEqual(normal, other.normal, epsilon)) &&
           glm::all(glm::epsilonEqual(texCoord, other.texCoord, epsilon));
  }
};

class Model {
    public:
        Model() = default;
        virtual ~Model();

        Model(const Model&) = delete;
        Model(Model&&) = default;
        Model& operator = (const Model&) = delete;
        Model& operator = (Model&&) = default;

        void loadDiffuseTexture(std::string_view path);
        void loadFromFile(std::string_view path, bool standardize = true);
        void render(int numTriangles = -1) const;
        void setupVAO(GLuint program);

        [[nodiscard]] int getNumTriangles() const {
            return static_cast<int>(m_indices.size()) / 3;
        }

        [[nodiscard]] glm::vec4 getKa() const { return m_Ka; }
        [[nodiscard]] glm::vec4 getKd() const { return m_Kd; }
        [[nodiscard]] glm::vec4 getKs() const { return m_Ks; }
        [[nodiscard]] float getShininess() const { return m_shininess; }

    private:
        GLuint m_VAO{};
        GLuint m_VBO{};
        GLuint m_EBO{};

        glm::vec4 m_Ka;
        glm::vec4 m_Kd;
        glm::vec4 m_Ks;
        float m_shininess;
        GLuint m_diffuseTexture{};

        std::vector<Vertex> m_vertices;
        std::vector<GLuint> m_indices;

        bool m_hasNormals{false};
        bool m_hasTexCoords{false};

        void createBuffers();
        void standardize();
        void computeNormals();
};

#endif