#ifndef _CHNSE_H_
#define _CHNSE_H_

#include <deal.II/base/conditional_ostream.h>

#include "FileReader.h"
#include "CahnHilliard.h"
#include "NSE.h"

/**
 * This class wraps together the CahnHilliard problem and the ones related to Navier Stokes,
 * that is Velocity and Pressure.
 *
 * The purpose of doing this is to be able to implement an inner fixed point iteration between
 * these problems. This allows for bigger time steps.
 */
template<int dim> class CHNSE{
  public:
    /**
     * Initialize the subproblems.
     * @param tria : The triangulation where the problems are defined.
     * @param data : The collection of problem data needed to properly initialize each one
     *    of the subproblems
     * @param costream : The conditional ostream used to send convergence information to.
     */
    CHNSE( const Triangulation<dim> &tria, const DataStorage<dim> &data, ConditionalOStream &costream );
    /**
     * Clear data.
     */
    ~CHNSE();
    /**
     * Sets the time step in each one of the subproblems.
     */
    void set_dt( const double _dt );
    /**
     * Calls Problem::init on each one of the subproblems.
     */
    void init();
    /**
     * Solve, via an inner fixed point iteration, the Cahn Hilliard Navier Stokes system.
     * @param ph_params : The collection of AsFunction objects that CahnHilliard needs.
     * @param v_params : The collection of AsFunction objects that Velocity needs.
     * @param p_params : The collection of AsFunction objects that Pressure needs.
     * @param time : The current time.
     */
    void solve( std::vector< AsFunction<dim> *> &ph_params, std::vector< AsFunction<dim> *> v_params,
                std::vector< AsFunction<dim> *> &p_params, const double time );
    /**
     * Compute the total size of this problem. This is just
     * CahnHilliard.size + Velocity.size + Pressure.size
     */
    unsigned size() const;
  protected:
    Material_Parameters params; ///< Material Parameters.
    CahnHilliard<dim> ch; ///< The CahnHilliard subproblem.
    Velocity<dim> vel; ///< The Velocity subproblem.
    Pressure<dim> pres; ///< The pressure subproblem.
    ConditionalOStream &v_cout; ///< A reference to the conditional ostream for output
  public:
    AsFunction<dim> &get_phase, ///< Phase field. Part of CahnHilliard.
                    &get_potential, ///< Chemical potential. Part of CahnHilliard.
                    &get_old_phase, ///< Old phase field. Part of CahnHilliard.
                    &get_velocity, ///< Velocity.
                    &get_pressure, ///< Pressure.
                    &get_extrapolated_pressure; ///< $ 2p^k - p^{k-1} $. Part of Pressure.
    /**
     * Get the finite element space of CahnHilliard.
     */
    const hp::FECollection<dim> &get_ph_mu_fe() const { return ch.get_fe(); }
    /**
     * Get the finite element space of Velocity.
     */
    const hp::FECollection<dim> &get_vel_fe() const { return vel.get_fe(); }
    /**
     * Get the finite element space of Pressure.
     */
    const hp::FECollection<dim> &get_pres_fe() const { return pres.get_fe(); }
};

#endif //_CHNSE_H_