#ifndef GR_BFace3D
#define GR_BFace3D 1

#include "GR_BFace.h"
#include "GR_config.h"
#include "GR_misc.h"
#include "GR_Bdry3D.h"
#include "GR_Cell.h"
#include "GR_InsertionQueueEntry.h"

class TriBFaceBase : public BFace {
private:
  BdryPatch3D* pBP3D;
protected:
  /// Copy construction disallowed.  operator= inherited from BFace
  TriBFaceBase(const TriBFaceBase& TBF) : BFace(TBF), pBP3D(NULL) {assert(0);}
public:
  bool qValid() const
    {
      return (this && (pBP3D || true));
    }
  TriBFaceBase(const bool qIsInternal,
	       Face* const pFLeft,
	       Face* const pFRight = pFInvalidFace)
    : BFace(qIsInternal, pFLeft, pFRight), pBP3D(NULL) {}
  TriBFaceBase& operator=(const TriBFaceBase& TBF)
    {
      if (this != &TBF) {
	this->BFace::operator=(TBF);
	pBP3D = TBF.pBP3D;
      }
      return (*this);
    }
    
  int iNumVerts() const {return 3;}
  // Now we know enough to give the boundary condition
  virtual int iBdryCond() const
    {
      if (pBP3D) return pBP3D->iBdryCond();
      else       return iDefaultBC;
    }

  enum eEncroachResult eIsEncroached(const enum eEncroachType = eBall) const;
  // Tri specific versions of standard encroachment routines
  enum eEncroachResult eIsPointEncroachingBall(const double adPoint[],
					       const bool qTieBreak = false) const;
  // Lens checking isn't currently supported.
  enum eEncroachResult eIsPointEncroachingLens(const double adPoint[],
					       const bool qTieBreak = false) const;
  enum eEncroachResult eIsPointEncroachingNewLens(const double adPoint[],
						  const bool qTieBreak = false) const;
  double dInsertionPriority() const
    // To prevent infinitely recursive insertion, don't split
    // TriBFace's that are too small relative to the local length
    // scale (at this point, 15 times smaller is counted as "too
    // small".  This is easily accomplished by giving such over-small 
    // triangles a fake priority, much higher than the cutoff for
    // splitting. 
    {
      double dActualSize = 2/sqrt(2.) *
	(dynamic_cast<const TriFace*>(pFFace()))->dCircumradius();
      double dSizeLimit = 0;
      for (int ii = 0; ii < 3; ii++) {
	dSizeLimit += pVVert(ii)->dLS();
      }
      dSizeLimit /= (15*3); // 3 = num verts; 15 = size factor
      return (dActualSize > dSizeLimit ?
	      InsertionQueueEntry::dBdryFacePriority :
	      InsertionQueueEntry::dNeverQueuedPriority);
    }
  void vGetOffsetInsertionPoint(const double /*adTriggerLoc*/[],
				double /*adOffsetLoc*/[]) const {assert(0);}
  void vSetInsertionLocation(const double adNewLoc[]) const
    {
      adInsertionLoc[0] = adNewLoc[0];
      adInsertionLoc[1] = adNewLoc[1];
      adInsertionLoc[2] = adNewLoc[2];
    }
  void vCircumcenter(double adLoc[]) const {
    (dynamic_cast<const TriFace*>(pFFace()))->vCircumcenter(adLoc);
  }
  // Code needed for finding point locations for Shewchuk insertion;
  // these are all 3D-specific.
  bool qPointProjectsInside(const double adPoint[3]) const;
  int iSplitBdryFace(VolMesh *const pVM, const int iRecursionDepth);

  // Code for boundary connectivity in 3D; 2D is handled separately
  // because it needs to reference Bdry2D and BdryPatch2D objects.
  void vSetPatch(BdryPatch* const pBP)
    {
      vSetPatch(dynamic_cast<BdryPatch3D*>(pBP));
    }  
  void vSetPatch(BdryPatch3D* const pBP3DIn)
    {
      assert(pBP3DIn != NULL);
      pBP3D = pBP3DIn;
    }

  BdryPatch* pPatchPointer() const
    {
      return pBP3D;
    }
  void vCentroid(double adCent[]) const
    {
      adCent[0] = adCent[1] = adCent[2] = 0;
      for (int i = 0; i < 3; i++) {
	adCent[0] += pVVert(i)->dX();
	adCent[1] += pVVert(i)->dY();
	adCent[2] += pVVert(i)->dZ();
      }
      adCent[0] /= 3;
      adCent[1] /= 3;
      adCent[2] /= 3;
    }
};

