#pragma once
#include <vector>
#include <memory>
#include <span>
#include <glm/glm.hpp>
#include "glad.h"

// Vertex structure with alignment for GPU (C++11 alignas)
struct alignas(16) Vertex {
    glm::vec3 position{0.0f};
    glm::vec3 normal{0.0f, 1.0f, 0.0f};
    glm::vec2 tex_coords{0.0f};
    glm::vec3 tangent{0.0f};
    glm::vec3 bitangent{0.0f};
};

// Modern mesh class using C++23 features
class Mesh {
public:
    // Constructor using spans (C++20) for better safety
    explicit Mesh(std::span<const Vertex> vertices, std::span<const unsigned int> indices = {});
    
    // Constructor with move semantics for large meshes
    Mesh(std::vector<Vertex>&& vertices, std::vector<unsigned int>&& indices = {});
    
    ~Mesh();

    // Delete copy constructor and assignment (expensive operation)
    Mesh(const Mesh&) = delete;
    Mesh& operator=(const Mesh&) = delete;

    // Move semantics
    Mesh(Mesh&& other) noexcept;
    Mesh& operator=(Mesh&& other) noexcept;

    void Draw() const;
    void DrawInstanced(int instance_count) const;

    // Modern getters using [[nodiscard]]
    [[nodiscard]] size_t GetVertexCount() const { return vertex_count_; }
    [[nodiscard]] size_t GetIndexCount() const { return index_count_; }
    [[nodiscard]] bool HasIndices() const { return index_count_ > 0; }
    [[nodiscard]] GLuint GetVAO() const { return VAO_; }

    // Static factory methods for common shapes
    [[nodiscard]] static std::unique_ptr<Mesh> CreateQuad();
    [[nodiscard]] static std::unique_ptr<Mesh> CreateCube();
    [[nodiscard]] static std::unique_ptr<Mesh> CreateSphere(int sectors = 36, int stacks = 18);
    [[nodiscard]] static std::unique_ptr<Mesh> CreatePlane(float width = 1.0f, float height = 1.0f, int subdivisions = 1);

    // Utility functions
    void UpdateVertices(std::span<const Vertex> new_vertices);
    void UpdateIndices(std::span<const unsigned int> new_indices);
    
    // Calculate mesh properties
    [[nodiscard]] glm::vec3 CalculateCenter() const;
    [[nodiscard]] std::pair<glm::vec3, glm::vec3> CalculateBoundingBox() const;
    
    // Debug functions
    void PrintInfo() const;

private:
    GLuint VAO_ = 0, VBO_ = 0, EBO_ = 0;
    size_t vertex_count_ = 0;
    size_t index_count_ = 0;

    void SetupMesh(std::span<const Vertex> vertices, std::span<const unsigned int> indices);
    void Cleanup();
    
    // Vertex attribute setup using constexpr (C++11)
    static constexpr void SetupVertexAttributes() {
        // Position attribute (location = 0)
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
        glEnableVertexAttribArray(0);
        
        // Normal attribute (location = 1)  
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal));
        glEnableVertexAttribArray(1);
        
        // Texture coordinate attribute (location = 2)
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, tex_coords));
        glEnableVertexAttribArray(2);
        
        // Tangent attribute (location = 3)
        glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, tangent));
        glEnableVertexAttribArray(3);
        
        // Bitangent attribute (location = 4)
        glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, bitangent));
        glEnableVertexAttribArray(4);
    }
};