import { assert } from "../assert.js";

/**
 * Computes associated Legendre Polynomial P(l,m,x) at x
 * Useful in Spherical Harmonics coefficient computation
 * @param {number} l band
 * @param {number} m
 * @param {number} x must be between 0 and 1
 * @returns {number}
 */
export function compute_legendre_polynomial(l, m, x) {
    assert.lessThanOrEqual(x,1,'only valid for X <= 1');

    // TODO we might be able to do better, see https://github.com/halueda/wavy-orbitals/blob/fe738b0f8de8783cb7886d2c6ee616c2befb363b/legendre.js#L16
    // based on code from https://github.com/jan-van-bergen/SphericalHarmonicLighting/blob/2b214b3451859ded07ea4e41fb2aa8d2a44ab579/SphericalHarmonicsLighting/SphericalHarmonics.cpp#L61

    // Apply rule 2; P m m
    let pmm = 1.0;
    if (m > 0) {
        const somx2 = Math.sqrt((1.0 - x) * (1.0 + x));
        let fact = 1.0;

        for (let i = 1; i <= m; i++) {
            pmm *= (-fact) * somx2;
            fact += 2.0;
        }
    }

    // If l is equal to m then P m m is already the right answer
    if (l === m) {
        return pmm;
    }

    // Use rule 3 once
    let pmmp1 = x * (2.0 * m + 1.0) * pmm;

    if (l === m + 1) {
        return pmmp1;
    }

    // Iterate rule 1 until the right answer is found
    let pll = 0.0;

    for (let ll = m + 2; ll <= l; ll++) {
        pll = ((2.0 * ll - 1.0) * x * pmmp1 - (ll + m - 1.0) * pmm) / (ll - m);
        pmm = pmmp1;
        pmmp1 = pll;
    }

    return pll;
}