#include "GR_assert.h"
#include "GR_Cell.h"
#include "GR_Face.h"
#include "GR_Vec.h"
#include "GR_Vertex.h"

Cell::Cell(const bool qIsInternalBdry, Face* const pFLeft,
	   Face* const pFRight) : CellSkel(), ppFFaces(NULL)
  // A constructor for bdry faces of all stripes.
{
  if (qIsInternalBdry) {
    ppFFaces = new Face*[2];
    ppFFaces[0] = pFLeft;
    ppFFaces[1] = pFRight;
  }
  else {
    ppFFaces = new Face*[1];
    ppFFaces[0] = pFLeft;
    assert(pFRight == pFInvalidFace);
  }
  // Set flags to default values.
  // Cell type will be explicitly set by derived class constructor.
}

Cell::Cell(const int iNFaces, const int iNVerts, Face** const ppFIn)
  : CellSkel(), ppFFaces(NULL)
{
  assert(iNFaces >= 2 && iNFaces <= 6);
#ifndef NDEBUG
  switch (iNFaces) {
  case 2: assert(iNVerts == 2); break;
  case 3: assert(iNVerts == 3); break;
  case 4: assert(iNVerts == 4); break;
  case 5: assert(iNVerts == 5 || iNVerts == 6); break;
  case 6: assert(iNVerts == 8); break;
  default: assert2(0, "Illegal number of faces for cell"); break;
  }
#endif
  // Cell type will be explicitly set by derived class constructor.
  ppFFaces = new Face*[iNFaces];
  int i;
  if (ppFIn) {
    for (i = 0; i < iNFaces; i++) {
      ppFFaces[i] = ppFIn[i];
      assert2(ppFFaces[i] != pFInvalidFace, "Uninitialized face");
    }
    vCanonicalizeFaceOrder();
  }
  else
    for (i = 0; i < iNFaces; i++)
      ppFFaces[i] = pFInvalidFace;
  vSetRegion(1);
  vMarkIllSized();
}

Cell::~Cell()
{
  if (ppFFaces != NULL) delete [] ppFFaces;
}

Cell& Cell::operator=(const Cell& C)
{
  assert(eType() == C.eType());
  if (this != &C) {
    int i;
    // The cell *this will no longer be connected to its previous faces,
    // if the cell still exists.  If it doesn't, then either the face is
    // deleted or the face has been modified so that it isn't attached
    // to the cell anymore.
    if (!qDeleted()) {
      for (i = iNumFaces() - 1; i >= 0; i--)
	if (ppFFaces[i]->qValid() && ppFFaces[i]->qHasCell(this))
	  ppFFaces[i]->vRemoveCell(this);
    }
    // Copy the face pointers and connect them to this cell
    for (i = iNumFaces() - 1; i >= 0; i--) {
      ppFFaces[i] = C.ppFFaces[i];
      if (ppFFaces[i]->qValid()) {
	ppFFaces[i]->vReplaceCell(&C, this);
	assert(ppFFaces[i]->pCCellLeft() == this ||
	       ppFFaces[i]->pCCellRight() == this);
      }
    }
    vCopyAllFlags(C);
  }
  return (*this);
}

int Cell::iFullCheck() const
{
  if (!qValid() || qDeleted()) {
    return 0;
  }
  bool qOkay = true;

  int iInvalid = 0;
  for (int i = 0; qOkay && i < iNumFaces(); i++) {
    const Face* pFThisFace = pFFace(i);
    if (pFThisFace->qValid()) {
      qOkay = qOkay && pFThisFace->qHasCell(this);
    }
    else {
      iInvalid++;
    }
  }

  if (iInvalid == 0 && qOkay) return 1;
  else if (iInvalid == iNumFaces() && qOkay) return 2;
  else return 0;
}

const Face* Cell::pFFace(const int i) const
{
  assert(qValid());
  assert2(i >= 0 && i < iNumFaces(), "Index out of range");
  return ppFFaces[i];
}

Face* Cell::pFFace(const int i)
{
  assert(qValid());
  assert2(i >= 0 && i < iNumFaces(), "Index out of range");
  return ppFFaces[i];
}

bool Cell::qHasFace(const Face* pF) const
{
  assert(qValid());
  assert(pF->qValid());
  for (int i = 0; i < iNumFaces(); i++)
    if (ppFFaces[i] == pF) return(true);
  return(false);
}

const Face* Cell::pFFaceOpposite(const Vert* const pV) const
{assert(0 && pV->qValid()); return(pFInvalidFace);}
Face* Cell::pFFaceOpposite(const Vert* const pV)
{assert(0 && pV->qValid()); return(pFInvalidFace);}

Vert* Cell::pVVertOpposite(const Face* const pF) const
{assert(0 && pF->qValid()); return(pVInvalidVert);}

void Cell::vAddFace(Face* const pFNew, const int iInd)
{
  assert(qValid());
  assert2(pFNew->qValid(), "Tried to add non-existent face");
  if (iInd == -1) { // No face index specified
    int iNull = -1;
    for (int i = 0; i < iNumFaces() && iNull == -1; i++) {
      assert2(pFNew != ppFFaces[i], "Face already exists for this cell");
      if (! ppFFaces[i]->qValid() ) {
	iNull = i;
      }
    }
    assert2(iNull != -1, "No blank faces for this cell.");
    ppFFaces[iNull] = pFNew;
  }
  else {
    assert(iInd >= 0 && iInd < iNumFaces());
    assert2(ppFFaces[iInd] == pFInvalidFace,
	    "Face with that index exists already");
    ppFFaces[iInd] = pFNew;
  }
}

