#pragma once
#ifndef SUBDIVISION_H
#define SUBDIVISION_H

#include <QtOpenGL>
#include <QOpenGLFunctions_4_3_Core>

#include "mesh.h"
#include "triangle.h"

class Subdivision
{
public:
    Subdivision(QOpenGLFunctions_4_3_Core *f);
    ~Subdivision();

    void init();
    void splitRegular(Mesh *mesh);
    void subdivide(Mesh *mesh, int level);

private:
    struct Input
    {
        GLuint vb_handle;
        QVector<unsigned int> index_irregular_buffer;
        QVector<unsigned int> index_regular_buffer;
        QVector<Vertex> vertex_buffer;
    };

    struct Tables
    {
        QVector<GLuint> edge_indices;
        QVector<GLuint> vertex_indices;
        QVector<GLuint> vertex_offsets;
        QVector<GLuint> index_buffer;
        QVector<GLuint> patch_index_regular;
        QVector<GLuint> extra_triangles;
    };

    struct Result
    {
      GLuint vb_handle;
      QVector<Vertex> vertex_buffer;
      QVector<Vertex> vertex_buffer_irregular;
    };

    struct Patch
    {
        QVector<unsigned int> index_buffer;
        bool is_regular;
    };

    QString formatTimeMeasurement(int time);

    QOpenGLFunctions_4_3_Core *f;
    QOpenGLShaderProgram *neighborsShader;
    QOpenGLShaderProgram *copyShader;
    QOpenGLShaderProgram *edgeShader;
    QOpenGLShaderProgram *vertexShader;

    QOpenGLShaderProgram *initComputeShaderProgram(QString &source);
    Tables precomputeTables(Input input);
    void buildNeighborsMap(QVector<Vertex> &vb, QVector<Triangle> &triangles, QMap<Triangle, Triangle::Neighbors> &neighbors);
    QVector<Triangle> vertexNeighbors(Triangle &triangle, Triangle::Neighbor current_neighbor, QMap<Triangle, Triangle::Neighbors> neighbors);
    void precomputeEdgeTable(Subdivision::Tables &tables, QVector<Triangle> &triangles, QMap<Triangle, Triangle::Neighbors> &neighbors, unsigned int offset);
    void precomputeVertexTable(Subdivision::Tables &tables, Input &input, QMap<unsigned int, unsigned int> &modified_vertices);
    void updateIndexBuffer(QVector<unsigned int> &index_buffer, QMap<unsigned int, unsigned int> map);
    void findRegular(QVector<Triangle> triangles, QMap<Triangle, Triangle::Neighbors> neighbors, QVector<Triangle> &regular, QVector<Triangle> &irregular);
    Result runShader(Input input, Tables &tables);
    void runCopyShader(GLuint size, GLuint vb_in, GLuint vb_out);
    void runVertexShader(GLuint size, GLuint vb_handle, GLuint vertex_indices_handle, GLuint vertex_offsets_handle, GLuint output_handle, GLuint offset_handle);
    void runEdgeShader(GLuint size, GLuint vb_handle, GLuint edge_indices_handle, GLuint output_handle, GLuint offset_handle);
    void updateIrregularForDraw(const QVector<Triangle> &triangles, QMap<Triangle, Triangle::Neighbors> &neighbors, Result &result);
    bool containsVertex(QVector<Vertex> &vertices, Vertex vertex);
    QVector3D updateIrregularVertexForDraw(Vertex currentCorner, QVector<Triangle> neighboring_triangles);

    QVector<unsigned int> patchIBToTriangleIB(QVector<unsigned int> ib);

    void getPatchIndexBuffer(QVector<Triangle> &triangles, QMap<Triangle, Triangle::Neighbors> &neighbors, QVector<unsigned int> &patch_ib);
};

#endif
