import { assert } from "../../assert.js";
import { array_copy } from "../../collection/array/array_copy.js";
import { clamp } from "../clamp.js";
import { computeNonuniformCatmullRomSplineSample } from "./computeNonuniformCatmullRomSplineSample.js";

/**
 *
 * @type {number[]}
 */
const sample = [];
/**
 *
 * @type {number[]}
 */
const p0 = [];
/**
 *
 * @type {number[]}
 */
const p1 = [];
/**
 *
 * @type {number[]}
 */
const p2 = [];
/**
 *
 * @type {number[]}
 */
const p3 = [];

/**
 * Takes distances between points into account and samples at equal intervals along linear distance along the point sequence. Computationally slightly more expensive, but produces very predictable and stable results
 * @param {number[]|Float32Array} result
 * @param {number[]|Float32Array} input
 * @param {number} input_length number of points in the input
 * @param {number} dimensions number of dimensions per vertex
 * @param {number} sample_count number of discrete points to be generated
 * @param {number} [alpha=0.5] parameter for control point weights (see non-parametric catmull-rom for details on "alpha" definition)
 */
export function computeCatmullRomSplineUniformDistance(result, input, input_length, dimensions, sample_count, alpha = 0.5) {
    assert.greaterThan(dimensions, 0, 'number of dimensions must be greater than 0');
    assert.isNonNegativeInteger(dimensions, 'dimensions');

    assert.greaterThanOrEqual(sample_count, 2, 'number of samples must be >= 2');
    assert.isNonNegativeInteger(sample_count, 'sample_count');

    // we need to pre-compute come values such as distances between each point pair in the input
    const distances = [];
    let distances_sum = 0;

    // compute distances
    for (let i = 1; i < input_length; i++) {
        let sqr_d = 0;
        for (let j = 0; j < dimensions; j++) {
            const v0 = input[(i - 1) * dimensions + j];
            const v1 = input[i * dimensions + j];

            const dx = v0 - v1;

            sqr_d += dx * dx;
        }

        const distance = Math.sqrt(sqr_d);

        distances_sum += distance;
        distances[i - 1] = distances_sum;
    }

    const step_distance = distances_sum / (sample_count - 1);

    let cursor_index = 0;

    let current_input_distance = 0;
    let next_point_distance = 0;
    let included_point_count = 0;

    while (cursor_index < input_length) {
        const next_input_distance = distances[cursor_index];
        const distance_delta = next_input_distance - current_input_distance;

        while (next_input_distance >= next_point_distance) {
            // include point


            let t;

            if (distance_delta === 0) {
                t = 0;
            } else {
                const relative_position = next_point_distance - current_input_distance;
                t = relative_position / distance_delta
            }

            const i0 = clamp(cursor_index - 1, 0, input_length - 1);
            const i1 = clamp(cursor_index, 0, input_length - 1);
            const i2 = clamp(cursor_index + 1, 0, input_length - 1);
            const i3 = clamp(cursor_index + 2, 0, input_length - 1);

            // read relevant inputs
            array_copy(input, i0 * dimensions, p0, 0, dimensions);
            array_copy(input, i1 * dimensions, p1, 0, dimensions);
            array_copy(input, i2 * dimensions, p2, 0, dimensions);
            array_copy(input, i3 * dimensions, p3, 0, dimensions);

            computeNonuniformCatmullRomSplineSample(sample, p0, p1, p2, p3, dimensions, t, alpha);

            const result_address = included_point_count * dimensions;

            array_copy(sample, 0, result, result_address, dimensions);

            // increment search variables for the next point
            included_point_count++;
            next_point_distance += step_distance;
        }

        cursor_index++;
        current_input_distance = next_input_distance;
    }

    if (included_point_count < sample_count) {
        // include last point
        array_copy(input, (input_length - 1) * dimensions, result, (sample_count - 1) * dimensions, dimensions);
    }

}
