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

/** \file DOFAdmin.h */

/** \defgroup DOFAdministration DOF adaministration module
 * @{ <img src="dof.png"> @}
 * \brief
 * Contains all classes used for the DOF administration.
 */

#ifndef AMDIS_DOFADMIN_H
#define AMDIS_DOFADMIN_H

// ============================================================================
// ===== includes =============================================================
// ============================================================================

#include "Global.h"
#include "FixVec.h"
#include "MemoryManager.h"
#include "Serializable.h"
#include "OpenMP.h"
#include <vector>
#include <memory>
#include <list>

namespace AMDiS {

  // ============================================================================
  // ===== forward declarations =================================================
  // ============================================================================

  class Mesh;
  class FiniteElemSpace;
  class ElInfo;
  class DOFAdmin;
  class BasisFunction;
  class DOFIndexedBase;
  class DOFContainer;

  template<typename T> class DOFVector;

  // ============================================================================
  // ===== class DOFAdmin =======================================================
  // ============================================================================

  /** \ingroup DOFAdministration
   * \brief
   * Holds all data about one set of DOFs. It includes information about used and
   * unused DOF indices, as well as lists of DOFIndexed objects and DOFContainer
   * objects, that are automatically resized and resorted during mesh changes. 
   */
  class DOFAdmin : public Serializable
  {
  public:
    MEMORY_MANAGED(DOFAdmin);

    DOFAdmin();

    /** \brief
     * constructor
     */
    DOFAdmin(Mesh* m);

    /** \brief
     * constructor
     */
    DOFAdmin(Mesh* m, std::string aName);

    /** \brief
     * copy constructor
     */
    DOFAdmin(const DOFAdmin&);

    /** \brief
     * destructor
     */
    ~DOFAdmin();

    /** \brief
     * Enlarges the number of DOFs that can be managed at least to minsize by 
     * a step size of \ref sizeIncrement.
     */
    void enlargeDOFLists(int minsize);

    /** \brief
     * assignment operator
     */
    DOFAdmin& operator=(const DOFAdmin&);

    /** \brief
     * Compares two DOFAdmins by their names.
     */
    bool operator==(const DOFAdmin&) const;

    /** \brief
     * Compares two DOFAdmins by their names.
     */
    inline bool operator!=(const DOFAdmin& ad) const {
      return !(ad==*this);
    }
  
    /** \brief
     * Adds a DOFIndexedBase object to the DOFAdmin. This object will be
     * managed by DOFAdmin from now on.
     */
    void addDOFIndexed(DOFIndexedBase* dofIndexed);

    /** \brief
     * Adds a DOFContainer object to the DOFAdmin.
     */
    void addDOFContainer(DOFContainer* dofContainer);

    /** \brief
     * Removes the given DOFIndexedBase object from DOFAdmin.
     */
    void removeDOFIndexed(DOFIndexedBase* dofIndexed);

    /** \brief
     * Removes the given DOFContainer object from DOFAdmin.
     */
    void removeDOFContainer(DOFContainer* dofContainer);

    /** \brief
     * Removes all holes of unused DOF indices by compressing the used range of
     * indices (it does not resize the vectors). While the global index of a DOF
     * may change, the relative order of DOF indices remains unchanged during
     * compression. This method is usually called after mesh adaption involving
     * higher order elements or coarsening.
     */
    void compress(std::vector<DegreeOfFreedom> &new_dof);

    /** \brief
     * Returns an iterator to the begin of \ref dofIndexedList
     */
    std::list<DOFIndexedBase*>::iterator beginDOFIndexed() {
      return dofIndexedList.begin();
    }

    /** \brief
     * Returns an iterator to the end of \ref dofIndexedList
     */
    std::list<DOFIndexedBase*>::iterator endDOFIndexed() {
      return dofIndexedList.end();
    }

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

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

    /** \brief
     * Returns \ref sizeUsed.
     */
    inline const int getUsedSize() const { 
      return sizeUsed; 
    }

