// example_cgal_api.cpp
//
// conformallab++ — CGAL public API example
//
// This example demonstrates the HIGH-LEVEL CGAL API defined in
// <CGAL/Discrete_conformal_map.h>.  It is the recommended entry point for
// users who want a simple one-call interface without managing Maps bundles,
// DOF assignment, or Newton solver details.
//
// Contrast with example_euclidean.cpp / example_flatten.cpp which use the
// INTERNAL API (setup_euclidean_maps + newton_euclidean + euclidean_layout).
//
// ┌─────────────────────────────────────────────────────────────────────┐
// │  When to use the CGAL API vs the internal API                       │
// │                                                                     │
// │  CGAL API (this file):                                              │
// │    • One call, sensible defaults                                    │
// │    • Named parameters for tuning (tolerance, cone angles, …)       │
// │    • Result type: Conformal_map_result<FT>  (u_per_vertex indexed  │
// │      by raw vertex index)                                           │
// │    • Layout via CGAL::euclidean_layout (Conformal_layout.h)        │
// │                                                                     │
// │  Internal API (example_flatten.cpp, example_layout.cpp):           │
// │    • Full control over every pipeline step                         │
// │    • Access to holonomy, period matrix, cut graph                  │
// │    • Result type: NewtonResult  (x indexed by DOF index)           │
// │    • Required for closed surfaces (genus ≥ 1)                     │
// └─────────────────────────────────────────────────────────────────────┘
//
// Build (requires -DWITH_CGAL=ON):
//   cmake -S code -B build -DWITH_CGAL=ON
//   cmake --build build --target example_cgal_api
//
// Run:
//   ./build/examples/example_cgal_api                        # built-in mesh
//   ./build/examples/example_cgal_api code/data/obj/cathead.obj flat.off

#include <CGAL/Simple_cartesian.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Discrete_conformal_map.h>   // CGAL public API
#include <CGAL/Conformal_layout.h>         // CGAL layout wrapper

// For loading meshes and saving results we still use the internal helpers.
#include "mesh_io.hpp"
#include "mesh_builder.hpp"
#include "layout.hpp"

#include <iostream>
#include <iomanip>
#include <string>
#include <algorithm>

