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

/**
 * Computes derivative of Catmull Rom spline
 * @param {number[]} result_value
 * @param {number} result_value_offset
 * @param {number[]} result_derivative
 * @param {number} result_derivative_offset
 * @param {number[]} p0 spline point 0
 * @param {number[]} p1 spline point 1
 * @param {number[]} p2 spline point 2
 * @param {number[]} p3 spline point 3
 * @param {number} dimensions number of dimensions in the input and output vectors
 * @param {number} f between 0..1
 * @param {number} alpha between 0..1
 */
export function computeNonuniformCaltmullRomSplineDerivative(
    result_value, result_value_offset,
    result_derivative, result_derivative_offset,
    p0, p1, p2, p3,
    dimensions, f, alpha
) {
    /*
    @see https://math.stackexchange.com/questions/843595/how-can-i-calculate-the-derivative-of-a-catmull-rom-spline-with-nonuniform-param
    @see http://denkovacs.com/2016/02/catmull-rom-spline-derivatives/
    @see https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline
     */

    const half_alpha = alpha * 0.5;

    // calculate T
    const t0 = 0;
    let t_01 = catmull_rom_compute_T(p0, p1, dimensions, half_alpha);
    let t_02 = catmull_rom_compute_T(p1, p2, dimensions, half_alpha);
    let t_03 = catmull_rom_compute_T(p2, p3, dimensions, half_alpha);

    // safety check for repeated points, to prevent division by 0
    if (t_01 < 1e-4) {
        t_01 = 1;
    }
    if (t_02 < 1e-4) {
        t_02 = t_01;
    }
    if (t_03 < 1e-4) {
        t_03 = t_02;
    }

    const t1 = t_01 + t0;
    const t2 = t_02 + t1;
    const t3 = t_03 + t2;

    /**
     * Interpolation between points 1 and 2
     * @type {number}
     */
    const t = t1 * (1 - f) + t2 * f;

    for (let i = 0; i < dimensions; i++) {
        // read vector values for the dimension
        const v0 = p0[i];
        const v1 = p1[i];
        const v2 = p2[i];
        const v3 = p3[i];

        const A1 = (t1 - t) / (t1 - t0) * v0 + (t - t0) / (t1 - t0) * v1;
        const A2 = (t2 - t) / (t2 - t1) * v1 + (t - t1) / (t2 - t1) * v2;
        const A3 = (t3 - t) / (t3 - t2) * v2 + (t - t2) / (t3 - t2) * v3;

        const B1 = (t2 - t) / (t2 - t0) * A1 + (t - t0) / (t2 - t0) * A2;
        const B2 = (t3 - t) / (t3 - t1) * A2 + (t - t1) / (t3 - t1) * A3;

        const C = (t2 - t) / (t2 - t1) * B1 + (t - t1) / (t2 - t1) * B2;

        result_value[result_value_offset + i] = C;

        // derivatives

        const dA1 = (v1 - v0) / (t1 - t0)
        const dA2 = (v2 - v1) / (t2 - t1);
        const dA3 = (v3 - v2) / (t3 - t2);

        const dB1 = (A2 - A1) / (t2 - t0) + dA1 * (t2 - t) / (t2 - t0) + dA2 * (t - t0) / (t2 - t0);
        const dB2 = (A3 - A2) / (t3 - t1) + dA2 * (t3 - t) / (t3 - t1) + dA3 * (t - t1) / (t3 - t1);

        const dC = (B2 - B1) / (t2 - t1) + dB1 * (t2 - t) / (t2 - t1) + dB2 * (t - t1) / (t2 - t1);

        result_derivative[result_derivative_offset + i] = dC;
    }

}