    /** \brief 
     * Returns \ref size
     */
    inline const int getSize() const { 
      return size; 
    }

    /** \brief 
     * Returns \ref usedCount
     */
    inline const int getUsedDOFs() const { 
      return usedCount; 
    }

    /** \brief 
     * Returns \ref holeCount
     */
    inline const int getHoleCount() const { 
      return holeCount; 
    }

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

    /** \brief 
     * Returns \ref nrDOF[i], i.e., the number of dofs for the
     * position i.
     */
    inline const int getNumberOfDOFs(int i) const {
      return nrDOF[i];
    }
 
    /** \brief
     * Returns \ref nrDOF
     */
    inline const DimVec<int>& getNumberOfDOFs() const { 
      return nrDOF; 
    }
 
    /** \brief
     * Returns \ref nr0DOF[i]
     */
    inline const int getNumberOfPreDOFs(int i) const {
      return nr0DOF[i];
    }
 
    /** \brief
     * Returns \ref nr0DOF
     */
    inline const DimVec<int>& getNumberOfPreDOFs() const { 
      return nr0DOF; 
    }

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

    /** \brief
     * Returns \ref dofFree, the array denoting DOFs to be either free or used.
     */
    inline const std::vector<bool>& getDOFFree() const { 
      return dofFree; 
    }

    /** \brief
     * Returns if the given DOF is free.
     */
    inline const bool isDOFFree(int i) const {
      return dofFree[i];
    }

    /** \} */

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

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

    /** \brief
     * Sets \ref nrDOF[i] = v
     */
    void setNumberOfDOFs(int i, int v); 

    /** \brief
     * Sets \ref nr0DOF[i] = v
     */
    void setNumberOfPreDOFs(int i, int v);

    /** \brief
     * Sets \ref name = n
     */
    inline void setName(const std::string& n) { 
      name = n; 
    }

    /** \brief
     * Sets \ref mesh = m
     */
    inline void setMesh(Mesh* m) { 
      mesh = m; 
    }

    /** \} */

  protected:
    /** \brief
     * initializes this DOFAdmin
     */
    void init();

    /** \brief
     * Adds one index to all DOF lists. Used by Mesh::getDOF() to provide 
     * DOFS for a specific position
     */
    int getDOFIndex();

    /** \brief
     * Frees index dof. Used by Mesh::getDOF()
     */
    void freeDOFIndex(int dof);

    // ===== Serializable implementation =====
  
    void serialize(std::ostream &out);

    void deserialize(std::istream &in);

  protected:
    /** \brief
     * name of this DOFAdmin
     */
    std::string name;

    /** \brief
     * the mesh this DOFAdmin belongs to
     */
    Mesh *mesh;

    /** \brief
     * the dofFree vector stores for each index whether it is used or not
     */
    std::vector<bool> dofFree;

    /** \brief
     * index of the first free value in \ref dofFree
     */
    int firstHole;   

    /** \brief
     * allocated size of managed vectors and matrices
     */
    int size;

    /** \brief
     * number of used dof indices
     */
    int usedCount;

    /** \brief
     * number of FREED dof indices (NOT size-sizeUsed)
     */
    int holeCount;

    /** \brief
     * > max. index of a used entry
     */
    int sizeUsed;

    /** \brief
     * Number of dofs for each position, i.e., vertex, 
     * edge, ..., center, for this DOFAdmin.
     */
    DimVec<int> nrDOF;

    /** \brief
     * dofs from previous DOFAdmins
     */
    DimVec<int> nr0DOF;

    /** \brief
     * list of all managed DOFIndexed objects.
     */
    std::list<DOFIndexedBase*> dofIndexedList;

    /** \brief
     * list of all managed DOFContainer objects
     */
    std::list<DOFContainer*> dofContainerList;

    /** \brief
     * size increment used by \ref enlargeDOFLists.
     */
    static const int sizeIncrement; 

    friend class DOFIteratorBase;
    friend class Mesh;
  };

}

#endif  // AMDIS_DOFADMIN_H