#ifndef GR_Face
#define GR_Face 1

#include "GR_config.h"
#include "GR_Entity.h"
#include "GR_Vertex.h"
#include "GR_Cell.h"

// Categories into which tetrahedral faces are placed for purposes of
// swapping.  T indicates that the face is transformable, N indicates
// non-transformable.  For "T" faces, the digits are the number of tets
// before and after the transformation.  For "N" faces, the digits are
// the number of tets that would be present for a transformable case; a
// zero as the second digit indicates that the case is irrevocably
/// unswappable.
enum eFaceCat {eT23 = 0, eT32 = 1, eT22 = 2, eT44 = 3, eN32 = 4,
	       eN44 = 5, eN40 = 6, eN30 = 7, eN20 = 8, eBdry = 9,
	       eBdryReflex = 10, eOther = 11};

///
class Face : public Entity {
protected:
  /// Copy construction disallowed.
  Face(const Face&) : Entity() {assert(0);}
  ///
  Cell* pCL;
  ///
  Cell* pCR;
  ///
  Vert* apVVerts[4];
  ///
  Face(Cell* const pCA, Cell* const pCB, const int iNV,
       Vert* const pV0 = pVInvalidVert,
       Vert* const pV1 = pVInvalidVert,
       Vert* const pV2 = pVInvalidVert,
       Vert* const pV3 = pVInvalidVert);
public:
  ///
  enum eFaceType {eEdgeFace, eMultiEdge, eTriFace, eQuadFace};
  ///
  // This enum must begin with eUnknown and end with eInvalidFaceLoc
  enum eFaceLoc {eUnknown = 0, eBdryFace, eBdryTwoSide, ePseudoSurface,
		 eInterior, eExterior, eInvalidFaceLoc};
protected:
  // This is a packed bitfield containing a set of flags, including 10
  // bits for user data, all using native C++ handling.
  //
  // Bit     18:  Can the face be swapped legally? (T)/F  Could be false
  //                for pseudo-structured regions, eBdryTwoSide, eBdry,
  //                different cell shapes, etc.  Computable.
  // Bit     17:  Check for swapping (last swap successful or
  //                neighborhood changed) (T)/F
  // Bit     16:  Face size OK? T/(F)
  // Bit     15:  Embedded in a "structured" part of the mesh? T/(F)
  // Bit     14:  Same cell shape on each side? (T)/F
  // Bit     13:  Face deleted? T/(F)
  // Bits 12-10:  Face location (from the enum eFaceLoc) (eInterior)
  // Bits  9- 0:  User flags (0)
  ///
  mutable unsigned int uiLoc:4;
  unsigned int uiUser:10;
  bool qSwapCheck:1, qSizeOK:1, qStruct:1, qSameShape:1, qDel:1;
public:
  ///
  virtual ~Face() {}
  ///
  Face& operator=(const Face& F);
  ///
  virtual eFaceType eType() const = 0;
  ///
  virtual int iNumVerts() const = 0;
  ///
  void vSetDefaultFlags();
  ///
  void vCopyAllFlags(const Face &F);
  ///
  void vSetUserData(const unsigned int uiData) {
    assert(uiData < 1024);
    uiUser = uiData;
  }
  ///
  void vClearUserData() {uiUser = 0;}
  ///
  int iGetUserData() const {return uiUser;}
  ///
  int iFaceLoc() const { return uiLoc; }
  ///
  void vSetFaceLoc(const int iNewLoc) {
    assert(iNewLoc >= eUnknown && iNewLoc <= eInvalidFaceLoc);
    uiLoc = iNewLoc;
  }
  ///
  void vSetFaceLoc();
  ///
  bool qDeleted() const {return qDel;}
  ///
  void vMarkDeleted() {qDel = true;}
  ///
  void vMarkNotDeleted() {qDel = false;}
  ///
  bool qCellsSameShape() const
    {
      return ((pCL == pCInvalidCell || pCR == pCInvalidCell) ||
	      (pCL->eType() != pCR->eType()) );
    }
  ///
  bool qStructured() const {return qStruct;}
  ///
  void vMarkStructured() {qStruct = true;}
  ///
  void vClearStructured() {qStruct = false;}
  ///
  bool qWellSized() const {return qSizeOK;}
  ///
  void vMarkWellSized() {qSizeOK = true;}
  ///
  void vMarkIllSized() {qSizeOK = false;}
  ///
  bool qCheckForSwap() const {return qSwapCheck;}
  ///
  void vMarkForSwap() {qSwapCheck = true;}
  ///
  void vClearSwapFlag() {qSwapCheck = false;}
  ///
  bool qSwapAllowed() const;
  ///
  bool qIsBdryFace() const;
  ///
  bool qValid() const {return (this != pFInvalidFace);}
  ///
  virtual int iFullCheck() const;
  ///
  virtual bool qIsLocallyDelaunay() const {return false;}

