#include <queue>

#include "GR_config.h"
#include "GR_assert.h"
#include "GR_Mesh.h"
#include "GR_Vertex.h"
#include "GR_Face.h"

#ifdef BROKEN_INLINE
// Compute hint faces for all vertices
void Mesh::vClearAllHintFaces() const
{
  int i;
  for (i = 0; i < iNumVerts(); i++)
    pVVert(i)->vClearHintFace();
}

void Mesh::vSetAllHintFaces() const
{
  vClearAllHintFaces();
  // Have to clear vertex hints -before- checking validity, else bad
  // hints will cause an irrelevant assertion.
  assert(qValid());

  int i;
  for (i = 0; i < iNumFaces(); i++) {
    Face* pF = pFFace(i);
    if (pF->qDeleted() || !(pF->iFullCheck())) continue;
    bool qIsBFace = pF->qIsBdryFace();
    for (int ii = pF->iNumVerts() - 1; ii >= 0; ii --) {
      Vert* pV = pF->pVVert(ii);
      if (pV != pVInvalidVert) {
	if (!(pV->pFHintFace()->qValid()) || qIsBFace)
	  pV->vSetHintFace(pF);
      }
    }
  }
}

#endif

void vNeighborhood(const Vert* const pVert, std::set<Cell*>& spCInc,
		   std::set<Vert*>& spVNeigh, std::set<BFace*>* pspBFInc,
		   bool *pqBdryVert, std::set<Face*>* pspFNearby)
{
  Face *pF = pVert->pFHintFace();
  assert(pF->qValid() && pF->qHasVert(pVert));

  spCInc.clear();
  spVNeigh.clear();
  if (pspBFInc) {
    pspBFInc->clear();
    assert(pqBdryVert != NULL);
    (*pqBdryVert) = false;
  }
  if (pspFNearby) pspFNearby->clear();

  Cell *pC = pF->pCCellLeft();
  if (pC->qIsBdryCell()) {
    pC = pF->pCCellOpposite(pC);
  }
  spCInc.insert(pC);
  std::queue<Cell*> qpC;
  qpC.push(pC);

  // Keep adding cells incident on pVert until they're all there.
  // spCInc.iLength() increases as cells are added, but the process
  // is guaranteed to exit.  In the process, create a list of pointers
  // to all the neighboring verts of pVert and all the faces incident on
  // pVert. The latter list will have to be trimmed later by removing faces
  // opposite to pVert.
  while (!qpC.empty()) {
    pC = qpC.front();
    qpC.pop();
    // Add the parts of this cell to the appropriate lists.
    for (int iV = pC->iNumVerts() - 1; iV >= 0; iV--) {
	Vert *pVAdd = pC->pVVert(iV);
	if (!pVAdd->qDeleted())
	  spVNeigh.insert(pVAdd);
    }

    for (int iF = pC->iNumFaces() - 1; iF >= 0; iF--) {
      pF = pC->pFFace(iF);
      if (pspFNearby) {
	pspFNearby->insert(pF);
      }
      if (pF->qHasVert(pVert)) {
	// This Face has the right vertex, so the cell behind it does, too.
	Cell *pCCand = pF->pCCellOpposite(pC);
	assert(pCCand->qHasVert(pVert));
	if (pCCand->qIsBdryCell() && pspBFInc) {
	  pspBFInc->insert(dynamic_cast<BFace*>(pCCand));
	}
	// If BFaces  get added to this list, then we can walk right
	// across internal bdry faces.
	if (spCInc.count(pCCand) == 0) {
	  // The insertion is safe anyway, but the queue push is not!
	  spCInc.insert(pCCand);
	  qpC.push(pCCand);
	}
      }
    } // Done checking all faces for this cell
  } // Done checking all cells

  // Clean up vertex list
  spVNeigh.erase(const_cast<Vert*>(pVert));

  // Clean up cell list by removing any BFaces from it
  std::set<Cell*>::iterator iter;
  for (iter = spCInc.begin(); iter != spCInc.end(); ) {
    pC = *iter;
    if (pC->eType() == Cell::eBdryEdge ||
	pC->eType() == Cell::eIntBdryEdge ||
	pC->eType() == Cell::eTriBFace ||
	pC->eType() == Cell::eQuadBFace ||
	pC->eType() == Cell::eIntTriBFace ||
	pC->eType() == Cell::eIntQuadBFace) {
      std::set<Cell*>::iterator iterTmp = iter++;
      spCInc.erase(iterTmp);
    }
    else iter++;
  }

  // Identify whether this is a bdry vert
  if (pspBFInc) {
    if (pqBdryVert)
      *pqBdryVert = (!pspBFInc->empty());
  }
}

int Mesh::iQueueEncroachedBdryEntities(InsertionQueue& IQ) const
{
  // Make a set of all BFaces.
  std::set<BFace*> spBF;
  int i;
  for (i = 0; i < iNumTotalBdryFaces(); i++) {
    spBF.insert(pBFBFace(i));
  }
  return iQueueEncroachedBdryEntities(IQ, spBF);
}

bool Mesh::qIsLocallyDelaunay() const
{
  for (int iF = 0; iF < iNumFaces(); iF++) {
    Face *pF = pFFace(iF);
    if (!pF->qDeleted() &&
	!pF->qIsLocallyDelaunay())
      return false;
  }
  return true;
}

void Mesh::vSetLengthScale(const double dLen)
{
  dLipschitzAlpha = 1;
  dMeshResolution = 1;
  for (int iV = 0; iV < iNumVerts(); iV++) {
    Vert *pV = pVVert(iV);
    pV->vSetLS(dLen);
  }
}

void Mesh::vCheckEdgeLengths() const
{
  int iNEdges = 0;
  double dMin = LARGE_DBL;
  double dMax = 0;
  double dSum = 0;

  for (int iV = 0; iV < iNumVerts(); iV++) {
    Vert *pV = pVVert(iV);
    if (pV->qDeleted()) continue;
    std::set<Vert*> spVNeigh;
    {
      std::set<Cell*> spCTmp;
      vNeighborhood(pV, spCTmp, spVNeigh);
    }
    double dLenScale;

    // Use the Vertex-stored length scale
    dLenScale = pV->dLS();

    std::set<Vert*>::iterator iterV;
    for (iterV = spVNeigh.begin(); iterV != spVNeigh.end(); iterV++) {
      Vert *pVNeigh = *iterV;
      double dDist = dDistanceBetween(pVNeigh, pV);
      double dRelLen = dDist / dLenScale;
      dMax = max(dMax, dRelLen);
      dMin = min(dMin, dRelLen);
      dSum += dRelLen;
      iNEdges ++;
    }
  }
  double dAve = dSum / iNEdges;
  vMessage(2, "Checked relative length scale for %d edges, counting duplicates.\n", iNEdges);
  vMessage(2, "Average: %f.  Min: %f.  Max: %f.\n",
	   dAve, dMin, dMax);
}