void Cell::vReplaceFace(const Face* const pFOld, Face* const pFNew)
{
  assert(qValid());
  assert2(pFOld->qValid(), "Tried to replace non-existent face");
  assert2(pFNew->qValid(), "Tried to add non-existent face");
  int iRepl = -1;
  for (int i = 0; i < iNumFaces(); i++) {
    if (ppFFaces[i] == pFOld) iRepl = i;
  }
  assert2(iRepl >= 0, "Tried to replace face which doesn't bound this cell");
  ppFFaces[iRepl] = pFNew;
  vMarkIllSized();
}

void Cell::vRemoveFace(const Face* const pFOld)
{
  if (!qValid()) return; // A useful short-circuit when destroying part
			 // of a mesh.  This way there is no need to be
			 // careful about the order in which objects are
			 // killed.
  assert2(pFOld->qValid(), "Tried to remove non-existent face");
  int iRepl = -1;
  for (int i = 0; i < iNumFaces(); i++) {
    if (ppFFaces[i] == pFOld) iRepl = i;
  }
  assert2(iRepl >= 0, "Tried to remove face which doesn't bound this cell");
  ppFFaces[iRepl] = pFInvalidFace;
  vMarkIllSized();
}

void Cell::vAssign(Face* const apF[])
{
  assert(qValid());
  assert(apF != NULL);
  for (int i = 0; i < iNumFaces(); i++) {
    ppFFaces[i] = apF[i];
    assert2(ppFFaces[i] != pFInvalidFace, "Uninitialized face");
  }
  vCanonicalizeFaceOrder();
  vMarkIllSized();
}

void Cell::vAssign(Face* const pF0, Face* const pF1, Face* const pF2,
		   Face* const pF3, Face* const pF4, Face* const pF5)
     // All alternate interface to vAssign, so that stuff doesn't have
     // to be loaded into an array by the user.
{
  assert(qValid());
  Face *apF[] = {pF0, pF1, pF2, pF3, pF4, pF5};
  vAssign(apF);
}

void Cell::vClearFaces()
{
  assert(qValid());
  for (int i = 0; i < iNumFaces(); i++)
    ppFFaces[i] = pFInvalidFace;
}

const Face* SimplexCell::pFFaceOpposite(const Vert* const pV) const
{
  assert(iFullCheck());
  assert(pV->qValid());

  for (int i = 0; i < iNumFaces(); i++) {
    assert(pFFace(i)->qValid());
    if (! pFFace(i)->qHasVert(pV)) return(pFFace(i));
  }
  assert2(0, "Strange. All faces of a simplex are incident on a single vert.");
  return(pFInvalidFace);
}

Face* SimplexCell::pFFaceOpposite(const Vert* const pV)
{
  return const_cast<Face*>(static_cast<const SimplexCell*>(this)
			   ->pFFaceOpposite(pV));
}

Vert* SimplexCell::pVVertOpposite(const Face* const pFFaceIn) const
{
  assert(iFullCheck());
  assert(pFFaceIn->qValid());

  int iNVerts = iNumVerts();
  Face* pF = (ppFFaces[0] == pFFaceIn) ? ppFFaces[1] : ppFFaces[0];
  GRUMMP_Entity* apV[3];
  pFFaceIn->vAllVertHandles(apV);
  for (int i = 0; i < iNVerts - 1; i++) {
    Vert *pVCand = pF->pVVert(i);
    bool qFoundMatch = false;
    for (int j = 0; j < iNVerts - 1 && !qFoundMatch; j++) {
      qFoundMatch = (apV[j] == pVCand);
    }
    if (!qFoundMatch) return pVCand;
  }
  assert2(0, "Strange. All faces of a simplex are incident on a single vert.");
  return(pVInvalidVert);
}

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

void Cell::vCentroid(double adCent[]) const
{
  // This is a really crappy centroid calculation.  More precisely, it's
  // correct for tris and tets, and bogus for other cell types.  So this
  // should probably be moved to SimplexCell::vCentroid.  Even then,
  // it'll be obsolete for general mesh geometry...
  assert(qValid());
  // assert(iFullCheck() == 1);
  int iDim = pVVert(0)->iSpaceDimen();
  switch (iDim) {
  case 2:
    {
      adCent[0] = adCent[1] = 0;
      for (int i = 0; i < iNumVerts(); i++) {
	adCent[0] += pVVert(i)->dX();
	adCent[1] += pVVert(i)->dY();
      }
      adCent[0] /= iNumVerts();
      adCent[1] /= iNumVerts();
    }
    break;
  case 3:
    {
      adCent[0] = adCent[1] = adCent[2] = 0;
      for (int i = 0; i < iNumVerts(); i++) {
	adCent[0] += pVVert(i)->dX();
	adCent[1] += pVVert(i)->dY();
	adCent[2] += pVVert(i)->dZ();
      }
      adCent[0] /= iNumVerts();
      adCent[1] /= iNumVerts();
      adCent[2] /= iNumVerts();
    }
    break;
  default:
    assert2(0, "Unknown number of dimensions");
  }
}

Face *pFCommonFace(Cell* const pC0, Cell* const pC1)
{
  if (pC0->iFullCheck() != 1 || pC1->iFullCheck() != 1)
    return (pFInvalidFace);
  for (int iF0 = 0; iF0 < pC0->iNumFaces(); iF0++) {
    Face *pF0 = pC0->pFFace(iF0);
    for (int iF1 = 0; iF1 < pC1->iNumFaces(); iF1++)
      if (pF0 == pC1->pFFace(iF1)) return pF0;
  }
  return pFInvalidFace;
}

void Cell::vAllFaceHandles(GRUMMP_Entity* aHandles[]) const {
  for (int i = iNumFaces() - 1; i >= 0; i--) {
    aHandles[i] = ppFFaces[i];
  }
}
