// CelerisLab – boundary/inlet_outlet.cuh
// Inlet and outlet dispatch layer.
//
// This file contains only profile helpers, method includes, and compile-time
// switching. Each concrete inlet or outlet implementation lives in its own file
// under boundary/inlet/ or boundary/outlet/.
//
// Inlet scheme IDs:
//   0 = zou_he_local
//   1 = channel_stabilized
//   2 = equilibrium
//   3 = regularized
// ============================================================================

#ifndef CELERIS_BOUNDARY_INLET_OUTLET_CUH
#define CELERIS_BOUNDARY_INLET_OUTLET_CUH

#ifndef INLET_SCHEME
#define INLET_SCHEME 0
#endif

#include "inlet/common.cuh"
#include "inlet/zou_he_local.cuh"
#include "inlet/channel_stabilized.cuh"
#include "inlet/equilibrium.cuh"
#include "inlet/regularized.cuh"
#include "outlet/pressure_neq.cuh"

#if DIM == 2
__device__ __forceinline__ void apply_inlet_pull_d2q9(
    float* __restrict__ f,
    unsigned int x,
    unsigned int y,
    const fpxx* __restrict__ fi_in)
{
    const float u_target = inlet_target_u((float)y);
    const float v_target = 0.0f;

    // Free-slip y-walls: repair west inlet ghost-node known directions
    // that may have been polluted by wall source populations.
    // Required by all schemes using west_velocity_rho_closure_d2q9().
#if Y_WALL_BC == 1
    repair_zou_he_west_knowns_d2q9(f, fi_in, x, y);
#endif

#if INLET_SCHEME == 0
    apply_zou_he_left_velocity_inlet_d2q9(f, u_target, v_target);
#elif INLET_SCHEME == 1 || INLET_SCHEME == 3
    float f_neb[NQ];
    const unsigned long k_neb = linear_index(x + 1u, y);
    for (int i = 0; i < NQ; i++) {
        f_neb[i] = load_ddf(fi_in, index_f(k_neb, (unsigned int)i));
    }
  #if INLET_SCHEME == 1
    apply_channel_stabilized_inlet_d2q9(f, f_neb, (float)y);
  #else
    apply_regularized_left_velocity_inlet_d2q9(f, f_neb, u_target, v_target);
  #endif
#elif INLET_SCHEME == 2
    apply_equilibrium_left_velocity_inlet_d2q9(f, u_target, v_target);
#else
  #error "Unsupported INLET_SCHEME for D2Q9"
#endif
}

__device__ __forceinline__ void apply_outlet_pull_d2q9(
    float* __restrict__ f,
    unsigned int x,
    unsigned int y,
    const fpxx* __restrict__ fi_in)
{
    float f_neb[NQ];
    const unsigned long k_neb = linear_index(x - 1u, y);
    for (int i = 0; i < NQ; i++) {
        f_neb[i] = load_ddf(fi_in, index_f(k_neb, (unsigned int)i));
    }
    apply_pressure_outlet_d2q9(f, f_neb);
}
#endif

#if DIM == 3
__device__ __forceinline__ void apply_inlet_pull_d3q19(
    float* __restrict__ f,
    unsigned int x,
    unsigned int y,
    unsigned int z,
    const fpxx* __restrict__ fi_in)
{
    const float u_target = inlet_target_u((float)y);
    const float v_target = 0.0f;
    const float w_target = 0.0f;

#if INLET_SCHEME == 0
    apply_zou_he_left_velocity_inlet_d3q19(f, u_target, v_target, w_target);
#elif INLET_SCHEME == 1 || INLET_SCHEME == 3
    float f_neb[NQ];
    const unsigned long k_neb = linear_index(x + 1u, y, z);
    for (int i = 0; i < NQ; i++) {
        f_neb[i] = load_ddf(fi_in, index_f(k_neb, (unsigned int)i));
    }
  #if INLET_SCHEME == 1
    apply_channel_stabilized_inlet_d3q19(f, f_neb, (float)y);
  #else
    apply_regularized_left_velocity_inlet_d3q19(f, f_neb, u_target, v_target, w_target);
  #endif
#elif INLET_SCHEME == 2
    apply_equilibrium_left_velocity_inlet_d3q19(f, u_target, v_target, w_target);
#else
  #error "Unsupported INLET_SCHEME for D3Q19"
#endif
}

