#ifndef DUNE_ALU3DGRIDFACEUTILITY_HH
#define DUNE_ALU3DGRIDFACEUTILITY_HH

#include <dune/common/misc.hh>
#include <dune/geometry/referenceelements.hh>

#include "mappings.hh"
#include "alu3dinclude.hh"
#include "topology.hh"

namespace Dune
{

  // convert FieldVectors to alu3dtypes 
  // only used for calculating the normals because the method of the
  // mapping classes want double (&)[3] and we have FieldVectors which store an
  // double [3] this is why we can cast here
  // plz say notin' Adrian 
  template< int dim >
  inline alu3d_ctype (&fieldVector2alu3d_ctype ( FieldVector< alu3d_ctype, dim > &val ))[ dim ]
  {
    return ((alu3d_ctype (&)[dim]) (*( &(val[0])) ));
  }

  // convert const FieldVectors to const alu3dtypes 
  template< int dim >
  inline const alu3d_ctype (&fieldVector2alu3d_ctype ( const FieldVector< alu3d_ctype, dim > &val ))[ dim ]
  {
    return ((const alu3d_ctype (&)[dim]) (*( &(val[0])) ) );
  }


  // * Note: reconsider lazy evaluation of coordinates

  //- class ALU3dGridFaceInfo
  /* \brief Stores face and adjoining elements of the underlying ALU3dGrid
     The class has the same notion of inner and outer element as the 
     intersection iterator.
  */
  template< ALU3dGridElementType type, class Comm >
  class ALU3dGridFaceInfo
  {
    typedef ALU3dImplTraits< type, Comm >  ImplTraits;
    //- private typedefs
    typedef typename ImplTraits::HasFaceType HasFaceType;
  public:
    enum ConformanceState {CONFORMING, REFINED_INNER, REFINED_OUTER, UNDEFINED };
    //- typedefs
    typedef typename ImplTraits::GEOFaceType GEOFaceType;
    typedef typename ImplTraits::GEOElementType GEOElementType;
    typedef typename ImplTraits::GEOPeriodicType GEOPeriodicType;
    typedef typename ImplTraits::IMPLElementType IMPLElementType;
    typedef typename ImplTraits::GhostPairType GhostPairType; 
    typedef typename ImplTraits::BNDFaceType BNDFaceType;

  public:
    //! constructor creating empty face info 
    ALU3dGridFaceInfo();
    void updateFaceInfo(const GEOFaceType& face, int innerLevel, int innerTwist);

    //- constructors and destructors
    //! Construct a connector from a face and the twist seen from the inner
    //! element
    //! \note: The user is responsible for the consistency of the input data
    //! as well as for choosing the appropriate (i.e. most refined) face
    ALU3dGridFaceInfo(const GEOFaceType& face, int innerTwist);
    //! Copy constructor
    ALU3dGridFaceInfo(const ALU3dGridFaceInfo &orig);
    //! Destructor
    ~ALU3dGridFaceInfo();

  protected:  
    //! returns true if outerEntity casts into a helement
    bool isElementLike() const;

    //! returns true if inside is a ghost entity
    bool innerBoundary() const; 

  public:  
    //- queries
    //! returns true if the face lies on the domain boundary 
    //! and is not a periodic boundary 
    bool outerBoundary() const;

    //! returns true if the face lies on the domain boundary 
    bool boundary() const;

    //! returns true if outside is something meaningfull
    bool neighbor() const ;
  
    //! is the neighbour element a ghost element or a ghost face 
    //! in case of face true is returned 
    bool ghostBoundary () const;

    //! Returns the ALU3dGrid face
    const GEOFaceType& face() const;
    //! Returns the inner element at that face
    const GEOElementType& innerEntity() const;
    //! Returns the outer element at that face
    //! \note This function is only meaningful in the interior
    const GEOElementType& outerEntity() const;
    //! Returns the inner element at that face
    //! \note This function is only meaningful at a boundary
    const BNDFaceType& innerFace() const;
    //! Returns the boundary (outer) element at that face
    //! \note This function is only meaningful at a boundary
    const BNDFaceType& boundaryFace() const;

    //! Twist of the face seen from the inner element
    int innerTwist() const;
    //! Twist of the face seen from the outer element
    int outerTwist() const;
  
    //! Twist of the face seen from the inner element
    int duneTwist(const int faceIdx, const int aluTwist) const;

    //! Local number of the face in inner element (ALU3dGrid reference element)
    int innerALUFaceIndex() const;
    //! Local number of the face in outer element (ALU3dGrid reference element)
    int outerALUFaceIndex() const;

    int outsideLevel() const;

    //! return boundary segment index if intersection is with domain boundary
    int segmentIndex() const;

    //! return boundary id if intersection is with domain boundary
    int boundaryId() const;
  
    //! Description of conformance on the face
    ConformanceState conformanceState() const;

    //! return whether we are in a parallel environment or not 
    bool parallel() const {
      return ! Conversion< Comm, No_Comm > :: sameType ;
    }
   
