#ifndef GR_Cell
#define GR_Cell 1

#include "GR_config.h"
#include "GR_CellSkel.h"

class Cell : public CellSkel {
protected:
  /// Copy construction disallowed.
  Cell(const Cell&) : CellSkel(), ppFFaces(NULL) {
    assert(0);
    qAngleSelected = false;
  }
  ///
  Face** ppFFaces;
protected:
  /// Constructor for all types of bdry faces; all args are provided.
  Cell(const bool qIsInternalBdry, Face* const pFLeft, Face* const pFRight);
  ///
  Cell(const int iNFaces, const int iNVerts, Face** const ppFIn = 0);
public:
  ///
  virtual ~Cell();
  ///
  Cell& operator=(const Cell& C);
  ///
  // Asks if it is a bdry cell.. return false by default
  virtual bool qIsBdryCell() const {return false;}
  ///
  virtual int iFullCheck() const;
   ///
  virtual int iNumFaces() const = 0;
  ///
  virtual int iNumVerts() const = 0;
  int iNumCells() const {return 0;}
  ///
  virtual const Vert* pVVert(const int i) const = 0;
  virtual Vert* pVVert(const int i) {return this->Entity::pVVert(i);}
  ///
  const Face* pFFace(const int i) const;
  Face* pFFace(const int i);
  void vAllFaceHandles(GRUMMP_Entity* aHandles[]) const;
  ///
  const Cell* pCCell(const int i) const {
    assert(i == 0);
    return this;
  }
  Cell* pCCell(const int i) {
    assert(i == 0);
    return this;
  }
  void vAllCellHandles(GRUMMP_Entity* []) const {/* Do nothing */}
  ///
  bool qHasFace(const Face* pF) const;
  ///
  virtual bool qHasVert(const Vert* pV) const;
  ///
  virtual const Face* pFFaceOpposite(const Vert* const pVVert) const;
  virtual Face* pFFaceOpposite(const Vert* const pVVert);
  ///
  virtual Vert* pVVertOpposite(const Face* const pFFace) const;
  ///
  virtual void vVecSize(double []) const {assert(0);}
  ///
  virtual double dSize() const = 0;
  ///
  virtual void vAllDihed(double [], int* const piN) const
  {*piN = 0;} // Fail silently; pyrs, prisms, and hexes currently get skipped
  virtual void vAllSolid(double [], int* const piN) const
  {*piN = 0;} // Fail silently; pyrs, prisms, and hexes currently get skipped 
  virtual bool qIsClosed() const = 0;
  ///
  virtual double dShortestEdgeLength() const {assert(0); return -1.e300;}
  ///
  void vAddFace(Face* const pFNew, const int iFaceIndex = -1);
  ///
  void vReplaceFace(const Face* const pFOld, Face* const pFNew);
  ///
  void vRemoveFace(const Face* const pFOld);
  ///
  void vAssign(Face* const apF[]);
  // Must allow as few as one valid face, because bdry faces have only
  // one face attached to them, and bdry faces think they're cells.
  void vAssign(Face* const pF0,
	       Face* const pF1 = pFInvalidFace,
	       Face* const pF2 = pFInvalidFace,
	       Face* const pF3 = pFInvalidFace,
	       Face* const pF4 = pFInvalidFace,
	       Face* const pF5 = pFInvalidFace);
  /// This next one should probably be private, since it's only going to
  /// be called by the cell assignment function...
  virtual void vCanonicalizeFaceOrder() {}
  ///
  void vClearFaces();
  ///
  virtual void vCentroid(double adCent[]) const;
};