__device__ __forceinline__ void apply_outlet_pull_d3q19(
    float* __restrict__ f,
    unsigned int x,
    unsigned int y,
    unsigned int z,
    const fpxx* __restrict__ fi_in)
{
    float f_neb[NQ];
    const unsigned long k_neb = linear_index(x - 1u, y, z);
    for (int i = 0; i < NQ; i++) {
        f_neb[i] = load_ddf(fi_in, index_f(k_neb, (unsigned int)i));
    }
    apply_pressure_outlet_d3q19(f, f_neb);
}
#endif

#if DIM == 2
__device__ __forceinline__ void apply_inlet_esopull_d2q9(
    float* __restrict__ f,
    unsigned int x,
    unsigned int y,
    const fpxx* __restrict__ fi,
    unsigned long t)
{
    const float u_target = inlet_target_u((float)y);
    const float v_target = 0.0f;

    // Free-slip y-walls: repair west inlet ghost-node known directions.
#if Y_WALL_BC == 1
    repair_zou_he_west_knowns_d2q9(f, fi, x, y);
#endif

#if INLET_SCHEME == 0
    apply_zou_he_left_velocity_inlet_d2q9(f, u_target, v_target);
#elif INLET_SCHEME == 1 || INLET_SCHEME == 3
    const unsigned long k_neb = linear_index(x + 1u, y);
    unsigned long j_neb[NQ];
    compute_neighbors(x + 1u, y, j_neb);
    float f_neb[NQ];
    load_f_esopull(k_neb, f_neb, fi, j_neb, t);
  #if INLET_SCHEME == 1
    apply_channel_stabilized_inlet_d2q9(f, f_neb, (float)y);
  #else
    apply_regularized_left_velocity_inlet_d2q9(f, f_neb, u_target, v_target);
  #endif
#elif INLET_SCHEME == 2
    apply_equilibrium_left_velocity_inlet_d2q9(f, u_target, v_target);
#else
  #error "Unsupported INLET_SCHEME for D2Q9"
#endif
}

__device__ __forceinline__ void apply_outlet_esopull_d2q9(
    float* __restrict__ f,
    unsigned int x,
    unsigned int y,
    const fpxx* __restrict__ fi,
    unsigned long t)
{
    const unsigned long k_neb = linear_index(x - 1u, y);
    unsigned long j_neb[NQ];
    compute_neighbors(x - 1u, y, j_neb);
    float f_neb[NQ];
    load_f_esopull(k_neb, f_neb, fi, j_neb, t);
    apply_pressure_outlet_d2q9(f, f_neb);
}
#endif

#if DIM == 3
__device__ __forceinline__ void apply_inlet_esopull_d3q19(
    float* __restrict__ f,
    unsigned int x,
    unsigned int y,
    unsigned int z,
    const fpxx* __restrict__ fi,
    unsigned long t)
{
    const float u_target = inlet_target_u((float)y);
    const float v_target = 0.0f;
    const float w_target = 0.0f;

#if INLET_SCHEME == 0
    apply_zou_he_left_velocity_inlet_d3q19(f, u_target, v_target, w_target);
#elif INLET_SCHEME == 1 || INLET_SCHEME == 3
    const unsigned long k_neb = linear_index(x + 1u, y, z);
    unsigned long j_neb[NQ];
    compute_neighbors(k_neb, j_neb);
    float f_neb[NQ];
    load_f_esopull(k_neb, f_neb, fi, j_neb, t);
  #if INLET_SCHEME == 1
    apply_channel_stabilized_inlet_d3q19(f, f_neb, (float)y);
  #else
    apply_regularized_left_velocity_inlet_d3q19(f, f_neb, u_target, v_target, w_target);
  #endif
#elif INLET_SCHEME == 2
    apply_equilibrium_left_velocity_inlet_d3q19(f, u_target, v_target, w_target);
#else
  #error "Unsupported INLET_SCHEME for D3Q19"
#endif
}

__device__ __forceinline__ void apply_outlet_esopull_d3q19(
    float* __restrict__ f,
    unsigned int x,
    unsigned int y,
    unsigned int z,
    const fpxx* __restrict__ fi,
    unsigned long t)
{
    const unsigned long k_neb = linear_index(x - 1u, y, z);
    unsigned long j_neb[NQ];
    compute_neighbors(k_neb, j_neb);
    float f_neb[NQ];
    load_f_esopull(k_neb, f_neb, fi, j_neb, t);
    apply_pressure_outlet_d3q19(f, f_neb);
}
#endif

#endif // CELERIS_BOUNDARY_INLET_OUTLET_CUH
