#pragma once
// spherical_geometry.hpp
//
// Pure-math building blocks for the spherical discrete conformal map.
// Ported from de.varylab.discreteconformal.functional.SphericalFunctional
// (the geometry helpers embedded there).
//
// Notation:
//   u_i          – vertex conformal factor (DOF)
//   λ°_e         – base log-length of edge e (fixed initial value)
//   λ_ij         – effective log-length = λ°_ij + u_i + u_j
//   l_ij         – spherical arc length = 2·asin(min(exp(λ_ij/2), 1))
//   α_k          – interior angle of the spherical triangle at vertex k

#include "constants.hpp"
#include <cmath>
#include <algorithm>

namespace conformallab {

/// Backward-compatible alias — prefer conformallab::PI in new code.
constexpr double PI_SPHER = PI;

// ── Effective spherical arc length ────────────────────────────────────────────

// l(λ) = 2·asin(min(exp(λ/2), 1)).
// Clamps exp(λ/2) to [0, 1] so the arcsin stays in domain.
inline double spherical_l(double lambda)
{
    double half = std::exp(lambda * 0.5);
    if (half >= 1.0) half = 1.0 - 1e-15;
    if (half <= 0.0) return 0.0;
    return 2.0 * std::asin(half);
}

// ── Interior angles of a spherical triangle ──────────────────────────────────

struct SphericalFaceAngles {
    double alpha1, alpha2, alpha3;  // corner angles at v1, v2, v3
    bool   valid;                   // false when the three lengths fail the
                                    // spherical triangle inequality
};

// Compute corner angles from spherical arc lengths using the half-angle formula.
//
// Convention (matching the halfedge cycle h0→v1→v2, h1→v2→v3, h2→v3→v1):
//   l12 – arc length of edge opposite v3 (edge e12)
//   l23 – arc length of edge opposite v1 (edge e23)
//   l31 – arc length of edge opposite v2 (edge e31)
//
// Half-angle formula (spherical law of cosines):
//   α_k = 2·atan2(sqrt(sin(s-a)·sin(s-b)), sqrt(sin(s)·sin(s-c)))
// where a,b are the two edges ADJACENT to vertex k, c is the opposite edge.
//
// Equivalently (in terms of s-deficiencies):
//   α1 = 2·atan2( sqrt(sin(s12)·sin(s31)), sqrt(sin(s)·sin(s23)) )
//   α2 = 2·atan2( sqrt(sin(s12)·sin(s23)), sqrt(sin(s)·sin(s31)) )
//   α3 = 2·atan2( sqrt(sin(s23)·sin(s31)), sqrt(sin(s)·sin(s12)) )
//
// where s = (l12+l23+l31)/2 and s_ij = s - l_ij.
inline SphericalFaceAngles spherical_angles(double l12, double l23, double l31)
{
    double s   = (l12 + l23 + l31) * 0.5;
    double s12 = s - l12;
    double s23 = s - l23;
    double s31 = s - l31;

    // Spherical triangle inequalities: all s-deficiencies > 0 and s < π.
    if (s12 <= 0.0 || s23 <= 0.0 || s31 <= 0.0 || s >= PI_SPHER)
        return {0.0, 0.0, 0.0, false};

    const double ss   = std::sin(s);
    const double ss12 = std::sin(s12);
    const double ss23 = std::sin(s23);
    const double ss31 = std::sin(s31);

    double a1 = 2.0 * std::atan2(std::sqrt(ss12 * ss31), std::sqrt(ss * ss23));
    double a2 = 2.0 * std::atan2(std::sqrt(ss12 * ss23), std::sqrt(ss * ss31));
    double a3 = 2.0 * std::atan2(std::sqrt(ss23 * ss31), std::sqrt(ss * ss12));

    return {a1, a2, a3, true};
}

} // namespace conformallab