///
class SimplexCell : public Cell {
protected:
  /// Copy construction disallowed.
  SimplexCell(const SimplexCell& SC) : Cell(SC) {assert(0);}
  ///
  SimplexCell(const int iNFaces, const int iNVerts, Face** const ppFIn)
    : Cell(iNFaces, iNVerts, ppFIn) {}
public:
  ///
  const Face* pFFaceOpposite(const Vert* const pVVert) const;
  Face* pFFaceOpposite(const Vert* const pVVert);
  ///
  Vert* pVVertOpposite(const Face* const pFFace) const;
  ///
  virtual void vCircumcenter(double adRes[]) const = 0;
  ///
  virtual double dCircumradius() const = 0;
  ///
  virtual bool qCircumscribes(const double adPoint[]) const = 0;
  ///
  virtual void vContaincenter(double adRes[]) const = 0;
  ///
  virtual double dContainradius() const = 0;
  ///
  virtual void vBarycentrics(const double adPoint[], double adBary[])
       const = 0;
};

///
class TetCell : public SimplexCell {
  /// Copy construction disallowed.
  TetCell(const TetCell& TC) : SimplexCell(TC) {assert(0);}
public:
  ///
  TetCell(Face** const ppFIn = 0) : SimplexCell(4, 4, ppFIn)
    { vSetType(Cell::eTet); }
/// Declared in Cell
  int iNumFaces() const {return 4;}
  ///
  int iNumVerts() const {return 4;}
  ///
  virtual const Vert* pVVert(const int i) const;
  virtual Vert* pVVert(const int i) {return this->Entity::pVVert(i);}
  void vAllVertHandles(GRUMMP_Entity* aHandles[]) const;
  ///
  bool qIsClosed() const;
  ///
  virtual double dSize() const;
  ///
  void vAllDihed(double adDihed[], int* const piNDihed) const;
  ///
  void vAllSolid(double adSolid[], int* const piNSolid) const;
/// Declared in SimplexCell
  void vCircumcenter(double adRes[]) const;
  ///
  double dCircumradius() const;
  ///
  bool qCircumscribes(const double adPoint[]) const;
  ///
  void vContaincenter(double adRes[]) const;
  ///
  double dContainradius() const;
  ///
  void vBarycentrics(const double adPoint[], double adBary[]) const;
  ///
  double dShortestEdgeLength() const;
#ifdef ITAPS
  int iTopologicalDimension() const {return 3;}
  int eEntType() const {return iBase_REGION;}
  int eEntTopology() const {return iMesh_TETRAHEDRON;}
#endif
};

///
class TriCell : public SimplexCell {
  /// Copy construction disallowed.
  TriCell(const TriCell& TC) : SimplexCell(TC) {assert(0);}
public:
  ///
  TriCell(Face** const ppFIn = 0) : SimplexCell(3, 3, ppFIn)
    { vSetType(Cell::eTriCell); }
/// Declared in Cell
  int iNumFaces() const {return 3;}
  ///
  int iNumVerts() const {return 3;}
  ///
  virtual const Vert* pVVert(const int i) const;
  virtual Vert* pVVert(const int i) {return this->Entity::pVVert(i);}
  void vAllVertHandles(GRUMMP_Entity* aHandles[]) const;
  ///
  double dSize() const;
  ///
  bool qIsClosed() const;
  ///
  void vAllDihed(double adDihed[], int* const piNDihed) const;
  ///
  void vAllSolid(double adSolid[], int* const piNSolid) const;
/// Declared in SimplexCell
  void vCircumcenter(double adRes[]) const;
  ///
  double dCircumradius() const;
  ///
  bool qCircumscribes(const double adPoint[]) const;
  ///
  void vContaincenter(double adRes[]) const;
  ///
  double dContainradius() const;
  ///
  void vBarycentrics(const double adPoint[], double adBary[]) const;
  ///
  double dShortestEdgeLength() const;
  /// Things peculiar to surface cells
  void vVecSize(double adRes[]) const;
#ifdef ITAPS
  int iTopologicalDimension() const {return 2;}
  int eEntType() const {return iBase_FACE;}
  int eEntTopology() const {return iMesh_TRIANGLE;}
#endif
};

