#include <language/modules/CompositeSchemeModule.hpp>

#include <language/modules/MeshModuleTypes.hpp>
#include <language/modules/SchemeModuleTypes.hpp>
#include <language/utils/BuiltinFunctionEmbedder.hpp>
#include <language/utils/TypeDescriptor.hpp>
#include <scheme/DiscreteFunctionVariant.hpp>
#include <scheme/IBoundaryConditionDescriptor.hpp>

#include <language/modules/ModuleRepository.hpp>

#include <scheme/AcousticCompositeSolver.hpp>
#include <scheme/RoeViscousFormEulerianCompositeSolver_v2.hpp>
#include <scheme/RoeViscousFormEulerianCompositeSolver_v2_o2.hpp>
#include <scheme/RusanovEulerianCompositeSolver.hpp>
#include <scheme/RusanovEulerianCompositeSolver_o2.hpp>
#include <scheme/RusanovEulerianCompositeSolver_v2.hpp>
#include <scheme/RusanovEulerianCompositeSolver_v2_o2.hpp>
#include <scheme/RusanovEulerianCompositeSolver_v2_order_n.hpp>

CompositeSchemeModule::CompositeSchemeModule()
{
  this->_addBuiltinFunction("composite_glace_fluxes",
                            std::function(

                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list)
                                -> std::tuple<std::shared_ptr<const ItemValueVariant>,
                                              std::shared_ptr<const SubItemValuePerItemVariant>,
                                              std::shared_ptr<const ItemValueVariant>,
                                              std::shared_ptr<const SubItemValuePerItemVariant>,
                                              std::shared_ptr<const ItemValueVariant>,
                                              std::shared_ptr<const SubItemValuePerItemVariant>> {
                                return AcousticCompositeSolverHandler{getCommonMesh({rho, c, u, p})}
                                  .solver()
                                  .compute_fluxes(AcousticCompositeSolverHandler::SolverType::GlaceComposite, rho, c, u,
                                                  p, bc_descriptor_list);
                              }

                              ));

  this->_addBuiltinFunction("composite_glace_solver",
                            std::function(

                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& E,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list,
                                 const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>> {
                                return AcousticCompositeSolverHandler{getCommonMesh({rho, u, E, c, p})}
                                  .solver()
                                  .apply(AcousticCompositeSolverHandler::SolverType::GlaceComposite, dt, rho, u, E, c,
                                         p, bc_descriptor_list);
                              }

                              ));

  this->_addBuiltinFunction("rusanov_eulerian_composite_solver_version1",
                            std::function(

                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& E, const double& gamma,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p,   // const size_t& degree,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list,
                                 const double& dt) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>> {
                                return rusanovEulerianCompositeSolver(rho, u, E, gamma, c, p,   // degree,
                                                                      bc_descriptor_list, dt);
                              }

                              ));

  this->_addBuiltinFunction("rusanov_eulerian_composite_solver_version1_with_checks",
                            std::function(

                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& E, const double& gamma,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p,   // const size_t& degree,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list,
                                 const double& dt) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>> {
                                return rusanovEulerianCompositeSolver(rho, u, E, gamma, c, p,   // degree,
                                                                      bc_descriptor_list, dt, true);
                              }

                              ));

  this->_addBuiltinFunction("rusanov_eulerian_composite_solver_version1_o2",
                            std::function(

                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& E, const double& gamma,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p,   // const size_t& degree,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list,
                                 const double& dt) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>> {
                                return rusanovEulerianCompositeSolver_o2(rho, u, E, gamma, c, p,   // degree,
                                                                         bc_descriptor_list, dt);
                              }

                              ));

  this->_addBuiltinFunction("rusanov_eulerian_composite_solver_version1_o2_with_checks",
                            std::function(

                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& E, const double& gamma,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p,   // const size_t& degree,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list,
                                 const double& dt) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>> {
                                return rusanovEulerianCompositeSolver_o2(rho, u, E, gamma, c, p,   // degree,
                                                                         bc_descriptor_list, dt, true);
                              }

                              ));

  this->_addBuiltinFunction("rusanov_eulerian_composite_solver_version2",
                            std::function(
                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& E, const double& gamma,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p,   // const size_t& degree,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list,
                                 const double& dt) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>> {
                                return rusanovEulerianCompositeSolver_v2(rho, u, E, gamma, c, p,   // degree,
                                                                         bc_descriptor_list, dt);
                              }

                              ));

  this->_addBuiltinFunction("rusanov_eulerian_composite_solver_version2_with_checks",
                            std::function(
                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& E, const double& gamma,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p,   // const size_t& degree,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list,
                                 const double& dt) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>> {
                                return rusanovEulerianCompositeSolver_v2(rho, u, E, gamma, c, p,   // degree,
                                                                         bc_descriptor_list, dt, true);
                              }));
  this->_addBuiltinFunction("roe_viscosityform_eulerian_composite_solver_version2",
                            std::function(
                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& E, const double& gamma,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p,   // const size_t& degree,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list,
                                 const double& dt) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>> {
                                return roeViscousFormEulerianCompositeSolver_v2(rho, u, E, gamma, c, p,   // degree,
                                                                                bc_descriptor_list, dt);
                              }

                              ));

  this->_addBuiltinFunction("roe_viscosityform_eulerian_composite_solver_version2_with_checks",
                            std::function(
                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& E, const double& gamma,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p,   // const size_t& degree,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list,
                                 const double& dt) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>> {
                                return roeViscousFormEulerianCompositeSolver_v2(rho, u, E, gamma, c, p,   // degree,
                                                                                bc_descriptor_list, dt, true);
                              }

                              ));

  this->_addBuiltinFunction("rusanov_eulerian_composite_solver_version2_o2",
                            std::function(
                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& E, const double& gamma,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p,   // const size_t& degree,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list,
                                 const double& dt) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>> {
                                return rusanovEulerianCompositeSolver_v2_o2(rho, u, E, gamma, c, p,   // degree,
                                                                            bc_descriptor_list, dt);
                              }

                              ));

  this->_addBuiltinFunction("rusanov_eulerian_composite_solver_version2_order_n",
                            std::function(
                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& E, const double& gamma,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p, const size_t& degree,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list,
                                 const double& dt) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>> {
                                return rusanovEulerianCompositeSolver_v2_order_n(rho, u, E, gamma, c, p, degree,
                                                                                 bc_descriptor_list, dt);
                              }

                              ));

  this->_addBuiltinFunction("rusanov_eulerian_composite_solver_version2_o2_with_checks",
                            std::function(
                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& E, const double& gamma,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p,   // const size_t& degree,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list,
                                 const double& dt) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>> {
                                return rusanovEulerianCompositeSolver_v2_o2(rho, u, E, gamma, c, p,   // degree,
                                                                            bc_descriptor_list, dt, true);
                              }));

  this->_addBuiltinFunction("roe_viscosityform_eulerian_composite_solver_version2_o2",
                            std::function(
                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& E, const double& gamma,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p,   // const size_t& degree,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list,
                                 const double& dt) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>> {
                                return roeViscousFormEulerianCompositeSolver_v2_o2(rho, u, E, gamma, c, p,   // degree,
                                                                                   bc_descriptor_list, dt);
                              }

                              ));

  this->_addBuiltinFunction("roe_viscosityform_eulerian_composite_solver_version2_o2_with_checks",
                            std::function(
                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& E, const double& gamma,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p,   // const size_t& degree,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list,
                                 const double& dt) -> std::tuple<std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>> {
                                return roeViscousFormEulerianCompositeSolver_v2_o2(rho, u, E, gamma, c, p,   // degree,
                                                                                   bc_descriptor_list, dt, true);
                              }

                              ));

  this->_addBuiltinFunction("composite_eucclhyd_fluxes",
                            std::function(

                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list)
                                -> std::tuple<std::shared_ptr<const ItemValueVariant>,
                                              std::shared_ptr<const SubItemValuePerItemVariant>,
                                              std::shared_ptr<const ItemValueVariant>,
                                              std::shared_ptr<const SubItemValuePerItemVariant>,
                                              std::shared_ptr<const ItemValueVariant>,
                                              std::shared_ptr<const SubItemValuePerItemVariant>> {
                                return AcousticCompositeSolverHandler{getCommonMesh({rho, c, u, p})}
                                  .solver()
                                  .compute_fluxes(AcousticCompositeSolverHandler::SolverType::EucclhydComposite, rho, c,
                                                  u, p, bc_descriptor_list);
                              }

                              ));

  this->_addBuiltinFunction("composite_eucclhyd_solver",
                            std::function(

                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& E,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& c,
                                 const std::shared_ptr<const DiscreteFunctionVariant>& p,
                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                   bc_descriptor_list,
                                 const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>> {
                                return AcousticCompositeSolverHandler{getCommonMesh({rho, u, E, c, p})}
                                  .solver()
                                  .apply(AcousticCompositeSolverHandler::SolverType::EucclhydComposite, dt, rho, u, E,
                                         c, p, bc_descriptor_list);
                              }

                              ));

  this->_addBuiltinFunction("apply_acoustic_composite_fluxes",
                            std::function(

                              [](const std::shared_ptr<const DiscreteFunctionVariant>& rho,      //
                                 const std::shared_ptr<const DiscreteFunctionVariant>& u,        //
                                 const std::shared_ptr<const DiscreteFunctionVariant>& E,        //
                                 const std::shared_ptr<const ItemValueVariant>& ur,              //
                                 const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr,   //
                                 const std::shared_ptr<const ItemValueVariant>& ue,              //
                                 const std::shared_ptr<const SubItemValuePerItemVariant>& Fje,   //
                                 const std::shared_ptr<const ItemValueVariant>& uf,              //
                                 const std::shared_ptr<const SubItemValuePerItemVariant>& Fjf,   //
                                 const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>,
                                                                 std::shared_ptr<const DiscreteFunctionVariant>> {
                                return AcousticCompositeSolverHandler{getCommonMesh({rho, u, E})}   //
                                  .solver()
                                  .apply_fluxes(dt, rho, u, E, ur, Fjr, ue, Fje, uf, Fjf);
                              }

                              ));

  this->_addBuiltinFunction("compute_dt", std::function(

                                            [](const std::shared_ptr<const DiscreteFunctionVariant>& u,
                                               const std::shared_ptr<const DiscreteFunctionVariant>& c) -> double {
                                              return toolsCompositeSolver::compute_dt(u, c);
                                            }));
}

void
CompositeSchemeModule::registerOperators() const
{}

void
CompositeSchemeModule::registerCheckpointResume() const
{}

ModuleRepository::Subscribe<CompositeSchemeModule> composite_scheme_module;