using Oceananigans.Advection
using Oceananigans.ImmersedBoundaries: ImmersedBoundaryGrid
using Oceananigans.Coriolis
using Oceananigans.Operators
using Oceananigans.TurbulenceClosures: ∂ⱼ_τ₁ⱼ, ∂ⱼ_τ₂ⱼ

@inline half_g_h²(i, j, k, grid, h, g)  = @inbounds 1/2 * g * h[i, j, k]^2
@inline h_plus_hB(i, j, k, grid, h, hB) = @inbounds h[i, j, k] + hB[i, j, k]

@inline x_pressure_gradient(i, j, k, grid, g, h, hB, formulation) = ∂xᵣᶠᶜᶜ(i, j, k, grid, half_g_h², h, g)
@inline y_pressure_gradient(i, j, k, grid, g, h, hB, formulation) = ∂yᵣᶜᶠᶜ(i, j, k, grid, half_g_h², h, g)

@inline x_pressure_gradient(i, j, k, grid, g, h, hB, ::VectorInvariantFormulation) = g * ∂xᵣᶠᶜᶜ(i, j, k, grid, h_plus_hB, h, hB)
@inline y_pressure_gradient(i, j, k, grid, g, h, hB, ::VectorInvariantFormulation) = g * ∂yᵣᶜᶠᶜ(i, j, k, grid, h_plus_hB, h, hB)

@inline bathymetry_contribution_x(i, j, k, grid, g, h, hB, formulation) = g * h[i, j, k] * ∂xᵣᶠᶜᶜ(i, j, k, grid, hB)
@inline bathymetry_contribution_y(i, j, k, grid, g, h, hB, formulation) = g * h[i, j, k] * ∂yᵣᶜᶠᶜ(i, j, k, grid, hB)

@inline bathymetry_contribution_x(i, j, k, grid, g, h, hB, ::VectorInvariantFormulation) = zero(grid)
@inline bathymetry_contribution_y(i, j, k, grid, g, h, hB, ::VectorInvariantFormulation) = zero(grid)

"""
Compute the tendency for the x-directional transport, uh
"""
@inline function uh_solution_tendency(i, j, k, grid,
                                      gravitational_acceleration,
                                      advection,
                                      velocities,
                                      coriolis,
                                      closure,
                                      bathymetry,
                                      solution,
                                      tracers,
                                      closure_fields,
                                      clock,
                                      formulation,
                                      forcing)

    g = gravitational_acceleration

    model_fields = shallow_water_fields(velocities, tracers, solution, formulation)

    return ( - div_mom_u(i, j, k, grid, advection, solution, formulation)
             - x_pressure_gradient(i, j, k, grid, g, solution.h, bathymetry, formulation)
             - x_f_cross_U(i, j, k, grid, coriolis, solution)
             - bathymetry_contribution_x(i, j, k, grid, g, solution.h, bathymetry, formulation)
             - sw_∂ⱼ_τ₁ⱼ(i, j, k, grid, closure, closure_fields, clock, model_fields, formulation)
             + forcing(i, j, k, grid, clock, merge(solution, tracers)))
end

"""
Compute the tendency for the y-directional transport, vh.
"""
@inline function vh_solution_tendency(i, j, k, grid,
                                      gravitational_acceleration,
                                      advection,
                                      velocities,
                                      coriolis,
                                      closure,
                                      bathymetry,
                                      solution,
                                      tracers,
                                      closure_fields,
                                      clock,
                                      formulation,
                                      forcing)

     g = gravitational_acceleration

     model_fields = shallow_water_fields(velocities, tracers, solution, formulation)

    return ( - div_mom_v(i, j, k, grid, advection, solution, formulation)
             - y_pressure_gradient(i, j, k, grid, g, solution.h, bathymetry, formulation)
             - y_f_cross_U(i, j, k, grid, coriolis, solution)
             - bathymetry_contribution_y(i, j, k, grid, g, solution.h, bathymetry, formulation)
             - sw_∂ⱼ_τ₂ⱼ(i, j, k, grid, closure, closure_fields, clock, model_fields, formulation)
             + forcing(i, j, k, grid, clock, merge(solution, tracers)))
end

"""
Compute the tendency for the height, h.
"""
@inline function h_solution_tendency(i, j, k, grid,
                                     gravitational_acceleration,
                                     advection,
                                     coriolis,
                                     closure,
                                     solution,
                                     tracers,
                                     closure_fields,
                                     clock,
                                     formulation,
                                     forcing)

    return ( - div_Uh(i, j, k, grid, advection, solution, formulation)
             + forcing(i, j, k, grid, clock, merge(solution, tracers)))
end

@inline function tracer_tendency(i, j, k, grid,
                                 val_tracer_index::Val{tracer_index},
                                 advection,
                                 closure,
                                 solution,
                                 tracers,
                                 closure_fields,
                                 clock,
                                 formulation,
                                 forcing) where tracer_index

    @inbounds c = tracers[tracer_index]

    return ( - div_Uc(i, j, k, grid, advection, solution, c, formulation)
             + c_div_U(i, j, k, grid, solution, c, formulation)
             + forcing(i, j, k, grid, clock, merge(solution, tracers))
            )
end
