#include "analysis.h"
#include <cmath>
#include <iostream>
#include <fstream>

#define PI 3.14159265


bool check_faces_are_triangles(MyMesh &mesh) {
	for (auto f_it = mesh.faces_begin(); f_it != mesh.faces_end(); ++f_it) {
		size_t n_edges = 0;
		for (auto fe_it = mesh.fe_iter(*f_it); fe_it.is_valid(); ++fe_it) {
			n_edges++;
		}
		if (n_edges != 3) {
			return false;
		}
	}
	return true;
}


bool check_faces_arent_lonely(MyMesh &mesh) {
	for (auto f_it = mesh.faces_begin(); f_it != mesh.faces_end(); ++f_it) {
		auto ff_it = mesh.ff_iter(*f_it);
		if (!ff_it.is_valid()) {
			return false;
		}
	}
	return true;
}


bool check_vertices_arent_lonely(MyMesh &mesh) {
	for (auto v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); ++v_it) {
		auto ve_it = mesh.ve_iter(*v_it);
		if (!ve_it.is_valid()) {
			return false;
		}
	}
	return true;
}


bool check_edges_arent_lonely(const char *path) {
	using namespace std;
	ifstream f(path);
	string line;
	while (getline(f, line)) {
		istringstream iss(line);
		char first;
		iss >> first;
		if (first == 'l') return false;
	}
	return true;
}


float face_area(MyMesh &mesh, const MyMesh::FaceHandle &face) {
	MyMesh::Point p0, p1, p2;
	auto fv_it = mesh.fv_iter(face);
	p0 = mesh.point(*fv_it++);
	p1 = mesh.point(*fv_it++);
	p2 = mesh.point(*fv_it);
	return ((p1 - p0) % (p2 - p0)).norm() / 2;
}


float total_area(MyMesh &mesh) {
	if (!check_faces_are_triangles(mesh)) {
		std::cerr << "Le calcul de l’aire ne peu se faire que sur un maillage triangulaire." << std::endl;
		return -1;
	}
	float ret = 0;
	for (const MyMesh::FaceHandle &face : mesh.faces()) {
		ret += face_area(mesh, face);
	}
	return ret;
}


void stats_surface_area(MyMesh &mesh) {
	if (!check_faces_are_triangles(mesh)) {
		std::cerr << "Le calcul de l’aire ne peu se faire que sur un maillage triangulaire." << std::endl;
		return;
	}
	for (const MyMesh::FaceHandle &face : mesh.faces()) {
		std::cout << face_area(mesh, face) << " ";
	}
	std::cout << std::endl;
}


void stats_n_neighbors(MyMesh &mesh) {
	for (const VertexHandle &vh : mesh.vertices()) {
		unsigned count = 0;
		for (auto vv_it = mesh.vv_iter(vh); vv_it.is_valid(); ++vv_it) {
			count++;
		}
		std::cout << count << " ";
	}
	std::cout << std::endl;
}


#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
static float f(int n, float h, float s, float v) {
	float k = fmod(n + h / 60, 6);
	return v - v * s * MAX(0, MIN(k, MIN(4 - k, 1)));
}

static OpenMesh::Vec3uc hsv_to_rgb(float h, float s, float v) {
	return OpenMesh::Vec3uc {f(5, h, s, v) * 255, f(3, h, s, v) * 255, f(1, h, s, v) * 255};
}

void stats_normal_deviation(MyMesh &mesh) {
	const float s = 1, v = 1, min_h = 50, max_h = 0;
	mesh.update_normals();
	for (const VertexHandle &vh : mesh.vertices()) {
		MyMesh::Normal normal = mesh.normal(vh);
		float max = 0;
		for (auto vf_it = mesh.vf_iter(vh); vf_it.is_valid(); ++vf_it) {
			float angle = acos(OpenMesh::dot(mesh.normal(*vf_it), normal)) * 180.0 / PI;
			if (angle > max)
				max = angle;
		}
		mesh.set_color(vh, (MyMesh::Color) hsv_to_rgb(fmod(max * 360 / (max_h - min_h) + max_h, 360), s, v));
		std::cout << max << " ";
	}
	std::cout << std::endl;
}


void stats_dihedral_angles(MyMesh &mesh) {
	mesh.update_normals();
	for (size_t i = 0; i < mesh.n_halfedges(); i++) {
		MyMesh::HalfedgeHandle heh = mesh.halfedge_handle(i);
		std::cout << mesh.calc_dihedral_angle_fast(heh) * 180.0 / PI + 180 << " ";
	}
	std::cout << std::endl;
}
