#pragma once

#include "util/OpenMeshUtils.h"

struct SimpleFace
{
	std::vector<OpenMesh::HalfedgeHandle> he_handles;

	bool is_triangulated()
	{
		if (he_handles.size() != 3)
		{
			return false;
		}

		return true;
	}
};

struct Triangle
{
	Triangle(std::vector<OpenMesh::Vec3f> vpoints)
	{
		if (vpoints.size() != 3)
		{
			abort();
		}

		for (int i = 0; i < 3; i++)
		{
			points[i] = vpoints[i];
		}
	}

	OpenMesh::Vec3f points[3];
};

class Triangles
{
  public:
	Triangles(HEMesh private_mesh)
	{
		std::vector<SimpleFace> simple_faces;

		if (!gather_faces(private_mesh, simple_faces))
		{
			simple_faces.clear();
			private_mesh.triangulate();

			if (!gather_faces(private_mesh, simple_faces))
			{
				abort();
			}
		}

		for (auto simple_face : simple_faces)
		{
			std::vector<OpenMesh::Vec3f> points = face_points(simple_face, private_mesh);
			triangles.emplace_back(points);
		}
	}

	float surface_area()
	{
		float surface_area = 0.0f;

		for (Triangle &triangle : triangles)
		{
			OpenMesh::Vec3f p1 = triangle.points[0];
			OpenMesh::Vec3f p2 = triangle.points[1];
			OpenMesh::Vec3f p3 = triangle.points[2];

			OpenMesh::Vec3f p12_vec = p1 - p2;
			OpenMesh::Vec3f p23_vec = p1 - p3;

			OpenMesh::Vec3f cross = OpenMesh::cross(p12_vec, p23_vec);

			surface_area += cross.length() / 2;
		}

		return surface_area;
	}

	float volume()
	{
		float volume = 0.0f;

		for (Triangle &triangle : triangles)
		{
			OpenMesh::Vec3f p1 = triangle.points[0];
			OpenMesh::Vec3f p2 = triangle.points[1];
			OpenMesh::Vec3f p3 = triangle.points[2];

			volume += OpenMesh::dot(p1, OpenMesh::cross(p2, p3));
		}

		return volume;
	}

  private:
	bool gather_faces(const HEMesh &m, std::vector<SimpleFace> &simple_faces)
	{
		auto faces = m.all_faces();

		for (auto face_handle : faces)
		{
			HEMesh::ConstFaceHalfedgeIter fh_it = m.cfh_iter(face_handle);

			SimpleFace simple_face;

			for (; fh_it.is_valid(); ++fh_it)
			{
				simple_face.he_handles.emplace_back(*fh_it);
			}

			if (!simple_face.is_triangulated())
			{
				return false;
			}

			simple_faces.emplace_back(simple_face);
		}

		return true;
	}

	std::vector<OpenMesh::Vec3f> face_points(SimpleFace &simple_face, const HEMesh &m)
	{
		std::vector<OpenMesh::Vec3f> points;

		for (auto he : simple_face.he_handles)
		{
			OpenMesh::VertexHandle vertex_handle = m.from_vertex_handle(he);

			points.emplace_back(m.point(vertex_handle));
		}

		return points;
	}

	std::vector<Triangle> triangles;
};
