#ifndef ACOUSTIC_SOLVER_HPP
#define ACOUSTIC_SOLVER_HPP

#include <memory>
#include <tuple>
#include <vector>

class IDiscreteFunction;
class IBoundaryConditionDescriptor;
class IMesh;
class ItemValueVariant;
class SubItemValuePerItemVariant;

double acoustic_dt(const std::shared_ptr<const IDiscreteFunction>& c);

double acoustic_rho_dt(const std::shared_ptr<const IDiscreteFunction>& rho,
                       const std::shared_ptr<const ItemValueVariant>& ur,
                       const double& dt_max);

double acoustic_pg_epsilon_dt(const std::shared_ptr<const IDiscreteFunction>& rho,
                              const std::shared_ptr<const IDiscreteFunction>& u,
                              const std::shared_ptr<const IDiscreteFunction>& E,
                              const std::shared_ptr<const IDiscreteFunction>& p,
                              const std::shared_ptr<const SubItemValuePerItemVariant>& Ajr_variant,
                              const std::shared_ptr<const ItemValueVariant>& ur_variant,
                              const double& dt_max);

double acoustic_pg_epsilon_dt(const std::shared_ptr<const IDiscreteFunction>& rho,
                              const std::shared_ptr<const IDiscreteFunction>& u,
                              const std::shared_ptr<const IDiscreteFunction>& E,
                              const std::shared_ptr<const IDiscreteFunction>& p,
                              const std::shared_ptr<const SubItemValuePerItemVariant>& Ajr_variant,
                              const std::shared_ptr<const ItemValueVariant>& ur_variant,
                              const double& dt_max);

double acoustic_pg_entropy_dt(const std::shared_ptr<const IDiscreteFunction>& rho,
                              const std::shared_ptr<const IDiscreteFunction>& u,
                              const std::shared_ptr<const IDiscreteFunction>& epsilon,
                              const std::shared_ptr<const IDiscreteFunction>& p,
                              const std::shared_ptr<const IDiscreteFunction>& gamma,
                              const std::shared_ptr<const SubItemValuePerItemVariant>& Ajr_variant,
                              const std::shared_ptr<const ItemValueVariant>& ur_variant,
                              const double& dt_max);

double acoustic_mg_epsilon_dt(const std::shared_ptr<const IDiscreteFunction>& rho,
                              const std::shared_ptr<const IDiscreteFunction>& u,
                              const std::shared_ptr<const IDiscreteFunction>& epsilon,
                              const std::shared_ptr<const IDiscreteFunction>& p,
                              const std::shared_ptr<const IDiscreteFunction>& p_k,
                              const std::shared_ptr<const IDiscreteFunction>& rho0k0_expN0p1,
                              const std::shared_ptr<const SubItemValuePerItemVariant>& Ajr_variant,
                              const std::shared_ptr<const ItemValueVariant>& ur_variant,
                              const double& dt_max);

double acoustic_mg_entropy_dt(const std::shared_ptr<const IDiscreteFunction>& rho,
                              const std::shared_ptr<const IDiscreteFunction>& u,
                              const std::shared_ptr<const IDiscreteFunction>& epsilon,
                              const std::shared_ptr<const IDiscreteFunction>& p,
                              const std::shared_ptr<const IDiscreteFunction>& p_k,
                              const std::shared_ptr<const IDiscreteFunction>& Gamma_tau,
                              const std::shared_ptr<const IDiscreteFunction>& Gamma_inf,
                              const std::shared_ptr<const SubItemValuePerItemVariant>& Ajr_variant,
                              const std::shared_ptr<const ItemValueVariant>& ur_variant,
                              const double& dt_max);

class AcousticSolverHandler
{
 public:
  enum class SolverType
  {
    Glace,
    Eucclhyd
  };

 private:
  struct IAcousticSolver
  {
    virtual std::tuple<const std::shared_ptr<const SubItemValuePerItemVariant>,
                       const std::shared_ptr<const ItemValueVariant>,
                       const std::shared_ptr<const SubItemValuePerItemVariant>>
    compute_fluxes(
      const SolverType& solver_type,
      const std::shared_ptr<const IDiscreteFunction>& rho,
      const std::shared_ptr<const IDiscreteFunction>& c,
      const std::shared_ptr<const IDiscreteFunction>& u,
      const std::shared_ptr<const IDiscreteFunction>& p,
      const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const = 0;

    virtual std::tuple<std::shared_ptr<const IMesh>,
                       std::shared_ptr<const IDiscreteFunction>,
                       std::shared_ptr<const IDiscreteFunction>,
                       std::shared_ptr<const IDiscreteFunction>>
    apply_fluxes(const double& dt,
                 const std::shared_ptr<const IDiscreteFunction>& rho,
                 const std::shared_ptr<const IDiscreteFunction>& u,
                 const std::shared_ptr<const IDiscreteFunction>& E,
                 const std::shared_ptr<const ItemValueVariant>& ur,
                 const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr) const = 0;

    virtual std::tuple<std::shared_ptr<const IMesh>,
                       std::shared_ptr<const IDiscreteFunction>,
                       std::shared_ptr<const IDiscreteFunction>,
                       std::shared_ptr<const IDiscreteFunction>>
    apply(const SolverType& solver_type,
          const double& dt,
          const std::shared_ptr<const IDiscreteFunction>& rho,
          const std::shared_ptr<const IDiscreteFunction>& u,
          const std::shared_ptr<const IDiscreteFunction>& E,
          const std::shared_ptr<const IDiscreteFunction>& c,
          const std::shared_ptr<const IDiscreteFunction>& p,
          const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const = 0;

    IAcousticSolver()                  = default;
    IAcousticSolver(IAcousticSolver&&) = default;
    IAcousticSolver& operator=(IAcousticSolver&&) = default;

    virtual ~IAcousticSolver() = default;
  };

  template <size_t Dimension>
  class AcousticSolver;

  std::unique_ptr<IAcousticSolver> m_acoustic_solver;

 public:
  const IAcousticSolver&
  solver() const
  {
    return *m_acoustic_solver;
  }

  AcousticSolverHandler(const std::shared_ptr<const IMesh>& mesh);
};

#endif   // ACOUSTIC_SOLVER_HPP