import { assert } from "../../../../core/assert.js";

/**
 * Compute the divergence of a cell-centered velocity field into a scalar field.
 *
 *     div(c) ≈ 0.5 * ( u(x+1) - u(x-1) + v(y+1) - v(y-1) + w(z+1) - w(z-1) )
 *
 * Boundary cells use one-sided differences (`u(x+1) - u(x)` or `u(x) - u(x-1)`), keeping
 * the scheme consistent for free outflow. Solid cells get divergence 0 — they are not
 * fluid and don't participate in the pressure solve.
 *
 * Unit assumption: cell spacing h = 1 (positions are in grid units). If you advect with
 * "world units per second" velocities, convert before calling.
 *
 * @param {Float32Array} divergence  Destination, mutated.
 * @param {Float32Array} vel_x
 * @param {Float32Array} vel_y
 * @param {Float32Array} vel_z
 * @param {number} res_x
 * @param {number} res_y
 * @param {number} res_z
 * @param {Uint8Array} solid          Required (zero-filled for a wall-free domain).
 */
export function v3_grid_compute_divergence(divergence, vel_x, vel_y, vel_z, res_x, res_y, res_z, solid) {
    const cell_count = res_x * res_y * res_z;
    assert.greaterThanOrEqual(divergence.length, cell_count, "divergence 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;
    const last_x = res_x - 1;
    const last_y = res_y - 1;
    const last_z = res_z - 1;

    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) {
                    divergence[c] = 0;
                    continue;
                }

                let du, dv, dw;

                if (x === 0)            du = vel_x[c + 1]          - vel_x[c];
                else if (x === last_x)  du = vel_x[c]              - vel_x[c - 1];
                else                    du = 0.5 * (vel_x[c + 1]   - vel_x[c - 1]);

                if (y === 0)            dv = vel_y[c + res_x]      - vel_y[c];
                else if (y === last_y)  dv = vel_y[c]              - vel_y[c - res_x];
                else                    dv = 0.5 * (vel_y[c + res_x] - vel_y[c - res_x]);

                if (z === 0)            dw = vel_z[c + slice_size] - vel_z[c];
                else if (z === last_z)  dw = vel_z[c]              - vel_z[c - slice_size];
                else                    dw = 0.5 * (vel_z[c + slice_size] - vel_z[c - slice_size]);

                divergence[c] = du + dv + dw;
            }
        }
    }
}
