import { v2_dot } from "../../../geom/vec2/v2_dot.js";
import { cadd, cdiv, cmult, vec2 } from "./lorenz_mie_coefs.js";

/**
 * Calculates the final optical properties by summing the Mie coefficients.
 * This function implements Equations (2) and (3) from the 2007 paper
 * "Computing the Scattering Properties of Participating Media..."
 *
 * @param {Float32Array} ab - The flat array of a_n, b_n coefficients from LorenzMie_coefs.
 * @param {number} radius - Particle radius (in meters).
 * @param {number} wavelength - Wavelength of light in vacuum (in meters).
 * @param {vec2} n_med - Complex refractive index of the medium (e.g., [1.0, 0.0] for air).
 * @returns {object} An object with Q_e, Q_s, albedo, C_ext, and C_sca.
 */
export function mie_ab_to_optical_properties(
    ab,
    wavelength,
    radius,
    n_med
) {
    const M = ab.length / 4;

    let sum_t = vec2(0, 0); // Complex sum for Ct
    let sum_s = 0; // Real sum for Cs
    let sum_g = 0;          // Real sum for Asymmetry (g)

    for (let n = 1; n < M; ++n) {
        // The 'n' in the paper's formulas
        const multiplier = 2 * n + 1;

        // Get an and bn from your flat array
        const an = vec2(ab[4 * n], ab[4 * n + 1]);
        const bn = vec2(ab[4 * n + 2], ab[4 * n + 3]);

        // 1. For Extinction (Ct)
        const an_plus_bn = cadd(an, bn);
        // You need complex math functions cadd, cdiv
        const n_med_sq = cmult(n_med, n_med);
        const term_t = cdiv(an_plus_bn, n_med_sq);
        sum_t = cadd(sum_t, cmult(vec2(multiplier, 0), term_t));

        // 2. For Scattering (Cs)
        const an_mag_sq = an[0] * an[0] + an[1] * an[1];
        const bn_mag_sq = bn[0] * bn[0] + bn[1] * bn[1];
        sum_s += multiplier * (an_mag_sq + bn_mag_sq);

        // 3. Asymmetry Accumulation (New)
        // Term A: Interference of an and bn
        // Formula: (2n+1) / (n(n+1)) * Re(an * bn*)
        const factor_A = (2 * n + 1) / (n * (n + 1));
        sum_g += factor_A * v2_dot(an[0], an[1], bn[0], bn[1]);

        // Term B: Interference of n and n+1
        // Formula: (n(n+2)) / (n+1) * Re(an * an+1* + bn * bn+1*)
        if (n < M - 1) {
            const n_next = n + 1;
            const an1_r = ab[4 * n_next];
            const an1_i = ab[4 * n_next + 1];
            const bn1_r = ab[4 * n_next + 2];
            const bn1_i = ab[4 * n_next + 3];

            const factor_B = (n * (n + 2)) / (n + 1);

            const term_an_an1 = v2_dot(an[0], an[1], an1_r, an1_i);
            const term_bn_bn1 = v2_dot(bn[0], bn[1], bn1_r, bn1_i);

            sum_g += factor_B * (term_an_an1 + term_bn_bn1);
        }
    }

    // --- Final Calculations ---

    // Asymmetry Parameter (g)
    // g = (4/x^2 * sum_g) / (2/x^2 * sum_s) => 2 * sum_g / sum_s
    let g = 0;
    if (sum_s > 1e-12) {
        g = (2 * sum_g) / sum_s;
    }

    // Extinction (Ct from Eq. 22)
    const C_t = (Math.pow(wavelength, 2) / (2 * Math.PI)) * sum_t[0]; //

    // Scattering (Cs from Eq. 23)
    const alpha = (4 * Math.PI * radius * n_med[1]) / wavelength;
    let gamma;
    if (Math.abs(alpha) < 1e-10) {
        // avoid division by 0
        gamma = 1;
    } else {
        gamma = (2 * (1 + (alpha - 1) * Math.exp(alpha))) / Math.pow(alpha, 2);
    }
    const n_med_mag_sq = n_med[0] * n_med[0] + n_med[1] * n_med[1];
    const C_s_factor = (Math.pow(wavelength, 2) * Math.exp(-alpha)) / (2 * Math.PI * gamma * n_med_mag_sq);
    const C_s = C_s_factor * sum_s;

    // Bulk Coefficients (assuming number density N)
    const sigma_a_med = (4 * Math.PI * n_med[1]) / wavelength;

    // To get bulk coefficients sigma_t and sigma_s, use the following formulae, where N is numeric density (particles per cubic meter):
    // sigma_t = sigma_a_med + C_t * N;
    // sigma_s = C_s * N;

    return {
        sigma_a_med,
        C_ext: C_t,
        C_sca: C_s,
        g, // Anisotropy parameter
    }
}