  private:
    //! Description of conformance on the face
    ConformanceState getConformanceState(const int innerLevel) const;
   
    //- forbidden methods
    const ALU3dGridFaceInfo & 
    operator=(const ALU3dGridFaceInfo &orig);

  private:

    //- member data
    const GEOFaceType* face_;
    const HasFaceType* innerElement_;
    const HasFaceType* outerElement_;

    int  innerFaceNumber_;
    int  outerFaceNumber_;

    int  innerTwist_;
    int  outerTwist_;

    int  segmentIndex_;
    int  bndId_;

    enum boundary_t { noBoundary          = 0, // no boundary, outside is normal element
                      periodicBoundary    = 1, // periodic boundary
                      innerGhostBoundary  = 2, // process boundary, inside is ghost, outside is normal element 
                      domainBoundary      = 3, // boundary with domain, no outside
                      outerGhostBoundary  = 4};// process boundary, outside is ghost 

    boundary_t bndType_; 

    ConformanceState conformanceState_;
  };


  // ALU3dGridSurfaceMappingFactory
  // ------------------------------

  template< ALU3dGridElementType type, class Comm >
  struct ALU3dGridSurfaceMappingFactory;

  template< class Comm >
  struct ALU3dGridSurfaceMappingFactory< tetra, Comm >
  {
    // this is the original ALUGrid LinearSurfaceMapping, 
    // see mapp_tetra_3d.* in ALUGrid code 
    typedef ALU3DSPACE LinearSurfaceMapping SurfaceMappingType;
    typedef typename ALU3dGridFaceInfo< tetra, Comm >::GEOFaceType GEOFaceType;

    static const int numVerticesPerFace = EntityCount< tetra >::numVerticesPerFace;

    typedef FieldMatrix< alu3d_ctype, numVerticesPerFace, 3 > CoordinateType;

    // old method, copies values for tetra twice
    SurfaceMappingType *buildSurfaceMapping ( const CoordinateType &coords ) const;
    // get face but doesn't copy values twice
    SurfaceMappingType *buildSurfaceMapping ( const GEOFaceType &face ) const;
  };

  template< class Comm >
  struct ALU3dGridSurfaceMappingFactory< hexa, Comm >
  {
    typedef BilinearSurfaceMapping SurfaceMappingType;
    typedef typename ALU3dGridFaceInfo< hexa, Comm >::GEOFaceType GEOFaceType;

    static const int numVerticesPerFace = EntityCount< hexa >::numVerticesPerFace;

    typedef FieldMatrix< alu3d_ctype, numVerticesPerFace, 3 > CoordinateType;

    // old method, copies values for tetra twice
    SurfaceMappingType *buildSurfaceMapping ( const CoordinateType &coords ) const;
    // get face but doesn't copy values twice
    SurfaceMappingType *buildSurfaceMapping ( const GEOFaceType &face ) const;
  };



  // ALU3dGridGeometricFaceInfoBase
  // ------------------------------

  //! Helper class which provides geometric face information for the 
  //! ALU3dGridIntersectionIterator
  template< ALU3dGridElementType type, class Comm >
  class ALU3dGridGeometricFaceInfoBase
  : public ALU3dGridSurfaceMappingFactory< type, Comm >
  {
    typedef ALU3dGridSurfaceMappingFactory< type, Comm > Base;

  public:
    typedef ElementTopologyMapping<type> ElementTopo;
    typedef FaceTopologyMapping<type> FaceTopo;
    typedef NonConformingFaceMapping< type, Comm > NonConformingMappingType;

    // type of container for reference elements 
    typedef GenericReferenceElements< alu3d_ctype, 3 > ReferenceElementContainerType;
    // type of container for reference faces 
    typedef GenericReferenceElements< alu3d_ctype, 2 > ReferenceFaceContainerType;

    // type of reference element
    typedef GenericReferenceElement<alu3d_ctype, 3> ReferenceElementType;
    // type of reference face 
    typedef GenericReferenceElement<alu3d_ctype, 2> ReferenceFaceType;

    enum SideIdentifier { INNER, OUTER };
    enum { dimworld = 3 }; // ALU is a pure 3d grid
    enum { numVerticesPerFace = 
           EntityCount<type>::numVerticesPerFace };

    //- public typedefs
    typedef FieldVector<alu3d_ctype, 3> NormalType;
    typedef FieldMatrix<alu3d_ctype, 
                        numVerticesPerFace,
                        dimworld> CoordinateType;

    typedef typename ALU3dGridFaceInfo< type, Comm >::GEOFaceType GEOFaceType;

  public:
    typedef ALU3dGridFaceInfo< type, Comm > ConnectorType;

    //- constructors and destructors
    ALU3dGridGeometricFaceInfoBase(const ConnectorType &);
    ALU3dGridGeometricFaceInfoBase(const ALU3dGridGeometricFaceInfoBase &);
   
    //! reset status of faceGeomInfo
    void resetFaceGeom();

    //- functions
    const CoordinateType& intersectionSelfLocal() const;
    const CoordinateType& intersectionNeighborLocal() const;

  private:
    //- forbidden methods
    const ALU3dGridGeometricFaceInfoBase &operator=(const ALU3dGridGeometricFaceInfoBase &);
  
  private:
    //- private methods
    void generateLocalGeometries() const;
  
    int globalVertexIndex(const int duneFaceIndex, 
                          const int faceTwist,
                          const int duneFaceVertexIndex) const;

    void referenceElementCoordinatesRefined(SideIdentifier side, 
                                            CoordinateType& result) const;
    void referenceElementCoordinatesUnrefined(SideIdentifier side,
                                              CoordinateType& result) const;
  
  protected:
    //- private data
    const ConnectorType& connector_; 
    
    mutable CoordinateType coordsSelfLocal_;
    mutable CoordinateType coordsNeighborLocal_;

    mutable bool generatedGlobal_;
    mutable bool generatedLocal_;

    inline static const ReferenceElementType& getReferenceElement() 
    {
      return (type == tetra) ? 
        ReferenceElementContainerType :: simplex() : 
        ReferenceElementContainerType :: cube(); 
    }

    inline static const ReferenceFaceType& getReferenceFace() 
    {
      return (type == tetra) ? 
        ReferenceFaceContainerType :: simplex() : 
        ReferenceFaceContainerType :: cube(); 
    }
  };

