/**
 * Fill the edges exposed by {@link v3_grid_shift_in_place} (with the same
 * `(shift_x, shift_y, shift_z)`) by replicating the adjacent valid column / row /
 * slice — zero-gradient Neumann extrapolation, equivalent to "the field is locally
 * uniform near the boundary".
 *
 * Axes are patched independently in x → y → z order. Corner cells (where two or
 * three axis-edges meet) are written by every axis they belong to; the final value
 * comes from the last axis, which sees the previously-patched cells as already-valid
 * neighbours.
 *
 * No-ops:
 *   - All shifts zero → nothing to patch.
 *   - `|shift| >= res` on any axis → the shift primitive already zero-filled the
 *     buffer; nothing to replicate from.
 *
 * @param {Float32Array|Uint8Array} buffer
 * @param {number} res_x
 * @param {number} res_y
 * @param {number} res_z
 * @param {number} shift_x  integer; same value passed to v3_grid_shift_in_place
 * @param {number} shift_y  integer
 * @param {number} shift_z  integer
 */
export function v3_grid_patch_edges_uniform(buffer, res_x, res_y, res_z, shift_x, shift_y, shift_z) {
    if (shift_x === 0 && shift_y === 0 && shift_z === 0) {
        return;
    }

    if (Math.abs(shift_x) >= res_x || Math.abs(shift_y) >= res_y || Math.abs(shift_z) >= res_z) {
        // Full clear from the shift primitive; no valid neighbour to replicate.
        return;
    }

    if (shift_x !== 0) patch_x(buffer, res_x, res_y, res_z, shift_x);
    if (shift_y !== 0) patch_y(buffer, res_x, res_y, res_z, shift_y);
    if (shift_z !== 0) patch_z(buffer, res_x, res_y, res_z, shift_z);
}

function patch_x(buf, rx, ry, rz, shift) {
    const slice = rx * ry;
    if (shift > 0) {
        // Exposed: x in [rx - shift, rx). Source: column x = rx - shift - 1.
        const src_x = rx - shift - 1;
        const first_exposed = rx - shift;
        for (let z = 0; z < rz; z++) {
            const z_off = z * slice;
            for (let y = 0; y < ry; y++) {
                const row = z_off + y * rx;
                const src_value = buf[row + src_x];
                for (let x = first_exposed; x < rx; x++) {
                    buf[row + x] = src_value;
                }
            }
        }
    } else {
        const n = -shift;
        // Exposed: x in [0, n). Source: column x = n.
        for (let z = 0; z < rz; z++) {
            const z_off = z * slice;
            for (let y = 0; y < ry; y++) {
                const row = z_off + y * rx;
                const src_value = buf[row + n];
                for (let x = 0; x < n; x++) {
                    buf[row + x] = src_value;
                }
            }
        }
    }
}

function patch_y(buf, rx, ry, rz, shift) {
    const slice = rx * ry;
    if (shift > 0) {
        // Exposed: y in [ry - shift, ry). Source: row y = ry - shift - 1.
        const src_y = ry - shift - 1;
        const first_exposed = ry - shift;
        for (let z = 0; z < rz; z++) {
            const z_off = z * slice;
            const src_row = z_off + src_y * rx;
            for (let y = first_exposed; y < ry; y++) {
                buf.copyWithin(z_off + y * rx, src_row, src_row + rx);
            }
        }
    } else {
        const n = -shift;
        for (let z = 0; z < rz; z++) {
            const z_off = z * slice;
            const src_row = z_off + n * rx;
            for (let y = 0; y < n; y++) {
                buf.copyWithin(z_off + y * rx, src_row, src_row + rx);
            }
        }
    }
}

function patch_z(buf, rx, ry, rz, shift) {
    const slice = rx * ry;
    if (shift > 0) {
        // Exposed: z in [rz - shift, rz). Source: slice z = rz - shift - 1.
        const src_slice_start = (rz - shift - 1) * slice;
        const first_exposed = rz - shift;
        for (let z = first_exposed; z < rz; z++) {
            buf.copyWithin(z * slice, src_slice_start, src_slice_start + slice);
        }
    } else {
        const n = -shift;
        const src_slice_start = n * slice;
        for (let z = 0; z < n; z++) {
            buf.copyWithin(z * slice, src_slice_start, src_slice_start + slice);
        }
    }
}
