import { assert } from "../../../../core/assert.js";
import { scs3d_sample_linear } from "../../../graphics/texture/3d/scs3d_sample_linear.js";

/**
 * Semi-Lagrangian advection of a scalar field by a separate velocity field.
 *
 * For each cell, traces backward by `(vel_x, vel_y, vel_z) * time_delta` and trilinearly
 * samples the input scalar at the back-traced position. Unconditionally stable for any
 * timestep. Used to transport densities, temperatures, or any passive quantity by the
 * flow.
 *
 * @param {Float32Array} output       Destination, mutated. Length = res_x*res_y*res_z.
 * @param {Float32Array} input        Scalar values to advect. Must not alias `output`.
 * @param {Float32Array} vel_x        Velocity X component (cell-centered).
 * @param {Float32Array} vel_y        Velocity Y component (cell-centered).
 * @param {Float32Array} vel_z        Velocity Z component (cell-centered).
 * @param {number} res_x
 * @param {number} res_y
 * @param {number} res_z
 * @param {number} time_delta
 * @param {Uint8Array} solid          Required cell flags (zero-filled for a wall-free
 *                                    domain); solid cells get their input value
 *                                    passed through unchanged.
 */
export function v3_grid_apply_scalar_advection(output, input, vel_x, vel_y, vel_z, res_x, res_y, res_z, time_delta, solid) {
    assert.notEqual(output, input, "output must not alias input");

    const cell_count = res_x * res_y * res_z;

    assert.greaterThanOrEqual(output.length, cell_count, "output covers grid");
    assert.greaterThanOrEqual(input.length, cell_count, "input covers grid");
    assert.greaterThanOrEqual(vel_x.length, cell_count, "vel_x covers grid");
    assert.greaterThanOrEqual(vel_y.length, cell_count, "vel_y covers grid");
    assert.greaterThanOrEqual(vel_z.length, cell_count, "vel_z covers grid");

    const slice_size = res_x * res_y;

    for (let z = 0; z < res_z; z++) {
        const z_off = z * slice_size;

        for (let y = 0; y < res_y; y++) {
            const y_off = y * res_x;

            for (let x = 0; x < res_x; x++) {
                const c = z_off + y_off + x;

                if (solid[c] !== 0) {
                    output[c] = input[c];
                    continue;
                }

                const s_x = x - vel_x[c] * time_delta;
                const s_y = y - vel_y[c] * time_delta;
                const s_z = z - vel_z[c] * time_delta;

                output[c] = scs3d_sample_linear(input, res_x, res_y, res_z, s_x, s_y, s_z);
            }
        }
    }
}
