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

// mesh_io.hpp
//
// Phase 4b — CGAL::IO wrappers for ConformalMesh.
//
// Reads and writes ConformalMesh (CGAL::Surface_mesh) in standard polygon-mesh
// formats using the CGAL Polygon Mesh I/O utilities (CGAL 5.4+).
//
// Supported formats (detected by file extension):
//   .off   — Object File Format   (read + write)
//   .obj   — Wavefront OBJ        (read + write)
//   .ply   — Polygon File Format  (read + write)
//
// Usage:
//   ConformalMesh mesh;
//   if (!read_mesh("input.off", mesh))  throw std::runtime_error("read failed");
//   // ... process mesh ...
//   write_mesh("output.off", mesh);
//
// Note: property maps (lambda0, v_idx, etc.) are NOT serialised — they must
// be re-initialised with setup_*_maps() + compute_lambda0_from_mesh() after
// reading a file.

#include "conformal_mesh.hpp"
#include <CGAL/IO/polygon_mesh_io.h>
#include <CGAL/boost/graph/helpers.h>
#include <string>
#include <stdexcept>
#include <cmath>

namespace conformallab {

/// Read a polygon mesh from `filename` into `mesh` (clears existing content).
/// Returns `true` on success, `false` on failure.
inline bool read_mesh(const std::string& filename, ConformalMesh& mesh)
{
    mesh.clear();
    return CGAL::IO::read_polygon_mesh(filename, mesh);
}

/// Write `mesh` to `filename`.  Returns `true` on success.
inline bool write_mesh(const std::string& filename, const ConformalMesh& mesh)
{
    return CGAL::IO::write_polygon_mesh(filename, mesh);
}

/// Throwing wrapper around `read_mesh`: returns the mesh by value
/// or throws `std::runtime_error` on read failure.
///
/// Also enforces that the mesh is triangulated, because every functional
/// in this library assumes triangle faces — a quad/polygon mesh would be
/// read in silently and then mis-handled by the angle/length formulas.
/// Fail loudly here at the I/O boundary instead.
inline ConformalMesh load_mesh(const std::string& filename)
{
    ConformalMesh mesh;
    if (!read_mesh(filename, mesh))
        throw std::runtime_error("conformallab: failed to read mesh from " + filename);
    if (!CGAL::is_triangle_mesh(mesh))
        throw std::runtime_error(
            "conformallab: mesh from " + filename +
            " is not triangulated (functionals require triangle faces)");
    for (auto v : mesh.vertices()) {
        const auto& p = mesh.point(v);
        if (!std::isfinite(p.x()) || !std::isfinite(p.y()) || !std::isfinite(p.z()))
            throw std::runtime_error(
                "conformallab: non-finite vertex coordinate in " + filename);
    }
    return mesh;
}

/// Throwing wrapper around `write_mesh`; throws `std::runtime_error` on
/// write failure.
inline void save_mesh(const std::string& filename, const ConformalMesh& mesh)
{
    if (!write_mesh(filename, mesh))
        throw std::runtime_error("conformallab: failed to write mesh to " + filename);
}

} // namespace conformallab