  inline bool qHasVert(const Vert* const pV) const;
  ///
  virtual bool qHasCell(const Cell* const pC) const
    {return (pC == pCL || pC == pCR);}
/// Data member reporting
  Cell* pCCellLeft () const {assert(qValid()); return pCL;}
  ///
  Cell* pCCellRight() const {assert(qValid()); return pCR;}
  ///
  virtual Cell* pCCellOpposite(const Cell* const pC) const {
    assert(qValid());
    assert(pCL == pC || pCR == pC ||
	   (pCL == pCInvalidCell && pCR == pCInvalidCell));
    return ((pCL == pC) ? pCR : pCL);
  }
  ///
  virtual int iNumCells() const {
    int iN = 0;
    if (pCL->qValid() && !pCL->qIsBdryCell()) iN++;
    if (pCR->qValid() && !pCR->qIsBdryCell()) iN++;
    return iN;
  }
  int iNumFaces() const {return 0;}
  const Vert* pVVert(const int i) const {
    assert(qValid());
    assert(i>=0 && i<iNumVerts());
    return apVVerts[i];
  }
  Vert* pVVert(const int i) {
    assert(qValid());
    assert(i>=0 && i<iNumVerts());
    return apVVerts[i];
  }
  const Face *pFFace(const int i) const {
    assert(i == 0);
    return this;
  }
  Face *pFFace(const int i) {
    assert(i == 0);
    return this;
  }

  Cell *pCCell(const int i) {
    assert(i == 0 || i == 1);
    if (i == 0) {
      if (pCL->qValid()) {
	int eCTL = pCL->eType();
	if (eCTL == Cell::eTriBFace || eCTL == Cell::eIntTriBFace ||
	    eCTL == Cell::eQuadBFace || eCTL == Cell::eIntQuadBFace) {
	  return (pCR);
	}
	else
	  return (pCL);
      }
      else { // pCL is invalid
	return (pCR);
      }
    }
    else {
      if (pCL->qValid() && pCR->qValid()) {
	int eCTL = pCL->eType();
	int eCTR = pCR->eType();
	if (eCTL == Cell::eTriBFace ||
	    eCTR == Cell::eTriBFace ||
	    eCTL == Cell::eIntTriBFace ||
	    eCTR == Cell::eIntTriBFace ||
	    eCTL == Cell::eQuadBFace ||
	    eCTR == Cell::eQuadBFace ||
	    eCTL == Cell::eIntQuadBFace ||
	    eCTR == Cell::eIntQuadBFace) {
	  return (pCInvalidCell);
	}
	else return (pCR);
      }
      else return pCInvalidCell; // If one cell's invalid, then the
				 // valid one is returned for i=0, so
				 // return garbage here.
    }
    // Can never get here.
    assert(0);
    return pCInvalidCell;
 }

 const Cell *pCCell(const int i) const {
    assert(i == 0 || i == 1);
    if (i == 0) {
      if (pCL->qValid()) {
	int eCTL = pCL->eType();
	if (eCTL == Cell::eTriBFace || eCTL == Cell::eIntTriBFace ||
	    eCTL == Cell::eQuadBFace || eCTL == Cell::eIntQuadBFace) {
	  return (pCR);
	}
	else
	  return (pCL);
      }
      else { // pCL is invalid
	return (pCR);
      }
    }
    else {
      if (pCL->qValid() && pCR->qValid()) {
	int eCTL = pCL->eType();
	int eCTR = pCR->eType();
	if (eCTL == Cell::eTriBFace ||
	    eCTR == Cell::eTriBFace ||
	    eCTL == Cell::eIntTriBFace ||
	    eCTR == Cell::eIntTriBFace ||
	    eCTL == Cell::eQuadBFace ||
	    eCTR == Cell::eQuadBFace ||
	    eCTL == Cell::eIntQuadBFace ||
	    eCTR == Cell::eIntQuadBFace) {
	  return (pCInvalidCell);
	}
	else return (pCR);
      }
      else return pCInvalidCell; // If one cell's invalid, then the
				 // valid one is returned for i=0, so
				 // return garbage here.
    }
    // Can never get here.
    assert(0);
    return pCInvalidCell;
 }

