//! MSDF calculation for a single position.

use crate::math_curve::cross;
use crate::math_segment::unsigned_dist_point_to_segment_sqr;
use crate::segment_soa::SegmentSoa;

use super::math_segment::{
    Segment, pseudo_signed_dist_point_to_segment, signed_dist_point_to_segment,
};

use glam::Vec2;

// Negligible in font space where glyphs span ~1000 units.
const DIST_TOLERANCE: f32 = 0.1;

/// Find the closest segment of each color channel via brute-force scan over the SoA.
///
/// Tie-break uses orthogonality (Chlumsky section 2.4): segments with higher
/// `|cross(tangent_unit, (perp / |perp|))|` win ties.
fn find_closest_seg(pos: Vec2, soa: &SegmentSoa) -> [Option<usize>; 3] {
    let mut best_dist = [f32::INFINITY; 3];
    // Store ortho's squared form to defer the final sqrt to tie-break.
    let mut best_ortho_sq = [f32::MIN; 3];
    let mut best_seg = [0usize; 3];

    let n = soa.len();
    for i in 0..n {
        let from = soa.from[i];
        let tangent = soa.tangent[i];
        let inv_tsq = soa.inv_tangent_len_sq[i];
        let tangent_unit = soa.tangent_unit[i];

        let pf = pos - from;
        let t = (pf.dot(tangent) * inv_tsq).clamp(0.0, 1.0);
        let perp = from + tangent * t - pos;
        let dist = perp.length_squared();

        let cmask = soa.color_mask[i];
        for col in 0..3 {
            if (cmask >> col) & 1 == 0 {
                continue;
            }
            let diff = dist - best_dist[col];
            let smaller = diff < -DIST_TOLERANCE;
            let equal = diff.abs() < DIST_TOLERANCE;
            // Only finalize the orthogonality value when we actually need it.
            if smaller {
                best_dist[col] = dist;
                best_seg[col] = i;
                let c = cross(tangent_unit, perp);
                // ortho = |c| / |perp|  =>  ortho^2 = c*c / dist
                best_ortho_sq[col] = if dist > 0.0 { c * c / dist } else { 0.0 };
            } else if equal {
                let c = cross(tangent_unit, perp);
                let ortho_sq = if dist > 0.0 { c * c / dist } else { 0.0 };
                if ortho_sq > best_ortho_sq[col] {
                    best_dist[col] = dist;
                    best_seg[col] = i;
                    best_ortho_sq[col] = ortho_sq;
                }
            }
        }
    }

    std::array::from_fn(|i| best_dist[i].is_finite().then_some(best_seg[i]))
}

/// Compute orthogonality for a specific segment using precomputed SoA scratch.
/// Matches `math_segment::orthogonality` semantics.
#[inline]
fn orthogonality_from_soa(pos: Vec2, soa: &SegmentSoa, i: usize) -> f32 {
    let from = soa.from[i];
    let tangent = soa.tangent[i];
    let inv_tsq = soa.inv_tangent_len_sq[i];
    let tangent_unit = soa.tangent_unit[i];

    let pf = pos - from;
    let t = (pf.dot(tangent) * inv_tsq).clamp(0.0, 1.0);
    let perp = pos - (from + tangent * t);
    let perp_len = perp.length();
    if perp_len <= 0.0 {
        return 0.0;
    }
    cross(tangent_unit, perp / perp_len).abs()
}

/// Calculate MSDF (Multi-channel SDF) of a point.
/// Based on Algorithm 7 of Chlumsky's thesis.
///
/// # Returns
/// - Pseudo distance in R, G, B color channels.
pub(crate) fn compute_msdf(
    pos: Vec2,
    soa: &SegmentSoa,
    segs: &[Segment],
    units_per_em: f32,
) -> [f32; 3] {
    let best_seg = find_closest_seg(pos, soa);
    let mut result = [0.; 3];
    for col in 0..3 {
        if let Some(seg) = best_seg[col] {
            let seg = &segs[seg];
            let pseudo_dist = pseudo_signed_dist_point_to_segment(pos, seg.from, seg.mid, seg.to);
            result[col] = pseudo_dist;
        }
    }

    [
        norm_dist(result[0], units_per_em),
        norm_dist(result[1], units_per_em),
        norm_dist(result[2], units_per_em),
    ]
}

/// Calculate MTSDF (Multi-channel + true sdf) of a point.
///
/// # Returns
/// - Pseudo distance in R, G, B color channels, plus true distance in A color channel.
pub(crate) fn compute_mtsdf(
    pos: Vec2,
    soa: &SegmentSoa,
    segs: &[Segment],
    units_per_em: f32,
) -> [u8; 4] {
    let best_seg = find_closest_seg(pos, soa);
    let mut result = [0.; 3];
    let mut closest_segment = 0;
    let mut closest_distance = f32::INFINITY;
    let mut max_ortho = 0.;

    for col in 0..3 {
        if let Some(seg_index) = best_seg[col] {
            let seg = &segs[seg_index];
            let pseudo_dist = pseudo_signed_dist_point_to_segment(pos, seg.from, seg.mid, seg.to);
            result[col] = pseudo_dist;

            let true_dist = unsigned_dist_point_to_segment_sqr(pos, seg.from, seg.to);
            let true_ortho = orthogonality_from_soa(pos, soa, seg_index);
            let diff = true_dist - closest_distance;
            let smaller = diff <= -1.;
            let equal = diff.abs() < 1.;
            if smaller || (equal && true_ortho > max_ortho) {
                closest_distance = true_dist;
                closest_segment = seg_index;
                max_ortho = true_ortho
            }
        }
    }

    if closest_distance.is_finite() {
        let seg = segs[closest_segment];
        closest_distance = signed_dist_point_to_segment(pos, seg.from, seg.mid, seg.to);
    } else {
        closest_distance = 0.;
    }

    [
        (norm_dist(result[0], units_per_em) * 255.) as u8,
        (norm_dist(result[1], units_per_em) * 255.) as u8,
        (norm_dist(result[2], units_per_em) * 255.) as u8,
        (norm_dist(closest_distance, units_per_em) * 255.) as u8,
    ]
}

/// Returns the most common value out of the three.
/// In a MSDF, the median of the 3 channels at each pixel yields the pseudo distance field.
pub fn median(r: u8, g: u8, b: u8) -> u8 {
    u8::max(r.min(g), r.max(g).min(b))
}

#[inline]
fn norm_dist(dist: f32, units_per_em: f32) -> f32 {
    (dist / units_per_em + 0.5).clamp(0., 1.)
}
