// ============================================================================
// ==                                                                        ==
// == AMDiS - Adaptive multidimensional simulations                          ==
// ==                                                                        ==
// ============================================================================
// ==                                                                        ==
// ==  crystal growth group                                                  ==
// ==                                                                        ==
// ==  Stiftung caesar                                                       ==
// ==  Ludwig-Erhard-Allee 2                                                 ==
// ==  53175 Bonn                                                            ==
// ==  germany                                                               ==
// ==                                                                        ==
// ============================================================================
// ==                                                                        ==
// ==  http://www.caesar.de/cg/AMDiS                                         ==
// ==                                                                        ==
// ============================================================================

/** \file ProblemScal.h */

#ifndef AMDIS_PROBLEMSCAL_H
#define AMDIS_PROBLEMSCAL_H

#include "Global.h"
#include "ProblemStatBase.h"
#include "MemoryManager.h"
#include "AbstractFunction.h"
#include "FixVec.h"
#include "Boundary.h"
#include "StandardProblemIteration.h"
#include <list>

namespace AMDiS {

  class Operator;
  class DOFMatrix;
  class FiniteElemSpace;
  class Estimator;
  template<typename T> class OEMSolver;
  template<typename T> class Preconditioner;
  template<typename T> class MatVecMultiplier;
  template<typename T> class DOFVector;
  class Marker;
  class AdaptInfo;
  class ElInfo;
  class FileWriterInterface;
  class RefinementManager;
  class CoarseningManager;