 void vAllFaceHandles(GRUMMP_Entity* /*aHandle*/[]) const {}
 void vAllCellHandles(GRUMMP_Entity* aHandle[]) const;

/// Data member manipulation
  virtual void vAddCell(Cell* const pCNew);
  ///
  virtual void vRemoveCell(const Cell* const pCOld);
  ///
  virtual void vReplaceCell(const Cell* const pCOld, Cell* const pCNew);
  ///
  void vInterchangeCells(void);
  ///
  void vInterchangeCellsAndVerts(void);
  ///
  void vReplaceVert(Vert* const pVOld, Vert* const pVNew);
  ///
  void vAssign(Cell* const pCA, Cell* const pCB,
	       Vert* const pV0 = pVInvalidVert,
	       Vert* const pV1 = pVInvalidVert,
	       Vert* const pV2 = pVInvalidVert,
	       Vert* const pV3 = pVInvalidVert);
  void vSetRightCell(Cell* const pC) {pCR = pC;}
  void vSetLeftCell (Cell* const pC) {pCL = pC;}
/// Geometric computations
  virtual void vNormal(double * const adNorm) const = 0;
  ///
  virtual void vUnitNormal(double * const adNorm) const = 0;
  ///
  virtual void vProjOntoFace(double adPoint[]) const;
  ///
  virtual double dSize() const = 0;
  ///
  virtual void vCentroid(double adCent[]) const;
};

///
class QuadFace : public Face {
  /// Copy construction disallowed.
  QuadFace(const QuadFace& QF) : Face(QF) {assert(0);}
public:
  ///
  QuadFace(Cell* const pCA = pCInvalidCell,
	   Cell* const pCB = pCInvalidCell,
	   Vert* const pV0 = pVInvalidVert,
	   Vert* const pV1 = pVInvalidVert,
	   Vert* const pV2 = pVInvalidVert,
	   Vert* const pV3 = pVInvalidVert)
    : Face(pCA, pCB, 4, pV0, pV1, pV2, pV3) {}
  ///
  int iFullCheck() const;
  ///
  void vNormal(double * const adNorm) const;
  ///
  void vUnitNormal(double * const adNorm) const;
  ///
  eFaceType eType() const {return eQuadFace;}
  ///
  int iNumVerts() const {return 4;}
  ///
  double dSize() const;
#ifdef ITAPS
  int iTopologicalDimension() const {return 2;}
  int eEntType() const {return iBase_FACE;}
  int eEntTopology() const {return iMesh_QUADRILATERAL;}
#endif
  void vAllVertHandles(GRUMMP_Entity* aHandle[]) const {
    aHandle[0] = static_cast<Entity*>(apVVerts[0]);
    aHandle[1] = apVVerts[1];
    aHandle[2] = apVVerts[2];
    aHandle[3] = apVVerts[3];
  }
};

///
class TriFace : public Face {
  /// Copy construction disallowed.
  TriFace(const TriFace& TF) : Face(TF) {assert(0);}
public:
  ///
  TriFace(Cell* const pCA = pCInvalidCell,
	  Cell* const pCB = pCInvalidCell,
	  Vert* const pV0 = pVInvalidVert,
	  Vert* const pV1 = pVInvalidVert,
	  Vert* const pV2 = pVInvalidVert)
    : Face(pCA, pCB, 3, pV0, pV1, pV2) {}
  ///
  int iFullCheck() const;
  ///
  void vNormal(double * const adNorm) const;
  ///
  void vUnitNormal(double * const adNorm) const;
  ///
  void vCircumcenter(double adCircCent[]) const;
  ///
  double dCircumradius() const;
  ///
  double dSize() const;
  ///
  eFaceType eType() const {return eTriFace;}
  ///
  int iNumVerts() const {return 3;}
  ///
  eFaceCat eCategorizeFace(Vert*& pVVertA, Vert*& pVVertB,
			   Vert*& pVVertC, Vert*& pVVertD,
			   Vert*& pVVertE, TetCell* apTCTets[], int& iNTets,
			   Vert*& pVPivot0, Vert*& pVPivot1,
			   Vert*& pVOther, const double dMaxAngle = 0) const;
  ///
  bool qIsLocallyDelaunay() const;
#ifdef ITAPS
  int iTopologicalDimension() const {return 2;}
  int eEntType() const {return iBase_FACE;}
  int eEntTopology() const {return iMesh_TRIANGLE;}
#endif
  void vAllVertHandles(GRUMMP_Entity* aHandle[]) const {
    aHandle[0] = apVVerts[0];
    aHandle[1] = apVVerts[1];
    aHandle[2] = apVVerts[2];
  }
};