int main(int argc, char* argv[])
{
    using Kernel = CGAL::Simple_cartesian<double>;
    using Mesh   = CGAL::Surface_mesh<Kernel::Point_3>;

    // ── Step 1: obtain mesh ───────────────────────────────────────────────────
    Mesh mesh;
    std::string input_path  = (argc > 1) ? argv[1] : "";
    std::string output_path = (argc > 2) ? argv[2] : "/tmp/conformallab_cgal_api_out.off";

    if (input_path.empty()) {
        std::cout << "[example_cgal_api] No input given.  Using make_quad_strip().\n"
                  << "  For a non-trivial result, supply a 3-D mesh:\n"
                  << "    ./example_cgal_api code/data/obj/cathead.obj\n\n";
        mesh = conformallab::make_quad_strip();
    } else {
        std::cout << "[example_cgal_api] Loading mesh: " << input_path << "\n";
        try { mesh = conformallab::load_mesh(input_path); }
        catch (const std::exception& e) {
            std::cerr << "Error loading mesh: " << e.what() << "\n";
            return 1;
        }
    }

    std::cout << "[example_cgal_api] Mesh: "
              << mesh.number_of_vertices() << " vertices, "
              << mesh.number_of_faces()    << " faces.\n";

    // ── Step 2: CGAL API call ─────────────────────────────────────────────────
    //
    // Default invocation — one function call, no explicit target angles:
    //   • No vertex_curvature_map supplied → "natural theta" default:
    //     Θ_v is set to the ACTUAL angle sum at x = 0.  This makes x* = 0
    //     the equilibrium, so Newton converges in 0 iterations with u_v = 0.
    //
    // ⚠  Natural theta is a TESTING CONVENTION, not a conformal flattening.
    //    The output u_v = 0 means "no deformation" — the map is identity.
    //    For real conformal flattening use:
    //      • example_flatten.cpp (internal API, recommended for open meshes)
    //      • Supply vertex_curvature_map(theta_map) with Θ_v = 2π for a
    //        closed mesh (must satisfy Gauss-Bonnet; see below)
    //
    // The function sets up maps, computes λ°, assigns DOFs, runs Newton,
    // and returns the converged result.
    auto result = CGAL::discrete_conformal_map_euclidean(mesh);

    std::cout << "[example_cgal_api] CGAL API result (natural theta — identity map):\n"
              << "  converged  = " << std::boolalpha << result.converged << "\n"
              << "  iterations = " << result.iterations
              << "  (0 = natural theta, trivially at equilibrium)\n"
              << "  |grad|_inf = " << std::scientific << std::setprecision(2)
              << result.gradient_norm << "\n";

    // ── Step 3: inspect the result ────────────────────────────────────────────
    //
    // result.u_per_vertex is indexed by raw vertex index (v.idx()), NOT by
    // DOF index.  Length = num_vertices(mesh).  Pinned vertices have u = 0.
    //
    // This differs from the internal API (NewtonResult.x) which is indexed
    // by DOF index (v_idx[v]).  The CGAL API handles the mapping internally.
    double u_min = 0.0, u_max = 0.0;
    for (auto v : mesh.vertices()) {
        double u = result.u_per_vertex[static_cast<std::size_t>(v.idx())];
        u_min = std::min(u_min, u);
        u_max = std::max(u_max, u);
    }
    std::cout << "  u_v range  = [" << std::fixed << std::setprecision(4)
              << u_min << ", " << u_max << "]\n\n";

    // Print a few per-vertex values
    std::cout << "[example_cgal_api] First 6 per-vertex scale factors u_v:\n";
    int shown = 0;
    for (auto v : mesh.vertices()) {
        if (shown++ >= 6) break;
        double u = result.u_per_vertex[static_cast<std::size_t>(v.idx())];
        std::cout << "  v" << v.idx() << "  u = " << std::fixed
                  << std::setprecision(6) << u << "\n";
    }

    // ── Step 4: tuning via named parameters ───────────────────────────────────
    //
    // The CGAL API accepts named parameters for fine-grained control.
    // Example: tighter tolerance and more iterations.
    //
    //   auto result2 = CGAL::discrete_conformal_map_euclidean(
    //       mesh,
    //       CGAL::parameters::gradient_tolerance(1e-12)
    //                        .max_iterations(500));
    //
    // Example: supply explicit cone angles (requires Gauss-Bonnet to hold):
    //
    //   auto angle_map = mesh.add_property_map<Mesh::Vertex_index, double>(
    //       "my:angles", 2.0 * M_PI).first;
    //   // … set angle_map[v] for cone vertices …
    //   auto result3 = CGAL::discrete_conformal_map_euclidean(
    //       mesh,
    //       CGAL::parameters::vertex_curvature_map(angle_map));
    //
    // See <CGAL/Discrete_conformal_map.h> for all named parameters.

    // ── Step 5: compute layout via the CGAL layout wrapper ────────────────────
    //
    // The CGAL API does not return a layout directly; call CGAL::euclidean_layout
    // (Conformal_layout.h) with the converged DOF vector and the internal maps.
    // Note: to access the DOF vector and maps from a CGAL-API call, use the
    // result.x field (if exposed) or call the internal API directly.
    //
    // For Phase 8b-Lite, the cleanest way to get a layout after the CGAL call
    // is to use the internal API (example_flatten.cpp) — the CGAL result carries
    // u_per_vertex for inspection but the full pipeline (layout, holonomy, period
    // matrix) still requires the internal Maps bundle.

    if (result.converged)
        std::cout << "\n[example_cgal_api] ✓ Conformal map computed successfully.\n"
                  << "  For UV layout output, see example_flatten.cpp (internal API)\n"
                  << "  which provides direct access to euclidean_layout().\n";

    return result.converged ? 0 : 1;
}
