// ============================================================================
// ==                                                                        ==
// == AMDiS - Adaptive multidimensional simulations                          ==
// ==                                                                        ==
// ============================================================================
// ==                                                                        ==
// ==  TU Dresden                                                            ==
// ==                                                                        ==
// ==  Institut f�r Wissenschaftliches Rechnen                               ==
// ==  Zellescher Weg 12-14                                                  ==
// ==  01069 Dresden                                                         ==
// ==  germany                                                               ==
// ==                                                                        ==
// ============================================================================
// ==                                                                        ==
// ==  https://gforge.zih.tu-dresden.de/projects/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

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

namespace AMDiS {

  /** \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:
    DOFAdmin();

    /// Constructor
    DOFAdmin(Mesh* m);

    /// Constructor
    DOFAdmin(Mesh* m, std::string aName);

    /// Copy constructor
    DOFAdmin(const DOFAdmin&);

    /// 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 = 0);

    /// assignment operator
    DOFAdmin& operator=(const DOFAdmin&);

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

    /// 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);

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

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

    /// 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);

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

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

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

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

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

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

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

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

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

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

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

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

    /// Sets a DOF to be free or not.
    inline void setDOFFree(int i, bool b)
    {
      dofFree[i] = b;
    }
    
    /// Sets \ref usedSize.
    inline void setUsedSize(int i)
    {
      sizeUsed = i;
    }

    /// Sets \ref usedCount.
    inline void setUsedCount(int i)
    {
      usedCount = i;
    }

    /// Sets \ref firstHole
    inline void setFirstHole(int i)
    {
      TEST_EXIT_DBG(dofFree[i])("There is no hole!\n");

      firstHole = i;
    }

    /** \} */

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

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

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

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

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

    int calcMemoryUsage();

    /** \} */

  protected:
    /// 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();

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

    ///
    void serialize(std::ostream &out);

    ///
    void deserialize(std::istream &in);

  protected:
    /// name of this DOFAdmin
    std::string name;

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

    /// The dofFree vector stores for each index whether it is used or not
    std::vector<bool> dofFree;

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

    /// allocated size of managed vectors and matrices
    int size;

    /// number of used dof indices
    int usedCount;

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

    /// > 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;

    /// Dofs from previous DOFAdmins
    DimVec<int> nr0DOF;

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

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

    /// Size increment used by \ref enlargeDOFLists.
    static const int sizeIncrement; 

    friend class DOFIteratorBase;
    friend class Mesh;
  };

}

#endif  // AMDIS_DOFADMIN_H