# tests/cgal/CMakeLists.txt
#
# CGAL-dependent test target.  Only built when -DWITH_CGAL=ON.
# Requires Boost (find_package(Boost REQUIRED) is called in the root CMakeLists).
#
# Run with:
#   cmake -S code -B build -DWITH_CGAL=ON
#   cmake --build build --target conformallab_cgal_tests
#   ctest --test-dir build -R cgal

add_executable(conformallab_cgal_tests
    # ── Phase 3a: mesh infrastructure ──────────────────────────────────────
    test_conformal_mesh.cpp

    # ── Phase 3b: HyperIdealFunctional ─────────────────────────────────────
    test_hyper_ideal_functional.cpp

    # ── Phase 3c + 3e: SphericalFunctional + gauge-fix ────────────────────
    test_spherical_functional.cpp

    # ── Phase 3d: EuclideanCyclicFunctional ───────────────────────────────
    test_euclidean_functional.cpp

    # ── Phase 3f: Hessians (cotangent Laplacian, spherical + Euclidean) ───
    test_euclidean_hessian.cpp
    test_spherical_hessian.cpp

    # ── Phase 9b: Hyper-ideal Hessian — block-FD vs full-FD validation ───
    #    Verifies the O(F·36) block-local Hessian agrees with the
    #    O(F·n) full-FD baseline.  Java upstream has no Hessian at all
    #    (HyperIdealFunctional.hasHessian() returns false) — both
    #    variants are conformallab++ extensions beyond the port.
    test_hyper_ideal_hessian.cpp

    # ── Phase 4a: Newton solver ────────────────────────────────────────────
    test_newton_solver.cpp

    # ── Phase 4b: Mesh I/O (CGAL::IO) ─────────────────────────────────────
    test_mesh_io.cpp

    # ── Phase 4c: End-to-end pipeline + user examples ─────────────────────
    test_pipeline.cpp

    # ── Phase 5: Layout / embedding + serialization ────────────────────────
    test_layout.cpp

    # ── Phase 6: Gauss–Bonnet, cut graph, exact trilateration, normalisation
    test_phase6.cpp

    # ── Phase 7: Java-parity layout — MobiusMap, priority BFS, halfedge_uv,
    #             period matrix, fundamental domain, tiling
    test_phase7.cpp

    # ── Java parity: geometry utility tests ──────────────────────────────────
    #    Ported from CuttinUtilityTest, UnwrapUtilityTest,
    #    ConvergenceUtilityTests, HomologyTest.  All tests active —
    #    the v0.7.0 genus-2 homology stub was implemented in Phase 7
    #    (HomologyGenerators.Genus2_FourCutEdges, brezel2.obj).
    test_geometry_utils.cpp

    # ── Scalability smoke tests ────────────────────────────────────────────────
    #    Newton convergence on large real-world meshes (cathead, brezel, brezel2).
    #    Assert correctness only (< 30 iterations, ||G|| < 1e-8).
    #    Wall-clock time is printed for documentation but NOT asserted,
    #    so the tests remain stable on slow CI hardware (Raspberry Pi ARM64).
    test_scalability_smoke.cpp

    # ── Phase 8 MVP: new CGAL-style public API ────────────────────────────────
    #    First client of Conformal_map_traits.h + Discrete_conformal_map.h.
    #    Acceptance probe before Phase 9a (Inversive-Distance) lands.
    test_cgal_traits_mvp.cpp

    # ── Phase 9a.1: CPEuclideanFunctional (BPS 2010 circle packing) ──────────
    #    Face-based circle-packing functional ported from
    #    CPEuclideanFunctional.java.  Reference: Bobenko-Pinkall-Springborn 2010.
    test_cp_euclidean_functional.cpp

    # ── Phase 9a.2: InversiveDistance (Luo 2004 + Glickenstein 2011) ─────────
    #    Vertex-based inversive-distance circle-packing functional.  No Java
    #    reference; implemented from the literature.  Cross-validated against
    #    EuclideanCyclicFunctional at the natural initial geometry (u = 0).
    test_inversive_distance_functional.cpp

    # ── Phase 9a: Newton solvers for the two new circle-packing functionals ──
    #    Convergence tests for newton_cp_euclidean (analytic Hessian) and
    #    newton_inversive_distance (FD Hessian).
    test_newton_phase9a.cpp

    # ── Tier-3 Java cross-validation: Lawson square-tiled HyperIdeal ─────────
    #    Low-level half-edge genus-2 generator + golden-vector convergence.
    test_lawson_hyperideal.cpp

    # ── Pn projective-metric substrate (de.jreality.math.Pn port) ───────────
    #    Inner product / norm / distance / geodesic interpolation in
    #    Euclidean, Elliptic, Hyperbolic signatures.
    test_pn_geometry.cpp

    # ── Phase 8b-Lite: CGAL entry wrappers for the 4 non-Euclidean modes ─────
    #    Spherical, HyperIdeal, CircleP-Euclidean, Inversive-Distance via
    #    <CGAL/Discrete_*.h> public API + Conformal_layout.h wrapper.
    test_cgal_phase8b_lite.cpp

    # ── Phase 9g.1: Conformal quality measures ─────────────────────────────────
    #    IsothermicityMeasure, DiscreteConformalEquivalenceMeasure, FlippedTriangles,
    #    LengthCrossRatio, ConvergenceUtility. Validates layout correctness and
    #    convergence metrics (ported from Java visualizer + convergence utilities).
    test_conformal_quality.cpp

    # ── Phase 9d.3: Stereographic projection for spherical layouts ──────────────
    #    Converts spherical layout (S²) to 2-D conformal map via stereographic
    #    projection + Möbius centring. Tests round-trip consistency.
    test_stereographic_layout.cpp
)

