#pragma once
#ifndef MESH_H
#define MESH_H

#include <memory>
#include <QtOpenGL>
#include <QOpenGLFunctions_4_3_Core>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <postprocess.h>
#include "texture.h"
#include "vertex.h"

#define positionIndex 0
#define normalIndex 1
#define uvIndex 2

#define EPSILON 0.00001

class Mesh
{
public:
    Mesh(QOpenGLFunctions_4_3_Core *f, QString filename);
    Mesh(QOpenGLFunctions_4_3_Core *f, Mesh *mesh, QVector<Vertex> &vertex_buffer, QVector<GLuint> &index_buffer);
    ~Mesh();

    void render(QOpenGLShaderProgram *shader, QMatrix4x4 V, QMatrix4x4 P, int subdivision, bool regular);
    const aiScene *scene;
    bool debug = true;

    struct SubdivEntry{
        SubdivEntry();
        ~SubdivEntry();
        GLuint VB_handle;

        GLuint IB_irregular_handle;
        GLuint IB_regular_handle;

        QVector<Vertex> vertices;
        QVector<unsigned int> indices_irregular;
        QVector<unsigned int> indices_regular;

        QOpenGLFunctions_4_3_Core *f;

        void init(QOpenGLFunctions_4_3_Core *f, QVector<Vertex>& Vertices,
                  QVector<unsigned int>& Indices_irregular);
        void init(QOpenGLFunctions_4_3_Core *f, GLuint VB_handle, QVector<Vertex>& Vertices,
                  QVector<unsigned int>& Indices_irregular, QVector<unsigned int>& patches);
        void updateIndices();
    };

    struct MeshEntry{
        MeshEntry();
        ~MeshEntry();

        void init(QOpenGLFunctions_4_3_Core *f, QVector<Vertex>& Vertices, QVector<unsigned int>& Indices);
        void update(GLuint VB_handle, QVector<Vertex>& Vertices, QVector<unsigned int>& Indices_irregular, QVector<unsigned int>& patches);

        QString name;

        QVector<std::shared_ptr<SubdivEntry>> buffers;


        int materialIndex;
        QOpenGLFunctions_4_3_Core *f;

        QVector3D min;
        QVector3D max;
    };

    struct MaterialInfo
    {
        MaterialInfo();
        QString Name;
        QVector3D Diffuse;
        QVector3D Specular;
        bool hasTexture;
        Texture texture;
        float Shininess;
    };

    struct Node
    {
        Node();
        QString name;
        QMatrix4x4 transformation;
        QVector<int> meshes;
        QVector<Node> children;

        bool getFirstMeshIndex(int& index);//false if no mesh entry found
    };

    Mesh::Node getRootNode();
    Mesh::MeshEntry *getMeshEntry(int index);

private:
    Assimp::Importer importer;
    QMatrix4x4 globalInverseTransform;
    QMatrix4x4 screenTransform;

    QVector<MeshEntry> entries;
    QVector<MaterialInfo> materials;

    Node rootNode;
    QOpenGLFunctions_4_3_Core *f;
    bool loaded;

    void initMeshEntry(int i, aiMesh * entry);
    void initMaterial(QString dir, unsigned int i, const aiMaterial* material);
    void initNode(const aiScene *scene, aiNode *node, Node &newNode, QString debugoffset);

    void renderNode(QOpenGLShaderProgram *shader, Node &node, QMatrix4x4 V, QMatrix4x4 P, QMatrix4x4 M, int subdivision, bool regular);
    void renderMesh(QOpenGLShaderProgram *shader, int index, int subdivision, bool regular);

    void findObjectDimension(Node node, QMatrix4x4 transform, QVector3D &min, QVector3D &max);
};

#endif // MESH_H