///
class EdgeFace : public Face {
  /// Copy construction disallowed.
protected:
  EdgeFace(const EdgeFace& EF) : Face(EF) {assert(0);}
public:
  ///
  EdgeFace(Cell* const pCA = pCInvalidCell,
	   Cell* const pCB = pCInvalidCell,
	   Vert* const pV0 = pVInvalidVert,
	   Vert* const pV1 = pVInvalidVert)
    : Face(pCA, pCB, 2, pV0, pV1) {}
  ///
  int iFullCheck() const;
  ///
  void vNormal(double * const adNorm) const;
  ///
  void vAssignVerts(Vert* const pV0, Vert* const pV1);
  ///
  void vUnitNormal(double * const adNorm) const;
  ///
  double dSize() const;
  ///
  eFaceType eType() const {return eEdgeFace;}
  ///
  int iNumVerts() const {return 2;}
  ///
  bool qIsLocallyDelaunay() const;
  ///
  void vCentroid(double adLoc[]) const;
#ifdef ITAPS
  int iTopologicalDimension() const {return 1;}
  int eEntType() const {return iBase_EDGE;}
  int eEntTopology() const {return iMesh_LINE_SEGMENT;}
#endif
  void vAllVertHandles(GRUMMP_Entity* aHandle[]) const {
    aHandle[0] = apVVerts[0];
    aHandle[1] = apVVerts[1];
  }
};

class MultiEdge : public EdgeFace {
  Cell *apCExtra[8];
  int iNCells;
  MultiEdge(const MultiEdge& ME) : EdgeFace(ME), iNCells(0) {assert(0);}
public:
  MultiEdge& operator=(const MultiEdge& ME) {
    if (this != &ME) {
      this->EdgeFace::operator=(ME);
      iNCells = ME.iNCells;
      for (int ii = 0; ii < iNCells; ii++) {
	apCExtra[ii] = ME.apCExtra[ii];
      }
    }
    return (*this);
  }
public:
  MultiEdge() : EdgeFace(), iNCells(0) {}
  // This constructor is actually never used.
//   MultiEdge(Vert* const pV0, Vert* const pV1, Cell **apCExtraIn,
//	    int iNCellsIn)
//     : EdgeFace(apCExtraIn[0], apCExtraIn[1], pV0, pV1), iNCells(iNCellsIn)
//     {
//       assert(iNCells > 2);
//       for (int i = 2; i < iNCells; i++)
//	apCExtra[i-2] = apCExtraIn[i];
//     }
  void vAssign(Vert* const pV0, Vert* const pV1, Cell **apCExtraIn,
	       int iNCellsIn);
  void vAddCell(Cell* const pCNew);
  void vRemoveCell(const Cell* const pCOld);
  void vReplaceCell(const Cell* const pCOld, Cell* const pCNew);
  const Cell *pCCell(const int i) const {
    assert(i >= 0 && i < iNCells);
    switch (i) {
    case 0: return pCL;
    case 1: return pCR;
    default: return apCExtra[i-2];
    }
  }
  Cell *pCCell(const int i) {
    return const_cast<Cell*>(static_cast<const Face*>(this)->pCCell(i));
  }
  bool qHasCell(const Cell* const pC) const;
  int iNumCells() const {return iNCells;}
  eFaceType eType() const {return eMultiEdge;}
  Cell* pCCellOpposite(const Cell* const pC) const;
  ///
  bool qIsLocallyDelaunay() const {assert(0); return false;} // Unsafe to use.
#ifdef ITAPS
  int iTopologicalDimension() const {return 1;}
  int eEntType() const {return iBase_EDGE;}
  int eEntTopology() const {return iMesh_LINE_SEGMENT;}
#endif
};

#ifndef BROKEN_INLINE

inline bool Face::qHasVert(const Vert* const pV) const {
//  fprintf(stdout, "Inside Face::qHasVert: %d\n", this);
  assert(qValid());
  for (int i = iNumVerts() - 1; i >= 0; i--)
    if (pV == apVVerts[i]) return (true);
  return (false);
}

#endif

///
Cell* pCCommonCell(Face* const pF0, Face* const pF1);

#endif