target_include_directories(conformallab_cgal_tests SYSTEM PRIVATE
    ${CMAKE_SOURCE_DIR}/deps/eigen-3.4.0
    ${CMAKE_SOURCE_DIR}/deps/CGAL-6.1.1/include
    ${CMAKE_SOURCE_DIR}/deps/single_includes
    ${Boost_INCLUDE_DIRS}
)

target_include_directories(conformallab_cgal_tests PRIVATE
    ${CMAKE_SOURCE_DIR}/include
)

target_compile_definitions(conformallab_cgal_tests PRIVATE
    CGAL_DISABLE_GMP
    CGAL_DISABLE_MPFR
    # Data directory — absolute path to code/data/ at build time.
    # Used by tests that load real mesh files (cathead.obj, brezel2.obj, …).
    CONFORMALLAB_DATA_DIR="${CMAKE_SOURCE_DIR}/data"
)

# Suppress warnings from CGAL/Boost headers
target_compile_options(conformallab_cgal_tests PRIVATE
    $<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:-Wno-unused-parameter>
)

# Fast test-build mode (lever #10): -O0 -g overrides the inherited
# Release-mode -O3 + -DNDEBUG.  Applies only to this test target.
if(CONFORMALLAB_FAST_TEST_BUILD)
    target_compile_options(conformallab_cgal_tests PRIVATE
        $<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:-O0 -g -UNDEBUG>
    )
endif()

# ── Low-memory build mode (for RAM-constrained CI runners, e.g. Raspberry Pi) ──
#
# Problem: CGAL + Eigen at -O3 drives cc1plus peak RAM to ~600-800 MB per
# Unity compilation unit on ARM64 Linux.  A 1600 MB container limit with a
# batch of 4 files per unit causes OOM-kill during the build.
#
# This flag enables three orthogonal memory-saving measures:
#
#   1. -O0 (no debug info): drops cc1plus backend RAM by ~60-70 %.
#      Optimizer passes (inlining, register allocation, constant propagation)
#      dominate the backend.  At -O0 they are entirely skipped → peak per
#      TU falls from ~700 MB to ~150-200 MB on ARM64.
#      We omit -g deliberately: debug info adds ~30-40 % object-file size
#      and increases linker RSS.  CI needs "does it compile + do tests pass",
#      not debuggability.
#
#   2. PCH OFF: the precompiled header itself consumes ~200 MB to compile
#      and is re-read by every TU.  Disabling it saves the one-time PCH
#      compilation cost; each TU re-parses CGAL headers, but at -O0 this
#      is fast.
#
#   3. UNITY_BUILD_BATCH_SIZE=1: one source file per unity unit.  Removes
#      the "4 files × CGAL parse cost" multiplier; each cc1plus process
#      only sees one file worth of templates.
#
#   4. Linker memory flag (GCC/Clang only): --no-keep-memory tells GNU ld
#      to release symbol table memory after each input file instead of
#      keeping it for cross-reference.  Reduces linker RSS by 15-25 % at
#      the cost of slightly longer link time.
#
# Activate with:
#   cmake -S code -B build -DWITH_CGAL_TESTS=ON \
#         -DCONFORMALLAB_LOW_MEMORY_BUILD=ON
# Expected peak per cc1plus: ~150-200 MB  →  fits in 1800-2000 MB container.
# Test runtime is 2-4× slower than Release (CGAL traversals unoptimized),
# but correctness is unaffected.
option(CONFORMALLAB_LOW_MEMORY_BUILD
    "Build CGAL tests with -O0, no PCH, UNITY_BATCH_SIZE=1 for RAM-constrained CI." OFF)

