// CelerisLab – step/aux_kernels.cu
// Auxiliary kernels: CurvedBoundaryKernel, SensorKernel.
// Launched on compact lists (n_curved / n_sensor threads).
// Included by kernel_v2.cu.  No standalone compilation.
//
// CurvedBoundaryKernel: per-link geometry is passed as cl_rx/y/z and runtime
// angular state is read from action[body_id * OBS_BODY_SLOT_FLOATS + ...].
// For DIM==2, wall velocity uses rigid-body rotation: Uw=-omega*ry, Vw=omega*rx.
//
// Important timing:
//   CurvedBoundaryKernel runs BEFORE the main pull step. It writes corrected
//   source populations into obstacle nodes so the pull loader receives valid
//   incoming distributions on the same step.
// ============================================================================

#ifndef CELERIS_STEP_AUX_KERNELS_CU
#define CELERIS_STEP_AUX_KERNELS_CU

__device__ __forceinline__ float action_omega(const float* action, int body_id)
{
    if (action == nullptr || body_id < 0) return 0.0f;
    unsigned int base = (unsigned int)body_id * (unsigned int)OBS_BODY_SLOT_FLOATS;
    return action[base + (unsigned int)(OBS_BODY_SLOT_FLOATS - 1)];
}

__global__ void CurvedBoundaryKernel(
    fpxx* fi,
    const unsigned int* cl_fluid_idx,
    const unsigned char* cl_dir,
    const float* cl_q,
    const float* cl_rx,
    const float* cl_ry,
    const float* cl_rz,
    const unsigned char* cl_fallback_class,
    const int* cl_body_id,
    const float* action,
    float* obs,
    unsigned int n_curved)
{
    unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x;
    if (tid >= n_curved) return;

    const unsigned long k_f = (unsigned long)cl_fluid_idx[tid];
    const unsigned int dir = (unsigned int)cl_dir[tid];
    const float q = cl_q[tid];
    const int bid = cl_body_id[tid];
    const float rx = cl_rx[tid];
    const float ry = cl_ry[tid];
    const unsigned char fallback_class = cl_fallback_class[tid];
    const float omega = action_omega(action, bid);

#if DIM == 2
    const float Uw = -omega * ry;
    const float Vw =  omega * rx;
    (void)cl_rz;
    apply_bouzidi_link(dir, q, k_f, fi, Uw, Vw, rx, ry, fallback_class, obs, bid);
#elif DIM == 3
    const float rz = cl_rz[tid];
    const float Uw = -omega * ry;
    const float Vw =  omega * rx;
    const float Ww = 0.0f;
    apply_bouzidi_link(dir, q, k_f, fi, Uw, Vw, Ww,
                       rx, ry, rz, fallback_class, obs, bid);
#endif
}

__global__ void SensorKernel(
    const fpxx* fi,
    const unsigned short* flag,
    const unsigned int* sensor_cells,
    const int* sensor_obj_id,
    float* obs,
    unsigned int n_sensor)
{
    unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x;
    if (tid >= n_sensor) return;

    unsigned long k = (unsigned long)sensor_cells[tid];
    int id_obj = sensor_obj_id[tid];

    float f[NQ];
    for (int i = 0; i < NQ; i++)
        f[i] = load_ddf(fi, index_f(k, (unsigned int)i));

#if DIM == 2
    float rho_n, ux, uy;
    compute_rho_u(f, rho_n, ux, uy);
    atomicAdd(&obs[obs_sensor_index(id_obj, 0)], ux);
    atomicAdd(&obs[obs_sensor_index(id_obj, 1)], uy);
#elif DIM == 3
    float rho_n, ux, uy, uz;
    compute_rho_u(f, rho_n, ux, uy, uz);
    atomicAdd(&obs[obs_sensor_index(id_obj, 0)], ux);
    atomicAdd(&obs[obs_sensor_index(id_obj, 1)], uy);
    atomicAdd(&obs[obs_sensor_index(id_obj, 2)], uz);
#endif

    (void)flag;
}

#endif // CELERIS_STEP_AUX_KERNELS_CU
