use crate::types::Vec3;
use std::f64::consts::PI;

/// H-field generated by a infinitesimal point current source
///
/// Handles singularities within the point element by providing it finite radius
pub fn h_point(centroid: &Vec3, vj: &Vec3, radius: f64, target: &Vec3) -> Vec3 {
    let r: Vec3 = *target - *centroid;
    let mut vjr: Vec3 = (*vj).cross(&r);
    let rmag: f64 = r.mag();

    if rmag < radius {
        let inside_scale: f64 = (0.25 / PI) * rmag * rmag * rmag / (radius * radius * radius);
        vjr[0] *= inside_scale;
        vjr[1] *= inside_scale;
        vjr[2] *= inside_scale;
    } else {
        let inv_rmag3 = (0.25 / PI) / (rmag * rmag * rmag);
        vjr[0] *= inv_rmag3;
        vjr[1] *= inv_rmag3;
        vjr[2] *= inv_rmag3;
    }

    vjr
}

/// H-field generated by an infinitesimal point dipole source
///
/// For now, applies zero field to internal points. The use of this function should be limited
/// to self (zero) or adjacent elements in the mesh, not arbitrary points in the mesh domain.
/// TODO: Use the internal-sphere correction provided in ex 6.1 of griffith's:
/// <https://peppyhare.github.io/griffiths-em/ch6-2/>
/// and in Fitzpatrick:
/// <https://farside.ph.utexas.edu/teaching/jk1/Electromagnetism/node61.html>
#[inline]
pub fn h_point_dipole(centroid: &Vec3, moment: &Vec3, radius: f64, target: &Vec3) -> Vec3 {
    let r = *target - *centroid;
    let rmag = r.mag();

    if rmag <= radius {
        // Internal magnetization field provided by the dipole moment at `radius`
        let m_field = *moment * (3.0 / (4.0 * PI * radius.powi(3)));
        let h_field = m_field * (-1.0 / 3.0);
        return h_field;
    }

    // outside the sphere
    let rmag_inv = 1.0 / rmag;
    let rhat = r * rmag_inv;

    let scale = (1.0 / (4.0 * PI)) * rmag_inv.powi(3);
    ((rhat * (3.0 * rhat.dot(moment))) - *moment) * scale
}
