#pragma once
// Copyright (c) 2024-2026 Tarik Moussa.
// SPDX-License-Identifier: MIT

// mesh_utils.hpp
//
// Conversions between CGAL::Surface_mesh and Eigen matrices.  Used
// primarily by the viewer / example programs to bridge to libigl, which
// expects (V, F) matrix pairs rather than a halfedge data structure.
//
// All functions are templated on the kernel so the same code works
// with `Simple_cartesian<double>` (production) and with any CGAL
// `Kernel_d::Point_3` (test scaffolding).

#include <CGAL/Surface_mesh.h>
#include <Eigen/Core>  // downgraded from <Eigen/Dense>: this header only
                      // uses Matrix/Vector primitives, no decompositions.
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>


namespace mesh_utils {

/// Copy `mesh` into an Eigen `(V, F)` pair (libigl convention).
///
/// **Side effect:** `mesh` is triangulated in place via
/// `CGAL::Polygon_mesh_processing::triangulate_faces` so the output
/// `F` is guaranteed to be a 3-column matrix.  If `mesh` is already a
/// triangle mesh this is a no-op.
///
/// \param mesh  Input surface mesh.  **Modified in place** if any face
///              has more than 3 vertices.
/// \param V     Output: `(num_vertices, 3)` matrix of vertex positions.
/// \param F     Output: `(num_faces, 3)` matrix of vertex indices per
///              face (rows are individual triangles).
template <typename Kernel>
void cgal_to_eigen(CGAL::Surface_mesh<typename Kernel::Point_3>& mesh,
                   Eigen::MatrixXd& V, Eigen::MatrixXi& F) {

 CGAL::Polygon_mesh_processing::triangulate_faces(mesh);

  V.resize(mesh.num_vertices(), 3);
  F.resize(mesh.num_faces(), 3);

  for (auto v : mesh.vertices()) {
    auto p = mesh.point(v);
    V.row(v.idx()) << p.x(), p.y(), p.z();
  }

  Eigen::Index face_idx = 0;
  for (auto f : mesh.faces()) {
    int vertex_count = 0;
    for (auto h : CGAL::halfedges_around_face(mesh.halfedge(f), mesh)) {
      auto v = mesh.target(h);
      F(face_idx, vertex_count) = v.idx();
      ++vertex_count;
    }
    face_idx++;
  }
}

/// Quick interactive visualisation via libigl + GLFW.
///
/// **Requires** `WITH_VIEWER=ON` at CMake time (which is implied by
/// `WITH_CGAL=ON`).  Blocks until the viewer window is closed.
/// Not suitable for CI / headless contexts.
///
/// Typical use:
/// \code{.cpp}
///   Eigen::MatrixXd V;  Eigen::MatrixXi F;
///   mesh_utils::cgal_to_eigen<Kernel>(mesh, V, F);
///   mesh_utils::simple_visualize_mesh<Kernel>(V, F);
/// \endcode
template <typename Kernel>
void simple_visualize_mesh(Eigen::MatrixXd& V, Eigen::MatrixXi& F) {
  igl::opengl::glfw::Viewer viewer;
  viewer.data().set_mesh(V, F);
  viewer.launch();
}

/// Zero-copy `Eigen::Map` view of `mesh`'s vertex positions.
///
/// Returns a row-major `(N, 3)` `Eigen::Map` that aliases the
/// `mesh.points()` storage directly — no allocation, O(1).
///
/// **Lifetime warning:** the returned `Map` references memory owned by
/// `mesh`.  Adding or removing vertices may invalidate the underlying
/// storage; use the `Map` only as long as `mesh` is structurally stable.
///
/// This is the read-write counterpart to `cgal_to_eigen` for cases
/// where the caller wants to *modify* vertex positions through Eigen
/// (e.g. apply a Möbius transformation) without an intermediate copy.
template <typename Kernel>
Eigen::Map<Eigen::Matrix<double, Eigen::Dynamic, 3, Eigen::RowMajor>>
get_vertex_map(CGAL::Surface_mesh<typename Kernel::Point_3>& mesh) {
  auto& points = mesh.points();
  double* data = reinterpret_cast<double*>(&points[0][0]);
  return {data, static_cast<Eigen::Index>(points.size()), 3};
}
}  // namespace