///
class QuadCell : public Cell {
  /// Copy construction disallowed.
  QuadCell(const QuadCell& QC) : Cell(QC) {assert(0);}
public:
  ///
  QuadCell(Face** const ppFIn = 0) : Cell(4, 4, ppFIn)
    { vSetType(Cell::eQuadCell); }
/// Declared in Cell
  int iNumFaces() const {return 4;}
  ///
  int iNumVerts() const {return 4;}
  ///
  virtual const Vert* pVVert(const int i) const;
  virtual Vert* pVVert(const int i) {return this->Entity::pVVert(i);}
  void vAllVertHandles(GRUMMP_Entity* /*aHandles*/[]) const {assert(0);}
  ///
  double dSize() const;
  ///
  bool qIsClosed() const;
  ///
  void vAllDihed(double adDihed[], int* const piNDihed) const;
  ///
  void vAllSolid(double adSolid[], int* const piNSolid) const;
  /// Things peculiar to surface cells
  void vVecSize(double adRes[]) const;
#ifdef ITAPS
  int iTopologicalDimension() const {return 2;}
  int eEntType() const {return iBase_FACE;}
  int eEntTopology() const {return iMesh_QUADRILATERAL;}
#endif
  // Get the faces into cyclic order!
  virtual void vCanonicalizeFaceOrder();
};

///
class PyrCell : public Cell {
  /// Copy construction disallowed.
  PyrCell(const PyrCell& PCIn) : Cell(PCIn) {assert(0);}
public:
  ///
  PyrCell(Face** const ppFIn = 0) : Cell(5, 5, ppFIn)
    { vSetType(Cell::ePyr); }
/// Declared in Cell
  int iNumFaces() const {return 5;}
  ///
  int iNumVerts() const {return 5;}
  ///
  virtual const Vert* pVVert(const int i) const;
  virtual Vert* pVVert(const int i) {return this->Entity::pVVert(i);}
  void vAllVertHandles(GRUMMP_Entity* /*aHandles*/[]) const {assert(0);}
  ///
  double dSize() const;
  ///
  bool qIsClosed() const;
#ifdef ITAPS
  int iTopologicalDimension() const {return 3;}
  int eEntType() const {return iBase_REGION;}
  int eEntTopology() const {return iMesh_PYRAMID;}
#endif
};

///
class PrismCell : public Cell {
  /// Copy construction disallowed.
  PrismCell(const PrismCell& PCIn) : Cell(PCIn) {assert(0);}
public:
  ///
  PrismCell(Face** const ppFIn = 0) : Cell(5, 6, ppFIn)
    { vSetType(Cell::ePrism); }
/// Declared in Cell
  int iNumFaces() const {return 5;}
  ///
  int iNumVerts() const {return 6;}
  ///
  virtual const Vert* pVVert(const int i) const;
  virtual Vert* pVVert(const int i) {return this->Entity::pVVert(i);}
  void vAllVertHandles(GRUMMP_Entity* /*aHandles*/[]) const {assert(0);}
  ///
  double dSize() const;
  ///
  bool qIsClosed() const;
#ifdef ITAPS
  int iTopologicalDimension() const {return 3;}
  int eEntType() const {return iBase_REGION;}
  int eEntTopology() const {return iMesh_PRISM;}
#endif
};

///
class HexCell : public Cell {
  /// Copy construction disallowed.
  HexCell(const HexCell& HC) : Cell(HC) {assert(0);}
public:
  ///
  HexCell(Face** const ppFIn = 0) : Cell(6, 8, ppFIn)
    { vSetType(Cell::eHex); }
/// Declared in Cell
  int iNumFaces() const {return 6;}
  ///
  int iNumVerts() const {return 8;}
  ///
  virtual const Vert* pVVert(const int i) const;
  virtual Vert* pVVert(const int i) {return this->Entity::pVVert(i);}
  void vAllVertHandles(GRUMMP_Entity* /*aHandles*/[]) const {assert(0);}
  ///
  double dSize() const;
  ///
  bool qIsClosed() const;
#ifdef ITAPS
  int iTopologicalDimension() const {return 3;}
  int eEntType() const {return iBase_REGION;}
  int eEntTopology() const {return iMesh_HEXAHEDRON;}
#endif
};

///
Face* pFCommonFace(Cell* const pC0, Cell* const pC1);

#endif
