// CelerisLab -- step/esopull_sync.cu
// Compact-list EsoPull decode/encode for runtime body topology sync.
//
// Decodes a subset of cells from EsoPull backing layout to physical DDF,
// or encodes physical DDF back to EsoPull backing layout.
// Each thread handles one cell from a compact index list.
//
// Included by kernel_v2.cu.  No standalone compilation.
// ============================================================================

#ifndef CELERIS_STEP_ESOPULL_SYNC_CU
#define CELERIS_STEP_ESOPULL_SYNC_CU

// ---------------------------------------------------------------------------
// DecodeCellsToPhysical:  backing layout -> physical DDF (compact list)
//
// Uses ``t`` as the step count for EsoPull semantic parity decoding.
// ---------------------------------------------------------------------------
__global__ void DecodeCellsToPhysical(
    const fpxx* fi,
    float* fi_phys,
    const unsigned int* cell_idx,
    unsigned int n_cells,
    unsigned long t)
{
    unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x;
    if (tid >= n_cells) return;

    unsigned long k = (unsigned long)cell_idx[tid];
    unsigned int x, y; coordinates(k, x, y);
    unsigned long j[NQ]; compute_neighbors(x, y, j);

    float f[NQ];
    load_physical_node_esopull(k, fi, j, t, f);

    // DDF-shifting: backing layout stores f-w, unshift to physical for host patch
#if USE_DDF_SHIFTING
    #pragma unroll
    for (int i = 0; i < NQ; i++)
        f[i] += (float)d_w[i];
#endif

    for (int i = 0; i < NQ; i++)
        fi_phys[tid * NQ + i] = f[i];
}

// ---------------------------------------------------------------------------
// EncodePhysicalToCells:  physical DDF -> backing layout (compact list)
//
// Reads physical (pre-collision) DDF from *fi_phys*, applies collision
// to produce post-collision DDF, then encodes into the EsoPull backing
// layout on *fi*.  The backing layout stores post-collision values,
// matching what store_f_esopull would produce at step *t*.
// ---------------------------------------------------------------------------
__global__ void EncodePhysicalToCells(
    fpxx* fi,
    const float* fi_phys,
    const unsigned int* cell_idx,
    unsigned int n_cells,
    unsigned long t)
{
    unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x;
    if (tid >= n_cells) return;

    unsigned long k = (unsigned long)cell_idx[tid];
    unsigned int x, y; coordinates(k, x, y);
    unsigned long j[NQ]; compute_neighbors(x, y, j);

    float f[NQ];
    for (int i = 0; i < NQ; i++)
        f[i] = fi_phys[tid * NQ + i];

    // Collide: physical (pre-collision) -> post-collision
    float rho_n, ux, uy;
    compute_rho_u(f, rho_n, ux, uy);
    collide_dispatch(f, rho_n, ux, uy);

    store_physical_node_esopull(k, f, fi, j, t);
}

#endif // CELERIS_STEP_ESOPULL_SYNC_CU