if(CONFORMALLAB_LOW_MEMORY_BUILD)
    message(STATUS "CONFORMALLAB_LOW_MEMORY_BUILD active: -O0, no PCH, unity batch 1.")

    # 1. -O0, no debug info
    target_compile_options(conformallab_cgal_tests PRIVATE
        $<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:-O0 -UNDEBUG>
    )

    # 2. PCH off — force-override the option so the block below is skipped
    set(CONFORMALLAB_USE_PCH OFF CACHE BOOL "" FORCE)

    # 3. Unity batch size = 1 (one source file per compilation unit)
    set_target_properties(conformallab_cgal_tests PROPERTIES
        UNITY_BUILD ON
        UNITY_BUILD_MODE BATCH
        UNITY_BUILD_BATCH_SIZE 1)

    # 4. Linker memory hint (GNU ld / lld)
    target_link_options(conformallab_cgal_tests PRIVATE
        $<$<CXX_COMPILER_ID:GNU>:-Wl,--no-keep-memory>)
endif()

target_link_libraries(conformallab_cgal_tests PRIVATE GTest::gtest_main)

# ── Compile-time speed-up: precompiled headers ───────────────────────────────
#
# The CGAL+Eigen template soup dominates every TU in this target:
# measured at 5.9 s per minimal "include <CGAL/Discrete_conformal_map.h>"
# TU on Apple M1.  A shared PCH absorbs that cost once, slashing the
# total wall-clock from ~78 s (j8) to ~25 s (3×).
#
# Opt-out with -DCONFORMALLAB_USE_PCH=OFF if the PCH itself misbehaves
# (e.g. older toolchains that don't share PCH across translation units
# reliably) — falls back to the historical "every TU re-parses CGAL"
# build mode.
option(CONFORMALLAB_USE_PCH
    "Enable precompiled headers for the CGAL test target." ON)

if(CONFORMALLAB_USE_PCH)
    # Per-target Unity Build property takes precedence over the global
    # CMAKE_UNITY_BUILD; honour CONFORMALLAB_DEV_BUILD's preference here
    # so `-DCONFORMALLAB_DEV_BUILD=ON` truly turns Unity Build off for
    # incremental-rebuild workflows.
    if(NOT CONFORMALLAB_DEV_BUILD)
        set_target_properties(conformallab_cgal_tests PROPERTIES
            # Unity-builds amortise the per-TU CGAL+Eigen header cost
            # across several tests in the same compile.  Batch size 4
            # keeps gtest's TEST(...) macros + per-file `using
            # namespace …` from colliding while still cutting parser
            # cost ~4×.
            UNITY_BUILD ON
            UNITY_BUILD_MODE BATCH
            UNITY_BUILD_BATCH_SIZE 4)
    endif()

    target_precompile_headers(conformallab_cgal_tests PRIVATE
        # CGAL headers that every test transitively includes.
        <CGAL/Surface_mesh.h>
        <CGAL/Simple_cartesian.h>
        <CGAL/Kernel_traits.h>
        <CGAL/boost/graph/iterator.h>
        <CGAL/Polygon_mesh_processing/triangulate_faces.h>

        # Eigen blocks that drive the slowest template instantiations
        # (SelfAdjointEigenSolver<Matrix<2,2>>, ColPivHouseholderQR<
        # Matrix<complex,3,3>>, sparse Cholesky + QR fallback).
        <Eigen/Dense>
        <Eigen/Sparse>
        <Eigen/SparseCholesky>
        <Eigen/SparseQR>

        # GoogleTest itself; every test includes it.
        <gtest/gtest.h>

        # std headers that appear in every test.
        <vector>
        <string>
        <cmath>
        <complex>
    )
endif()

include(GoogleTest)
gtest_discover_tests(conformallab_cgal_tests
    TEST_PREFIX "cgal."
    DISCOVERY_TIMEOUT 60
)