  class ProblemScal : public ProblemStatBase,
		      public StandardProblemIteration
  {
  public:
    MEMORY_MANAGED(ProblemScal);

    ProblemScal(const char *name, 
		ProblemIterationInterface *problemIteration = NULL)
      : StandardProblemIteration(this) ,
	name_(name),
	feSpace_(NULL),
	mesh_(NULL),
	marker_(NULL),
	estimator_(NULL),
	solver_(NULL),
	solution_(NULL),
	rhs_(NULL),
	systemMatrix_(NULL),
	matVec_(NULL),
	leftPrecon_(NULL),
	rightPrecon_(NULL),
	useGetBound_(true),
	refinementManager_(NULL),
	coarseningManager_(NULL),
	info_(10)
    {
    };

    /** \brief
     * Destructor
     */
    virtual ~ProblemScal() {
      FUNCNAME("ProblemScal::~ProblemScal()");
    };

    /** \brief
     * Initialisation of the problem.
     */
    virtual void initialize(Flag initFlag,
			    ProblemScal *adoptProblem = NULL,
			    Flag adoptFlag = INIT_NOTHING);

    /** \brief
     * Used in \ref initialize().
     */
    virtual void createMesh();

    /** \brief
     * Used in \ref initialize().
     */
    virtual void createFESpace();

    /** \brief
     * Used in \ref initialize().
     */
    virtual void createMatricesAndVectors();

    /** \brief
     * Used in \ref initialize().
     */
    virtual void createSolver();

    /** \brief
     * Used in \ref initialize().
     */
    virtual void createEstimator();

    /** \brief
     * Used in \ref initialize().
     */
    virtual void createMarker();

    /** \brief
     * Used in \ref initialize().
     */
    virtual void createFileWriter();

    /** \brief
     * Implementation of ProblemStatBase::solve(). Deligates the solving
     * of problems system to \ref solver.
     */
    virtual void solve(AdaptInfo *adaptInfo);

    /** \brief
     * Implementation of ProblemStatBase::estimate(). Deligates the estimation
     * to \ref estimator.
     */
    virtual void estimate(AdaptInfo *adaptInfo);

    /** \brief
     * Implementation of ProblemStatBase::markElements().
     * Deligated to \ref adapt.
     */
    virtual Flag markElements(AdaptInfo *adaptInfo);

    /** \brief
     * Implementation of ProblemStatBase::refineMesh(). Deligated to the
     * RefinementManager of \ref adapt.
     */
    virtual Flag refineMesh(AdaptInfo *adaptInfo);

    /** \brief
     * Implementation of ProblemStatBase::coarsenMesh(). Deligated to the
     * CoarseningManager of \ref adapt.
     */
    virtual Flag coarsenMesh(AdaptInfo *adaptInfo);

    /** \brief
     * Implementation of ProblemStatBase::buildBeforeRefine().
     * Does nothing here.
     */
    virtual void buildBeforeRefine(AdaptInfo *adaptInfo, Flag) {};

    /** \brief
     * Implementation of ProblemStatBase::buildBeforeCoarsen().
     * Does nothing here.
     */
    virtual void buildBeforeCoarsen(AdaptInfo *adaptInfo, Flag) {};

    /** \brief
     * Implementation of ProblemStatBase::buildAfterCoarsen().
     * Assembles \ref A and \ref rhs.
     */
    virtual void buildAfterCoarsen(AdaptInfo *adaptInfo, Flag flag);

    /** \brief
     * Returns number of managed problems
     */
    virtual int getNumProblems() { return 1; };

    /** \brief
     * Implementation of ProblemStatBase::getNumComponents()
     */
    virtual int getNumComponents() { return 1; }; 

    /** \brief
     * Returns the problem with the given number. If only one problem
     * is managed by this master problem, the number hasn't to be given.
     */
    virtual ProblemStatBase *getProblem(int number = 0) { return this; };

    /** \brief
     * Writes output files.
     */
    void writeFiles(AdaptInfo *adaptInfo, bool force);

    /** \brief
     * Interpolates fct to \ref solution.
     */
    void 
    interpolInitialSolution(AbstractFunction<double, WorldVector<double> > *fct);

    /** \brief
     * Adds an Operator to \ref systemMatrix_.
     */
    void addMatrixOperator(Operator *op, 
			   double *factor = NULL,
			   double *estFactor = NULL);

    /** \brief
     * Adds an Operator to \ref rhs_.
     */
    void addVectorOperator(Operator *op, 
			   double *factor = NULL,
			   double *estFactor = NULL);

    /** \brief
     * Adds dirichlet boundary conditions.
     */
    virtual void addDirichletBC(BoundaryType type, 
				AbstractFunction<double, WorldVector<double> >* b);

    /** \brief
     * Adds dirichlet boundary conditions.
     */
    virtual void addDirichletBC(BoundaryType type, 
				DOFVector<double> *vec);

    /** \brief
     * Adds robin boundary conditions.
     */
    virtual void addRobinBC(BoundaryType type, 
			    AbstractFunction<double, WorldVector<double> > *n,
			    AbstractFunction<double, WorldVector<double> > *r);

    /** \brief
     * Adds robin boundary conditions.
     */
    virtual void addRobinBC(BoundaryType type, 
			    DOFVector<double> *n,
			    DOFVector<double> *r);

    /** \brief
     * Adds Neumann boundary conditions.
     */
    virtual void addNeumannBC(BoundaryType type, 
			      AbstractFunction<double, WorldVector<double> > *n);


    virtual void addPeriodicBC(BoundaryType type);

    // ===== getting-methods ======================================================

    /** \name getting methods
     * \{ 
     */

    /** \brief
     * Returns \ref solution_.
     */
    inline DOFVector<double>* getSolution() { 
      return solution_; 
    };

    /** \brief
     * Returns \ref rhs_.
     */
    inline DOFVector<double>* getRHS() { 
      return rhs_; 
    };

    /** \brief
     * Returns \ref systemMatrix_.
     */
    inline DOFMatrix *getSystemMatrix() { 
      return systemMatrix_; 
    };

    /** \brief
     * Returns \ref mesh_
     */
    inline Mesh* getMesh() { 
      return mesh_; 
    };

    /** \brief
     * Returns \ref feSpace_.
     */
    inline FiniteElemSpace* getFESpace() { 
      return feSpace_; 
    };

    /** \brief
     * Returns \ref estimator_.
     */
    inline Estimator* getEstimator() { 
      return estimator_; 
    };

    /** \brief
     * Returns \ref refinementManager_.
     */
    inline RefinementManager* getRefinementManager() { 
      return refinementManager_; 
    };

    /** \brief
     * Returns \ref solver_.
     */
    inline OEMSolver<DOFVector<double> >* getSolver() { 
      return solver_; 
    };

    /** \brief
     * Returns \ref marker_.
     */
    inline Marker *getMarker() { 
      return marker_; 
    };

    /** \brief
     * Returns \ref name_.
     */
    virtual inline const ::std::string& getName() { 
      return name_; 
    };

    /** \brief
     * Returns \ref useGetBound_.
     */
    inline bool getBoundUsed() { 
      return useGetBound_; 
    };

    /** \brief
     * Returns \ref leftPrecon_.
     */
    inline Preconditioner<DOFVector<double> > *getLeftPrecon() { 
      return leftPrecon_; 
    };

    /** \brief
     * Returns \ref rightPrecon_.
     */
    inline Preconditioner<DOFVector<double> > *getRightPrecon() { 
      return rightPrecon_; 
    };

    inline CoarseningManager *getCoarseningManager() {
      return coarseningManager_;
    };

    /** \} */

    // ===== setting-methods ======================================================

    /** \name setting methods
     * \{ 
     */

    /** \brief
     * Sets \ref feSpace_.
     */
    inline void setFESpace(FiniteElemSpace* fe) { 
      feSpace_ = fe; 
    };

    /** \brief
     * Sets \ref estimator_.
     */
    inline void setEstimator(Estimator* est) { 
      estimator_ = est; 
    };

    /** \brief
     * Sets \ref solver_.
     */
    inline void setSolver(OEMSolver<DOFVector<double> >* sol) { solver_ = sol; };

    /** \brief
     * Sets \ref leftPrecon_.
     */
    inline void setLeftPrecon(Preconditioner<DOFVector<double> > *p) {
      leftPrecon_ = p;
    };

    /** \brief
     * Sets \ref rightPrecon_.
     */
    inline void setRightPrecon(Preconditioner<DOFVector<double> > *p) {
      rightPrecon_ = p;
    };


    inline void setMarker(Marker *marker) {
      marker_ = marker;
    };

    /** \} */

    // ===== Serializable implementation =====
  
    /** \brief
     * serialization
     */
    virtual void serialize(::std::ostream &out);

    /** \brief
     * deserialization
     */
    virtual void deserialize(::std::istream &in);

    ::std::list<FileWriterInterface*> getFileWriterList() {
      return fileWriters_;
    };

  protected:
    /** \brief
     * Used by \ref buildAfterCoarsen().
     */
    static int buildAfterCoarsenFct(ElInfo *elInfo);

  protected:
    /** \brief
     * Name of this problem.
     */
    ::std::string name_;

    /** \brief
     * FiniteElemSpace of this problem.
     */
    FiniteElemSpace *feSpace_;

    /** \brief
     * Mesh of this problem.
     */
    Mesh *mesh_;

    /** \brief
     * Responsible for element marking.
     */
    Marker *marker_;

    /** \brief
     * Estimator of this problem. Used in \ref estimate().
     */
    Estimator *estimator_;

    /** \brief
     * Linear solver of this problem. Used in \ref solve().
     */
    OEMSolver<DOFVector<double> > *solver_;

    /** \brief
     * DOFVector storing the calculated solution of the problem.
     */
    DOFVector<double> *solution_;

    /** \brief
     * DOFVector for the right hand side 
     */
    DOFVector<double> *rhs_;

    /** \brief
     * System matrix
     */
    DOFMatrix *systemMatrix_;

    /** \brief
     * Matrix-vector multiplication
     */
    MatVecMultiplier<DOFVector<double> > *matVec_;

    /** \brief
     * Left preconditioner. Used in \ref solver.
     */
    Preconditioner<DOFVector<double> > *leftPrecon_;

    /** \brief
     * Right preconditioner. Used in \ref solver.
     */
    Preconditioner<DOFVector<double> > *rightPrecon_;

    /** \brief
     * Determines whether domain boundaries should be considered at assembling.
     */
    bool useGetBound_;

    /** \brief
     * Writes the meshes and solution after the adaption loop.
     */
    ::std::list<FileWriterInterface*> fileWriters_;

    /** \brief
     * All actions of mesh refinement are performed by refinementManager.
     * If new refinement algorithms should be realized, one has to override
     * RefinementManager and give one instance of it to AdaptStationary.
     */
    RefinementManager *refinementManager_;

    /** \brief
     * All actions of mesh coarsening are performed by coarseningManager.
     * If new coarsening algorithms should be realized, one has to override
     * CoarseningManager and give one instance of it to AdaptStationary.
     */
    CoarseningManager *coarseningManager_;
  
    /** \brief
     * Info level.
     */
    int info_;

    /** \brief
     * Used for mesh traversal.
     */
    static ProblemScal *traversePtr_;
  };

}

#endif