"""
$(TYPEDSIGNATURES)

Return a fully materialized advection scheme appropriate for `grid`.
It exists to allow advection schemes to defer specialising settings until
additional information about the backend from grid is available.

For example it allows to set per-backend defaults for WENO weight computation
setting.
"""
materialize_advection(advection, grid) = advection
materialize_advection(::Nothing, grid) = nothing

materialize_advection(advection::FluxFormAdvection, grid) = FluxFormAdvection(
    materialize_advection(advection.x, grid),
    materialize_advection(advection.y, grid),
    materialize_advection(advection.z, grid),
)

# Upwinding treatments hold a cross_scheme that may contain deferred WENO weight computation
materialize_advection(u::OnlySelfUpwinding, grid) = OnlySelfUpwinding(materialize_advection(u.cross_scheme, grid),
                                                                      u.δU_stencil, u.δV_stencil, u.δu²_stencil, u.δv²_stencil)

materialize_advection(u::CrossAndSelfUpwinding, grid) = CrossAndSelfUpwinding(materialize_advection(u.cross_scheme, grid),
                                                                              u.divergence_stencil, u.δu²_stencil, u.δv²_stencil)

materialize_advection(u::VelocityUpwinding, grid) = VelocityUpwinding(materialize_advection(u.cross_scheme, grid))

# VectorInvariant wraps multiple sub-schemes; recurse into each
function materialize_advection(vi::VectorInvariant{N, FT, TD, <:Any, <:Any, <:Any, <:Any, <:Any, <:Any, M}, grid) where {N, FT, TD, M}
    return VectorInvariant{N, FT, TD, M}(materialize_advection(vi.vorticity_scheme, grid),
                                         vi.vorticity_stencil,
                                         materialize_advection(vi.vertical_advection_scheme, grid),
                                         materialize_advection(vi.kinetic_energy_gradient_scheme, grid),
                                         materialize_advection(vi.divergence_scheme, grid),
                                         materialize_advection(vi.upwinding, grid))
end

materialize_advection(weno::WENO{N, FT, WCT}, grid) where {N, FT, WCT} =
    WENO{N, FT, WCT}(weno.bounds,
                     materialize_advection(weno.buffer_scheme, grid),
                     materialize_advection(weno.advecting_velocity_scheme, grid),
                     weno.time_discretization)

function materialize_advection(weno::WENO{N, FT, Nothing}, grid) where {N, FT}
    WTC = default_weno_weight_computation(architecture(grid))
    return WENO{N, FT, WTC}(weno.bounds,
                            materialize_advection(weno.buffer_scheme, grid),
                            materialize_advection(weno.advecting_velocity_scheme, grid),
                            weno.time_discretization)
end

materialize_advection(scheme::UpwindBiased{N, FT}, grid) where {N, FT} =
    UpwindBiased{N, FT}(materialize_advection(scheme.buffer_scheme, grid),
                        materialize_advection(scheme.advecting_velocity_scheme, grid),
                        scheme.time_discretization)

materialize_advection(scheme::Centered{N,FT}, grid) where {N,FT} =
    Centered{N, FT}(materialize_advection(scheme.buffer_scheme, grid), scheme.time_discretization)
