// ---------------------------------------------------------------------
//
// Copyright (C) 2006 - 2021 by the deal.II authors
//
// This file is part of the deal.II library.
//
// The deal.II library is free software; you can use it, redistribute
// it, and/or modify it under the terms of the GNU Lesser General
// Public License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// The full text of the license can be found in the file LICENSE.md at
// the top level directory of deal.II.
//
// ---------------------------------------------------------------------

#ifndef dealii_dof_objects_h
#define dealii_dof_objects_h


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

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

#include <vector>

DEAL_II_NAMESPACE_OPEN

// Forward declarations
#ifndef DOXYGEN
template <int, int>
class DoFHandler;
#endif

namespace internal
{
  namespace DoFHandlerImplementation
  {
#ifndef DOXYGEN
    template <int>
    class DoFLevel;
    template <int>
    class DoFFaces;
#endif

    /**
     * Store the indices of the degrees of freedom which are located on
     * objects of dimension @p dim.
     *
     * <h3>Information for all DoFObjects classes</h3>
     *
     * The DoFObjects classes store the global indices of the degrees of
     * freedom for each cell on a certain level. The global index or number of
     * a degree of freedom is the zero-based index of the according value in
     * the solution vector and the row and column index in the global matrix
     * or the multigrid matrix for this level. These indices refer to the
     * unconstrained vectors and matrices, where we have not taken account of
     * the constraints introduced by hanging nodes.
     *
     * Since vertices are not associated with a particular level, the indices
     * associated with vertices are not stored in the DoFObjects classes but
     * rather in the DoFHandler::vertex_dofs array.
     *
     * The DoFObjects classes are not used directly, but objects of these
     * classes are included in the DoFLevel and DoFFaces classes.
     *
     * @ingroup dofs
     */
    template <int dim>
    class DoFObjects
    {
    public:
      /**
       * Store the global indices of the degrees of freedom.
       */
      std::vector<types::global_dof_index> dofs;

      /**
       * Return the global index of the @p local_index-th degree of freedom
       * located on the object with number @p obj_index. The @p dof_handler
       * argument is used to access the finite element that is to be used to
       * compute the location where this data is stored.
       *
       * The third argument, @p fe_index, must equal zero. It is otherwise
       * unused, but we retain the argument so that we can use the same
       * interface for non-hp- and hp-finite element methods, in effect making
       * it possible to share the DoFAccessor class hierarchy between hp- and
       * non-hp-classes.
       */
      template <int dh_dim, int spacedim>
      types::global_dof_index &
      access_dof_index(const dealii::DoFHandler<dh_dim, spacedim> &dof_handler,
                       const unsigned int                          obj_index,
                       const unsigned int                          fe_index,
                       const unsigned int                          local_index);

      /**
       * Return the value 1. The meaning of this function becomes clear by
       * looking at what the corresponding functions in the classes
       * internal::hp::DoFObjects
       */
      template <int dh_dim, int spacedim>
      unsigned int
      n_active_fe_indices(
        const dealii::DoFHandler<dh_dim, spacedim> &dof_handler,
        const types::global_dof_index               index) const;

      /**
       * Similar to the function above. Assert that the given index is zero,
       * and then return true.
       */
      template <int dh_dim, int spacedim>
      bool
      fe_index_is_active(
        const dealii::DoFHandler<dh_dim, spacedim> &dof_handler,
        const types::global_dof_index               index,
        const unsigned int                          fe_index) const;

      /**
       * Determine an estimate for the memory consumption (in bytes) of this
       * object.
       */
      std::size_t
      memory_consumption() const;

      /**
       * Read or write the data of this object to or from a stream for the
       * purpose of serialization using the [BOOST serialization
       * library](https://www.boost.org/doc/libs/1_74_0/libs/serialization/doc/index.html).
       */
      template <class Archive>
      void
      serialize(Archive &ar, const unsigned int version);

      // Declare the classes that store levels and faces of DoFs friends so
      // that they can resize arrays.
      template <int>
      friend class DoFLevel;
      template <int>
      friend class DoFFaces;
    };


    // --------------------- template and inline functions ------------------

    template <int dim>
    template <int dh_dim, int spacedim>
    inline unsigned int
    DoFObjects<dim>::n_active_fe_indices(
      const dealii::DoFHandler<dh_dim, spacedim> &,
      const types::global_dof_index) const
    {
      return 1;
    }



    template <int dim>
    template <int dh_dim, int spacedim>
    inline bool
    DoFObjects<dim>::fe_index_is_active(
      const dealii::DoFHandler<dh_dim, spacedim> &,
      const types::global_dof_index,
      const unsigned int fe_index) const
    {
      (void)fe_index;
      Assert((fe_index ==
              dealii::DoFHandler<dh_dim, spacedim>::default_fe_index),
             ExcMessage("Only zero fe_index values are allowed for "
                        "non-hp-DoFHandlers."));
      return true;
    }



    template <int dim>
    template <int dh_dim, int spacedim>
    inline types::global_dof_index &
    DoFObjects<dim>::access_dof_index(
      const dealii::DoFHandler<dh_dim, spacedim> &dof_handler,
      const unsigned int                          obj_index,
      const unsigned int                          fe_index,
      const unsigned int                          local_index)
    {
      (void)fe_index;
      Assert(
        (fe_index == dealii::DoFHandler<dh_dim, spacedim>::default_fe_index),
        ExcMessage(
          "Only the default FE index is allowed for non-hp-DoFHandler objects"));
      Assert(
        local_index < dof_handler.get_fe().template n_dofs_per_object<dim>(),
        ExcIndexRange(local_index,
                      0,
                      dof_handler.get_fe().template n_dofs_per_object<dim>()));
      Assert(obj_index *
                   dof_handler.get_fe().template n_dofs_per_object<dim>() +
                 local_index <
               dofs.size(),
             ExcInternalError());

      return dofs[obj_index *
                    dof_handler.get_fe().template n_dofs_per_object<dim>() +
                  local_index];
    }


    template <int dim>
    template <class Archive>
    void
    DoFObjects<dim>::serialize(Archive &ar, const unsigned int)
    {
      ar &dofs;
    }

  } // namespace DoFHandlerImplementation
} // namespace internal

DEAL_II_NAMESPACE_CLOSE

#endif