  //! Helper class which provides geometric face information for the 
  //! ALU3dGridIntersectionIterator
  template< class Comm >
  class ALU3dGridGeometricFaceInfoTetra
  : public  ALU3dGridGeometricFaceInfoBase< tetra, Comm >
  {
    typedef ALU3dGridGeometricFaceInfoBase< tetra, Comm > Base;

  public:
    //- public typedefs
    typedef FieldVector<alu3d_ctype, 3> NormalType;
    typedef typename Base::FaceTopo FaceTopo;
    typedef typename ALU3dGridFaceInfo< tetra, Comm >::GEOFaceType GEOFaceType;

    typedef ALU3dGridFaceInfo< tetra, Comm > ConnectorType;

    //- constructors and destructors
    ALU3dGridGeometricFaceInfoTetra(const ConnectorType& ctor);
    ALU3dGridGeometricFaceInfoTetra(const ALU3dGridGeometricFaceInfoTetra & orig);
   
    NormalType & outerNormal(const FieldVector<alu3d_ctype, 2>& local) const;
    
    //! reset status of faceGeomInfo
    void resetFaceGeom();
    
    //! update global geometry
    template <class GeometryImp>
    void buildGlobalGeom(GeometryImp& geo) const;
      
  private:
    //- forbidden methods
    const ALU3dGridGeometricFaceInfoTetra & operator=(const ALU3dGridGeometricFaceInfoTetra &);
  
  protected:
    using Base::connector_;

  private:
    //- private data 
    mutable NormalType outerNormal_;
    
    // false if surface mapping needs a update 
    mutable bool normalUp2Date_; 
  };

  //! Helper class which provides geometric face information for the 
  //! ALU3dGridIntersectionIterator
  template< class Comm >
  class ALU3dGridGeometricFaceInfoHexa
  : public  ALU3dGridGeometricFaceInfoBase< hexa, Comm >
  {
    typedef ALU3dGridGeometricFaceInfoBase< hexa, Comm > Base;

  public:
    //- public typedefs
    typedef FieldVector<alu3d_ctype, 3> NormalType;
    typedef typename Base::FaceTopo FaceTopo;
    typedef typename ALU3dGridFaceInfo< hexa, Comm >::GEOFaceType GEOFaceType;
    typedef SurfaceNormalCalculator SurfaceMappingType;

    typedef ALU3dGridFaceInfo< hexa, Comm > ConnectorType;

    //- constructors and destructors
    ALU3dGridGeometricFaceInfoHexa(const ConnectorType &);
    ALU3dGridGeometricFaceInfoHexa(const ALU3dGridGeometricFaceInfoHexa &);
   
    NormalType & outerNormal(const FieldVector<alu3d_ctype, 2>& local) const;
    
    //! reset status of faceGeomInfo
    void resetFaceGeom();
    
    //! update global geometry
    template <class GeometryImp>
    void buildGlobalGeom(GeometryImp& geo) const;
      
  private:
    //- forbidden methods
    const ALU3dGridGeometricFaceInfoHexa & operator=(const ALU3dGridGeometricFaceInfoHexa &);
  
  protected:
    using Base::connector_;

  private:
    //- private data 
    mutable NormalType outerNormal_;
  
    // surface mapping for calculating the outer normal 
    mutable SurfaceMappingType mappingGlobal_;

    // false if surface mapping needs a update 
    mutable bool mappingGlobalUp2Date_; 
  };

} // end namespace Dune

#include "faceutility_imp.cc"

#endif
