#include "mesh_view.h"
#include "util.h"
#include <QOpenGLContext>
#include <QOpenGLFunctions_2_1>


MeshView::MeshView(const MyMesh &mesh, QOpenGLShaderProgram &program)
	:vertex_buffer(QOpenGLBuffer::VertexBuffer),
	 index_buffer(QOpenGLBuffer::IndexBuffer),
	 holes_index_buffer(QOpenGLBuffer::IndexBuffer),
	 n_faces(mesh.n_faces()),
	 n_vertices(mesh.n_vertices()),
	 mesh(mesh) {
    QVector<GLfloat> vertices(n_vertices * 6);
	size_t i = 0;
	for (const VertexHandle it : mesh.vertices()) {
		vertices[6*i + 0] = mesh.point(it)[0];
		vertices[6*i + 1] = mesh.point(it)[1];
		vertices[6*i + 2] = mesh.point(it)[2];
		vertices[6*i + 3] = mesh.color(it)[0];
		vertices[6*i + 4] = mesh.color(it)[1];
		vertices[6*i + 5] = mesh.color(it)[2];
		i++;
	}

	QVector<GLuint> indices(n_faces * 3);
	i = 0;
	for (const FaceHandle it : mesh.faces()) {
		MyMesh::ConstFaceVertexIter it2 = mesh.cfv_begin(it);
		indices[3 * i + 0] = it2->idx();
		++it2;
		indices[3 * i + 1] = it2->idx();
		++it2;
		indices[3 * i + 2] = it2->idx();
		i++;
	}

	QVector<GLuint> holes_indices;
	n_boundary_halfedges = 0;
	for (std::vector<HalfedgeHandle> hole : mesh.holes) {
		for (HalfedgeHandle it : hole) {
			holes_indices.push_back(mesh.from_vertex_handle(it).idx());
			holes_indices.push_back(mesh.to_vertex_handle(it).idx());
			n_boundary_halfedges++;
		}
	}

	vao.create();
	{QOpenGLVertexArrayObject::Binder binder(&vao);
		vertex_buffer.create();
		vertex_buffer.bind();
		vertex_buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw);
		vertex_buffer.allocate(vertices.constData(), n_vertices * 6 * sizeof (GLfloat));
		index_buffer.create();
		index_buffer.bind();
		index_buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw);
		index_buffer.allocate(indices.constData(), n_faces * 3 * sizeof (GLuint));
		holes_index_buffer.create();
		holes_index_buffer.bind();
		holes_index_buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw);
		holes_index_buffer.allocate(holes_indices.constData(), n_boundary_halfedges * 2 * sizeof (GLuint));

		vertex_buffer.bind();
		program.setAttributeBuffer("pos", GL_FLOAT, 0,
								   3, 6 * sizeof (GLfloat));
		program.enableAttributeArray("pos");
		program.setAttributeBuffer("col", GL_FLOAT, 3 * sizeof (GLfloat),
								   3, 6 * sizeof (GLfloat));
		program.enableAttributeArray("col");
	}
}


MeshView::~MeshView() {
	vertex_buffer.destroy();
	index_buffer.destroy();
	vao.destroy();
}


void MeshView::paint(QOpenGLShaderProgram &program) {
	QOpenGLContext *ctx = QOpenGLContext::currentContext();
	QOpenGLExtraFunctions *glf = ctx->extraFunctions();

	QOpenGLFunctions_2_1 *glf21 = nullptr;
	glf21 = ctx->versionFunctions<QOpenGLFunctions_2_1>();
	if (!glf21)
		qFatal("Failed to get OpenGL 2.1 functions");

	program.setUniformValue("model", mesh.transform);
	// program.setUniformValue("col", mesh.color.redF(), mesh.color.greenF(), mesh.color.blueF());

	{QOpenGLVertexArrayObject::Binder binder(&vao);
		/* Mesh */
		glf->glEnable(GL_POLYGON_OFFSET_FILL);
		glf->glPolygonOffset(1.0, 2);
		glf->glDrawElements(GL_TRIANGLES, n_faces * 3, GL_UNSIGNED_INT, 0);
		glf->glDisable(GL_POLYGON_OFFSET_FILL);

		program.setUniformValue("wireframe", 1);
			/* Wireframe */
			glf21->glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			glf->glDepthMask(GL_FALSE);
			glf->glDrawElements(GL_TRIANGLES, n_faces * 3, GL_UNSIGNED_INT, 0);
			glf->glDepthMask(GL_TRUE);
			glf21->glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

			/* Holes */
			program.setUniformValue("wf_col", QVector3D(1, 0, 1));
			holes_index_buffer.bind();
			glf->glDrawElements(GL_LINES, n_boundary_halfedges * 2, GL_UNSIGNED_INT, 0);
			index_buffer.bind();
			program.setUniformValue("wf_col", QVector3D(0, 0, 0));
		program.setUniformValue("wireframe", 0);
	}
}
