// CelerisLab – boundary/bounce_back.cuh
// Half-way bounce-back for grid-aligned solid walls (pull-streaming variant).
//
// For wall-adjacent fluid nodes, the pull step loaded garbage from the wall.
// We replace those directions with the opposite-direction population at the
// SAME node from the INPUT buffer (half-way BB).
// ============================================================================

#ifndef CELERIS_BOUNDARY_BOUNCE_BACK_CUH
#define CELERIS_BOUNDARY_BOUNCE_BACK_CUH

// ---------------------------------------------------------------------------
// D2Q9 y-wall half-way bounce-back for pull double-buffer streaming.
// ---------------------------------------------------------------------------
#if LATTICE_MODEL == LATTICE_D2Q9
__device__ inline void apply_wall_bb_d2q9_y_pull(unsigned int y,
                                                  float* __restrict__ f,
                                                  const fpxx* __restrict__ fi_in,
                                                  unsigned long k)
{
    if (y == 1) {
        // Directions sourced from y=0 wall in pull step: +y, +x+y, -x+y.
        f[3] = load_ddf(fi_in, index_f(k, 4u));
        f[5] = load_ddf(fi_in, index_f(k, 6u));
        f[8] = load_ddf(fi_in, index_f(k, 7u));
    }
    else if (y == (unsigned int)(NY - 2)) {
        // Directions sourced from y=NY-1 wall in pull step: -y, -x-y, +x-y.
        f[4] = load_ddf(fi_in, index_f(k, 3u));
        f[6] = load_ddf(fi_in, index_f(k, 5u));
        f[7] = load_ddf(fi_in, index_f(k, 8u));
    }
}

// ---------------------------------------------------------------------------
// D2Q9 y-wall free-slip (specular reflection) for pull double-buffer streaming.
//
// Normal components use the same mapping as half-way BB at horizontal walls:
//   (0,+1) <-> (0,-1)
// Tangential components mirror across wall normal:
//   (+1,+1) <-> (+1,-1), (-1,+1) <-> (-1,-1)
// ---------------------------------------------------------------------------
__device__ inline void apply_wall_freeslip_d2q9_y_pull(unsigned int y,
                                                        float* __restrict__ f,
                                                        const fpxx* __restrict__ fi_in,
                                                        unsigned long k)
{
    if (y == 1) {
        f[3] = load_ddf(fi_in, index_f(k, 4u));
        f[5] = load_ddf(fi_in, index_f(k, 7u));
        f[8] = load_ddf(fi_in, index_f(k, 6u));
    }
    else if (y == (unsigned int)(NY - 2)) {
        f[4] = load_ddf(fi_in, index_f(k, 3u));
        f[6] = load_ddf(fi_in, index_f(k, 8u));
        f[7] = load_ddf(fi_in, index_f(k, 5u));
    }
}

#endif

// ---------------------------------------------------------------------------
// D3Q19 y-wall half-way bounce-back for pull double-buffer streaming.
//
// Instead of swapping f[i] <-> f[opp(i)] (which corrupts the toward-wall
// direction with garbage from the wall node), we read the opposite direction
// at the SAME node from the INPUT buffer.  That value is the post-collision
// population from the previous time step — exactly the correct half-way BB.
//
// At y=1  (wall at y=0):  directions that arrive FROM y=0 have cy_src = -1.
//   The pull loaded them from the wall (garbage).  Replace with fi_in[k, opp].
//
// At y=NY-2 (wall at y=NY-1): directions from y=NY-1, cy_src = +1.
// ---------------------------------------------------------------------------
#if LATTICE_MODEL == LATTICE_D3Q19
__device__ inline void apply_wall_bb_d3q19_y_pull(unsigned int y,
                                                   float* __restrict__ f,
                                                   const fpxx* __restrict__ fi_in,
                                                   unsigned long k)
{
    if (y == 1) {
        // Directions whose pull-source is at y=0 (wall):
        //   f[3]  (+y)    ← source j[4]  = (x, 0, z)
        //   f[7]  (+x+y)  ← source j[8]  = (x-1, 0, z)
        //   f[11] (+y+z)  ← source j[12] = (x, 0, z-1)
        //   f[14] (-x+y)  ← source j[13] = (x+1, 0, z)
        //   f[17] (+y-z)  ← source j[18] = (x, 0, z+1)
        f[3]  = load_ddf(fi_in, index_f(k, 4u));
        f[7]  = load_ddf(fi_in, index_f(k, 8u));
        f[11] = load_ddf(fi_in, index_f(k, 12u));
        f[14] = load_ddf(fi_in, index_f(k, 13u));
        f[17] = load_ddf(fi_in, index_f(k, 18u));
    }
    else if (y == (unsigned int)(NY - 2)) {
        // Directions whose pull-source is at y=NY-1 (wall):
        //   f[4]  (-y)    ← source j[3]  = (x, NY-1, z)
        //   f[8]  (-x-y)  ← source j[7]  = (x+1, NY-1, z)
        //   f[12] (-y-z)  ← source j[11] = (x, NY-1, z+1)
        //   f[13] (+x-y)  ← source j[14] = (x-1, NY-1, z)
        //   f[18] (-y+z)  ← source j[17] = (x, NY-1, z-1)
        f[4]  = load_ddf(fi_in, index_f(k, 3u));
        f[8]  = load_ddf(fi_in, index_f(k, 7u));
        f[12] = load_ddf(fi_in, index_f(k, 11u));
        f[13] = load_ddf(fi_in, index_f(k, 14u));
        f[18] = load_ddf(fi_in, index_f(k, 17u));
    }
}
#endif

#endif // CELERIS_BOUNDARY_BOUNCE_BACK_CUH
