#include "quad_patch_tesselator.h"
#include "mesh_processor.h"
#include "util.h"
#include "hole_filling.h"
#include "smoothing.h"
#include "curvature.h"
#include <QGenericMatrix>
#include <unordered_set>


MeshProcessor::MeshProcessor(const QString &path, MeshViewer &mesh_viewer)
	:mesh_viewer(mesh_viewer) {
	OpenMesh::IO::Options options;
	options.set(OpenMesh::IO::Options::VertexColor);
	if (!OpenMesh::IO::read_mesh(mesh, path.toUtf8().constData(), options)) {
		qWarning() << "Failed to read" << path;
		return;
	}
	for (VertexHandle vh : mesh.vertices()) {
		mesh.set_color(vh, MyMesh::Color(.5, .5, .5));
	}
	courbure = new Courbures(mesh);
	try {
		courbure->compute_KH();
		courbure->set_K_colors();
	} catch (std::runtime_error &e) {
		qWarning() << "Curvature computation failed";
	}
	connect(&mesh_viewer, &MeshViewer::clicked, this, &MeshProcessor::click);
	updateView();
}


MeshProcessor::~MeshProcessor() {
	if (mesh_viewer.isInitialized()) {
		mesh_viewer.removeMesh(mesh);
	}
	if (courbure) delete courbure;
}


void MeshProcessor::updateView() const {
	if (mesh_viewer.isInitialized()) {
		mesh_viewer.removeMesh(mesh);
		mesh_viewer.addMesh(mesh);
		mesh_viewer.updateForReal();
	} else {
		connect(&mesh_viewer, &MeshViewer::initialized, [&]() {
			mesh_viewer.addMesh(mesh);
			mesh_viewer.updateForReal();
		});
	}
}


void MeshProcessor::fillHolesDumb() {
	::fillHolesDumb(mesh);
	updateView();
}


void MeshProcessor::fillHolesImplicit() {
	std::vector<MyMesh> fillings = ::fillHolesImplicit(mesh);
	for (MyMesh &filling : fillings) {
		mesh_viewer.addMesh(filling);
	}
	updateView();
}


void MeshProcessor::smooth() {
	::smooth(mesh);
	updateView();
}


void MeshProcessor::setPatchView(bool on) {
	view_patches = on;
}


void MeshProcessor::click(QVector3D position) {
	if (!view_patches) return;
	Eigen::Vector3d pos {position.x(), position.y(), position.z()};
	MyMesh::VertexIter it = mesh.vertices_begin();
	VertexHandle min_vert = *it;
	double min_dist = (mesh.point(*it) - pos).squaredNorm();
	for (; it != mesh.vertices_end(); ++it) {
		double dist = (mesh.point(*it) - pos).squaredNorm();
		if (dist < min_dist) {
			min_dist = dist;
			min_vert = *it;
		}
	}
	QuadPatch q = mesh.property(courbure->vprop_quad, min_vert);
	patch = tesselate_quad_patch(q, mesh, min_vert);
	mesh_viewer.removeMesh(patch);
	mesh_viewer.addMesh(patch);
	mesh_viewer.updateForReal();
}
