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

/**
 * Spherical linear interpolation
 * NOTE: vectors are assumed to be normalized
 * @see https://en.wikipedia.org/wiki/Slerp
 * @param {Vector3|{set(x:number,y:number,z:number)}} result Rotated vector is written here
 * @param {number} a_x First vector
 * @param {number} a_y First vector
 * @param {number} a_z First vector
 * @param {number} b_x Second vector
 * @param {number} b_y Second vector
 * @param {number} b_z Second vector
 * @param {number} fraction Interpolation factor
 */
export function v3_slerp(
    result,
    a_x, a_y, a_z,
    b_x, b_y, b_z,
    fraction
) {
    assert.isNumber(fraction, 'fraction');
    assert.notNaN(fraction, "fraction");
    assert.greaterThanOrEqual(fraction, 0, "fraction");

    const dot = v3_dot(
        a_x, a_y, a_z,
        b_x, b_y, b_z
    );

    if (dot >= 1 || dot <= -1) {
        // avoid division by 0, both vectors are pointing in the same direction, use lerp instead
        v3_lerp(result, a_x, a_y, a_z, b_x, b_y, b_z, fraction);
        return;
    }

    // compute angle between two vectors
    const theta = Math.acos(dot);

    const sin_theta = Math.sin(theta);

    const inv_sin_theta = 1 / sin_theta;

    // TODO check for sin_theta == 0 to avoid division by 0

    const scale_0 = Math.sin((1 - fraction) * theta) * inv_sin_theta;
    const scale_1 = Math.sin((fraction) * theta) * inv_sin_theta;

    const x = a_x * scale_0 + b_x * scale_1;
    const y = a_y * scale_0 + b_y * scale_1;
    const z = a_z * scale_0 + b_z * scale_1;

    result.set(x, y, z);
}
