#ifndef GR_CellCV
#define GR_CellCV 1

#include "GR_config.h"

#include "GR_CellSkel.h"
#include "GR_Vertex.h"

static CellCV* const pCInvalidCellCV = static_cast<CellCV*>(NULL);

///
class CellCV : public CellSkel {
private:
  // These don't make sense.
  const Face* pFFace(const int) const {assert(0); return pFInvalidFace;}
  const Cell* pCCell(const int) const {assert(0); return pCInvalidCell;}
public:
  /// Override copy constructor
  CellCV(const CellCV& CCV) : CellSkel(), iNV(CCV.iNV),
    ppVVerts(new Vert*[iNV])
    {
      for (int ii = 0; ii < iNV; ii++) {
	ppVVerts[ii] = CCV.ppVVerts[ii];
      }
    }
protected:
  CellCV& operator=(const CellCV& CCV)
    {
      if (this != &CCV) {
	delete [] ppVVerts;
	iNV = CCV.iNV;
	ppVVerts = new Vert*[iNV];
	for (int ii = 0; ii < iNV; ii++) {
	  ppVVerts[ii] = CCV.ppVVerts[ii];
	}
      }
      return (*this);
    }
protected:
  ///
  int iNV;
  ///
  Vert** ppVVerts;
public:
  ///
  inline CellCV(const int iNVerts, Vert** const ppVIn = 0, int iDim = 2);
  ///
  inline virtual ~CellCV();
  ///
  bool qValid() const {return(this != pCInvalidCellCV);}
  ///
  inline int iFullCheck() const;
  ///
  int iNumVerts() const {return iNV;}
  ///
  int iNumFaces() const {
    if (pVVert(0)->iSpaceDimen() == 2) {
      return (iNumVerts());
    }
    else {
      switch (iNumVerts()) {
      case 3: return 3; // Tri in surface mesh
      case 4: return 4; // Tet; or quad in surface mesh
      case 5: return 5; // Pyr
      case 6: return 5; // Prism
      case 8: return 6; // Hex
      default: assert(0); return -1;
      }
    }
  }
  int iNumCells() const {return 1;}
  inline virtual const Vert* pVVert(const int i) const;
  inline virtual Vert* pVVert(const int i) {
    return (const_cast<Vert*>(static_cast<const CellCV*>(this)->pVVert(i)));
  }
  void vAllVertHandles(GRUMMP_Entity* aHandles[]) const {
    for (int i=0; i < iNV; i++)
      aHandles[i] = ppVVerts[i];
  }
  void vAllFaceHandles(GRUMMP_Entity*[]) const {assert(0);}
  void vAllCellHandles(GRUMMP_Entity*[]) const {assert(0);}
#ifdef ITAPS
  int iTopologicalDimension() const {return eEntType();}
  int eEntType() const {
    if (pVVert(0)->iSpaceDimen() == 2 || iNumVerts() == 3)
      // Incorrect for QuadCellCV; must override there.  For QuadCV's
      // that have been declared as CellCV's, there doesn't seem to be
      // any immediate hope.
      return iBase_FACE;
    else
      return iBase_REGION;
  }
  int eEntTopology() const {
    switch (iNumVerts()) {
    case 2: return iMesh_LINE_SEGMENT;
    case 3: return iMesh_TRIANGLE;
      // Incorrect for QuadCellCV; must override there.  For QuadCV's
      // that have been declared as CellCV's, there doesn't seem to be
      // any immediate hope.
    case 4: return iMesh_TETRAHEDRON;
    case 5: return iMesh_PYRAMID;
    case 6: return iMesh_PRISM;
    case 8: return iMesh_HEXAHEDRON;
    default:
      assert(0);
      return iMesh_ALL_TOPOLOGIES; // Bogus!
    }
  }
#endif
  ///
  inline bool qHasVert(const Vert* pV) const;
  ///
  inline void vAssign(const int iNVerts, Vert** const ppVIn);
  ///
  inline void vReplaceVert(Vert* const pVOld, Vert* const pVNew);
  ///
  virtual double dSize() const;
  ///
  virtual void vAllDihed(double adDihed[], int* const piNDihed) const;
  ///
  virtual void vAllSolid(double adSolid[], int* const piNSolid) const;
};

#ifndef BROKEN_INLINE

inline CellCV::CellCV(const int iNVerts, Vert** const ppVIn, int iDim) :
  iNV(iNVerts), ppVVerts(NULL)
{
  assert(iNVerts >= 2 && iNVerts <= 8);
  ppVVerts = new Vert*[iNVerts];
  int i;
  if (ppVIn) 
    for (i = 0; i < iNVerts; i++) {
      ppVVerts[i] = ppVIn[i];
      assert2(ppVVerts[i] != pVInvalidVert, "Uninitialized vert");
    }
  else
    for (i = 0; i < iNVerts; i++) 
      ppVVerts[i] = pVInvalidVert;
  switch (iNVerts) {
  case 2:
    break;
  case 3:
    uiType = eTriCV;
    break;
  case 4:
    if (iDim == 2) 
      uiType = eQuadCV;
    else
      uiType = eTetCV;
    break;
  case 5:
    uiType = ePyrCV;
    break;
  case 6:
    uiType = ePrismCV;
    break;
  case 8:
    uiType = eHexCV;
    break;
  default:
    break;
  }
}