// Boundary edges in 3D that are true boundaries of the domain
class TriBFace : public TriBFaceBase {
private:
  // Disallow copy constructor
  TriBFace(TriBFace& TBF) : TriBFaceBase(TBF) {assert(0);};
public:
  TriBFace(Face* const pF = pFInvalidFace)
    : TriBFaceBase(false, pF)
    {
      vSetType(Cell::eTriBFace);
    }
  ~TriBFace() {}
  int iNumFaces() const {return 1;}
}; // End of TriBFace declaration

// Boundary edges in 3D that are true boundaries of the domain
class IntTriBFace : public TriBFaceBase {
private:
  // Disallow copy constructor
  IntTriBFace(IntTriBFace& ITBF) : TriBFaceBase(ITBF) {assert(0);}
public:
  IntTriBFace(Face* const pFLeft = pFInvalidFace,
	      Face* const pFRight = pFInvalidFace)
    : TriBFaceBase(true, pFLeft, pFRight)
    {
      vSetType(Cell::eIntTriBFace);
    }
  ~IntTriBFace() {}
  int iNumFaces() const {return 2;}
  const Face *pFOtherFace(const Face * const pF) const
    {
      if (pF == pFFace(0)) return pFFace(1);
      else                 return pFFace(0);
    }
}; // End of IntTriBFace declaration

class QuadBFaceBase : public BFace {
private:
  BdryPatch3D* pBP3D;
protected:
  /// Copy construction disallowed.  operator= inherited from BFace
  QuadBFaceBase(const QuadBFaceBase& TBF) : BFace(TBF), pBP3D(NULL)
    {assert(0);}
public:
  QuadBFaceBase(const bool qIsInternal,
		Face* const pFLeft,
		Face* const pFRight = pFInvalidFace)
    : BFace(qIsInternal, pFLeft, pFRight), pBP3D(NULL)
    {}
  QuadBFaceBase& operator=(const QuadBFaceBase& QBF)
    {
      if (this != &QBF) {
	this->BFace::operator=(QBF);
	pBP3D = QBF.pBP3D;
      }
      return (*this);
    }
  int iNumVerts() const {return 4;}
  // Now we know enough to give the boundary condition
  virtual int iBdryCond() const
    {
      if (pBP3D) return pBP3D->iBdryCond();
      else       return iDefaultBC;
    }

  // Return value below only for the benefit of picky compilers.
  enum eEncroachResult eIsEncroached(const eEncroachType = eBall) const
    {assert(0); return eClean;}
  // Quad specific versions of standard encroachment routines
  enum eEncroachResult eIsPointEncroachingBall(const double [],
					       const bool = false) const
    {
      assert(0);
      return eClean;
    }
  enum eEncroachResult eIsPointEncroachingLens(const double [],
					       const bool = false) const
    {
      assert(0);
      return eClean;
    }
  enum eEncroachResult eIsPointEncroachingNewLens(const double [],
						  const bool = false) const
    {
      assert(0);
      return eClean;
    }
  void vCircumcenter(double[]) const {assert(0);}

  // Code for boundary connectivity in 3D; 2D is handled separately
  // because it needs to reference Bdry2D and BdryPatch2D objects.
  void vSetPatch(BdryPatch* const pBP)
    {
      vSetPatch(dynamic_cast<BdryPatch3D*>(pBP));
    }  
  void vSetPatch(BdryPatch3D* const pBP3DIn)
    {
      assert(pBP3DIn != NULL);
      pBP3D = pBP3DIn;
    }

  BdryPatch* pPatchPointer() const
    {
      return pBP3D;
    }
}; // End of QuadBFaceBase declaration

// Boundary edges in 3D that are true boundaries of the domain
class QuadBFace : public QuadBFaceBase {
private:
  // Disallow copy constructor
  QuadBFace(QuadBFace& QBF) : QuadBFaceBase(QBF) {assert(0);}
public:
  QuadBFace(Face* const pF = pFInvalidFace)
    : QuadBFaceBase(false, pF)
    {
      vSetType(Cell::eQuadBFace);
    }
  ~QuadBFace() {}
  int iNumFaces() const {return 1;}
}; // End of QuadBFace declaration

// Boundary edges in 3D that are true boundaries of the domain
class IntQuadBFace : public QuadBFaceBase {
private:
  // Disallow copy constructor
  IntQuadBFace(IntQuadBFace& IQBF) : QuadBFaceBase(IQBF) {assert(0);}
public:
  IntQuadBFace(Face* const pFLeft = pFInvalidFace,
	      Face* const pFRight = pFInvalidFace)
    : QuadBFaceBase(true, pFLeft, pFRight)
    {
      vSetType(Cell::eIntQuadBFace);
    }
  ~IntQuadBFace() {}
  int iNumFaces() const {return 2;}
  const Face *pFOtherFace(const Face * const pF) const
    {
      if (pF == pFFace(0)) return pFFace(1);
      else                 return pFFace(0);
    }
}; // End of IntQuadBFace declaration

#endif
