#ifndef GR_BFace2D
#define GR_BFace2D 1

#include "GR_BFace.h"
#include "GR_config.h"

#include "GR_misc.h"
#include "GR_InsertionQueueEntry.h"
#include "GR_BdryPatch2D.h"
#include "GR_Bdry2D.h"
#include "GR_Cell.h"

class BdryEdgeBase : public BFace {
private:
  Bdry2D* pB2D;
  int iBdryPatchIndex;
protected:
  // Copy construction disallowed, but declared protected so that
  // derived classes can pretend to use it.
  BdryEdgeBase(BdryEdgeBase& BEB) : BFace(BEB), pB2D(),
    iBdryPatchIndex() {assert(0);}
  // These need not be public because this is a virtual base class.

  // Typical constructor; called from BdryEdge and IntBdryEdge
  // constructors with first two args guaranteed to exist.
  BdryEdgeBase(const bool qIsInternal,
	       Face* const pFLeft,
	       Face* const pFRight = pFInvalidFace)
    : BFace(qIsInternal, pFLeft, pFRight), pB2D(NULL), 
      iBdryPatchIndex(-1) {}
  BdryEdgeBase& operator=(const BdryEdgeBase& BEB)
    {
      if (this != &BEB) {
	this->BFace::operator=(BEB);
	pB2D = BEB.pB2D;
	iBdryPatchIndex = BEB.iBdryPatchIndex;
      }
      return (*this);
    }
public:
  virtual ~BdryEdgeBase() {}

  bool qValid() const
    {
      return (this && pB2D &&
	      iBdryPatchIndex >= 0 &&
	      iBdryPatchIndex < pB2D->iNumPatches());
    }
  int iNumVerts() const {return 2;}
  // Now we know enough to give the boundary condition
  int iBdryCond() const
    {
      // fprintf(stdout, "Inside EdgeBFace::iBdryCond()\n");
      if (pB2D) {
	return pB2D->pBdry(iBdryPatchIndex)->iBdryCond();
      }
      else {
	return iDefaultBC;
      }
    }
  virtual enum eEncroachResult eIsEncroached(const eEncroachType eET) const;
  // Edge specific versions of standard encroachment routines
  enum eEncroachResult eIsPointEncroachingBall(const double adPoint[],
					       const bool qTieBreak = false) const;
  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
    {return InsertionQueueEntry::dBdryFacePriority;}
  void vGetOffsetInsertionPoint(const double adTriggerLoc[],
				double adOffsetLoc[]) const;
  void vSetInsertionLocation(const double adNewLoc[]) const
    {
      adInsertionLoc[0] = adNewLoc[0];
      adInsertionLoc[1] = adNewLoc[1];
    }
  // Code needed for finding point locations for Ruppert insertion;
  // these are all 2D-specific.
  void vGetRatioPoint(double dRatio, double adLoc[2]) const;
  void vGetBisectionPoint(double adLoc[2]) const;
  void vGetCCSPoint(double adLoc[2]) const;
  void vCentroid(double adLoc[]) const
    {
      vGetBisectionPoint(adLoc);
    }
  void vCircumcenter(double adLoc[]) const
    {
      vGetBisectionPoint(adLoc);
    }

  // Code for boundary connectivity in 2D; 3D is handled separately
  // because it needs to reference Bdry3D and BdryPatch3D objects.
  int iPatchIndex() {return iBdryPatchIndex;}
  void vSetPatch(BdryPatch* const pBPIn)
    {
      assert(ppFFaces[0] != NULL);
      assert(pBPIn != NULL);
      BdryPatch2D* pBP = dynamic_cast<BdryPatch2D*>(pBPIn);
      assert(pBP != NULL);
      pB2D = pBP->pBdry2D();
      iBdryPatchIndex = pB2D->iPatchIndex(pBP);
      if (pBP->qIsPeriodic()) {
	Periodic *pP = pBP->pPGetPeriodicData();
	Face *pF = ppFFaces[0];
	// Do the remove first in case it's already there.
	pP->vRemoveFace(pBP, pF);
	pP->vAddFace(pBP, pF);
      }
    } 
  void vSetPatch(int const iPatch, Bdry2D* const pB2Dptr = NULL)
    {
      assert(ppFFaces[0] != NULL);
      iBdryPatchIndex = iPatch;
      if (pB2Dptr != static_cast<Bdry2D*>(NULL))
	pB2D = pB2Dptr;
      BdryPatch* pBP = pB2D->pBdry(iPatch);
      if (pBP->qIsPeriodic()) {
	Periodic *pP = pBP->pPGetPeriodicData();
	Face *pF = ppFFaces[0];
	// Do the remove first in case it's already there.
	pP->vRemoveFace(pBP, pF);
	pP->vAddFace(pBP, pF);
      }
    }

  Bdry2D* pBdryPointer() const { return pB2D; }
  BdryPatch* pPatchPointer() const
    {
      return pB2D->pBdry(iBdryPatchIndex);
    }
};

// Boundary edges in 2D that are true boundaries of the domain
class BdryEdge : public BdryEdgeBase {
private:
  // Disallow copy constructor
  BdryEdge(BdryEdge& BE) : BdryEdgeBase(BE) {assert(0);};
public:
  BdryEdge(Face* const pF = pFInvalidFace)
    : BdryEdgeBase(false, pF)
    {
      vSetType(Cell::eBdryEdge);
    }
  BdryEdge& operator=(BdryEdge& BE) {
    this->BdryEdgeBase::operator=(BE);
    return (*this);
  }
  virtual ~BdryEdge() {}
  int iNumFaces() const {return 1;}
}; // End of BdryEdge declaration

// Boundary edges in 2D that are true boundaries of the domain
class IntBdryEdge : public BdryEdgeBase {
private:
  // Disallow copy constructor
  IntBdryEdge(IntBdryEdge& IBE) : BdryEdgeBase(IBE) {assert(0);}
public:
  // Probably need copy constructor and operator= here.
  IntBdryEdge(Face* const pFLeft = pFInvalidFace,
	      Face* const pFRight = pFInvalidFace)
    : BdryEdgeBase(true, pFLeft, pFRight)
    {
      vSetType(Cell::eIntBdryEdge);
    }
  IntBdryEdge& operator=(IntBdryEdge& IBE)
    {
      this->BdryEdgeBase::operator=(IBE);
      return (*this);
    }
  virtual ~IntBdryEdge() {}
  int iNumFaces() const {return 2;}
  Face *pFOtherFace(const Face * const pF) const
    {
      if (pF == pFFace(0)) return const_cast<Face*>(pFFace(1));
      else                 return const_cast<Face*>(pFFace(0));
    }
}; // End of IntBdryEdge declaration

#endif