#endif

inline CellCV::~CellCV()
{
  if (ppVVerts) {
    delete [] ppVVerts;
    ppVVerts = NULL;
  }
}

inline const Vert* CellCV::pVVert(const int i) const
{
  assert2(i >= 0 && i < iNV, "Index out of range");
  return ppVVerts[i];
}

#ifndef BROKEN_INLINE

inline int CellCV::iFullCheck() const
{
  int iRetVal = qValid();
  int iNVerts = iNumVerts();
  for (int i = 0; iRetVal && i < iNVerts; i++) {
    const Vert* pVThisVert = pVVert(i);
    iRetVal = iRetVal && pVThisVert->qValid();
  }
  return (iRetVal);
}

inline bool CellCV::qHasVert(const Vert* pV) const
{
  assert(pV->qValid());
  for (int i = 0; i < iNV; i++)
    if (ppVVerts[i] == pV) return(true);
  return(false);
}

inline void CellCV::vAssign(const int iNVerts, Vert** const ppVIn)
{
  assert(iNVerts >= 2 && iNVerts <= 8);
  if (iNV != iNVerts) {
    delete [] ppVVerts;
    iNV = iNVerts;
    ppVVerts = new Vert*[iNVerts];
  }
  int i;
  for (i = 0; i < iNVerts; i++) {
    ppVVerts[i] = ppVIn[i];
    assert2(ppVVerts[i] != pVInvalidVert, "Uninitialized vert");
  }
}

inline void CellCV::vReplaceVert(Vert* const pVOld, Vert* const pVNew)
{
  int i, iRep = -1;
  for (i = 0; i < iNV; i++) {
    if (ppVVerts[i] == pVOld) iRep = i;
    assert(ppVVerts[i] != pVNew);
  }
  assert(iRep != -1);
  ppVVerts[iRep] = pVNew;
}

#endif
 
///
class TriCellCV : public CellCV {
public:
  ///
  TriCellCV(Vert** const ppVIn = 0) : CellCV(3, ppVIn, 2) {}
  TriCellCV(Vert * const pVA, Vert * const pVB,
	    Vert * const pVC) : CellCV(3, NULL, 2)
    {
      ppVVerts[0] = pVA;
      ppVVerts[1] = pVB;
      ppVVerts[2] = pVC;
    }      
  ///
  void vAssign(Vert* const pV0, Vert* const pV1, Vert* const pV2)
    {ppVVerts[0] = pV0; ppVVerts[1] = pV1; ppVVerts[2] = pV2;}
  void vSwitchOrientation()
    {
      Vert *pVTmp = ppVVerts[2];
      ppVVerts[2] = ppVVerts[1];
      ppVVerts[1] = pVTmp;
    }
};

///
class QuadCellCV : public CellCV {
public:
  ///
  QuadCellCV(Vert** const ppVIn = 0) : CellCV(4, ppVIn, 2) {}
  void vSwitchOrientation()
    {
      Vert *pVTmp = ppVVerts[3];
      ppVVerts[3] = ppVVerts[0];
      ppVVerts[0] = pVTmp;

      pVTmp = ppVVerts[2];
      ppVVerts[2] = ppVVerts[1];
      ppVVerts[1] = pVTmp;
    }
#ifdef ITAPS
  int eEntType() const {return iBase_FACE;}
  int eEntTopology() const { return iMesh_QUADRILATERAL;}
#endif
};

///
class TetCellCV : public CellCV {
public:
  ///
  TetCellCV(Vert** const ppVIn = 0) : CellCV(4, ppVIn, 3) {}
  TetCellCV(Vert * const pVA, Vert * const pVB,
	    Vert * const pVC, Vert * const pVD) : CellCV(4, NULL, 3)
    {
      ppVVerts[0] = pVA;
      ppVVerts[1] = pVB;
      ppVVerts[2] = pVC;
      ppVVerts[3] = pVD;
    }      
  ~TetCellCV() {}
  void vCircumcenter(double adCircCent[3]) const;
  double dSize() const;
  void vAllDihed(double adDihed[], int* const piNDihed) const;
  void vAllSolid(double adSolid[], int* const piNSolid) const;
};

///
class PyrCellCV : public CellCV {
public:
  ///
  PyrCellCV(Vert** const ppVIn = 0) : CellCV(5, ppVIn, 3) {}
};

///
class PrismCellCV : public CellCV {
public:
  ///
  PrismCellCV(Vert** const ppVIn = 0) : CellCV(6, ppVIn, 3) {}
};

///
class HexCellCV : public CellCV {
public:
  ///
  HexCellCV(Vert** const ppVIn = 0) : CellCV(8, ppVIn, 3) {}
};
#endif
