#include <math.h>
#include <stdlib.h>
#include "GR_Vertex.h"
#include "GR_BFace.h"
#include "GR_Mesh.h"

#if (defined(SOLARIS2) && !defined(NDEBUG))
// Solaris puts the definition of finite() in an odd place.  Need this
// only in some assertions, so don't include the file when assertions
// are null.
#include <ieeefp.h>
#endif

Face* Vert::pFHintFace() const
{
  assert(qDeleted() || !pFHint->qValid() || pFHint->qHasVert(this));
  return pFHint;
}

void Vert::vSetHintFace(Face *const pF)
{
  assert(qValid() && !qDeleted());
  assert(pF->qValid() && !pF->qDeleted());
#ifndef NDEBUG
  bool qOK = false;
  for (int ii = 0; ii < pF->iNumVerts(); ii++) {
    if (pF->pVVert(ii) == this) qOK = true;
  }
  assert(qOK);
#endif
  pFHint = pF;
}

Face* Vert::pFSetHintFace(const Mesh* const pM)
{
  assert(qValid() && !qDeleted());

  if (pFHint->qValid() && !pFHint->qDeleted() && pFHint->qHasVert(this))
    return pFHint;
  
  int i;
  // Check first among boundary faces.
  for (i = pM->iNumBdryFaces() - 1; i >= 0; i--) {
    BFace *pBF = pM->pBFBFace(i);
    if (pBF->qValid() && !pBF->qDeleted() && pBF->qHasVert(this)) {
      pFHint = pBF->pFFace(0);
      return pFHint;
    }
  }
  // Failing that, try all the faces until you get a hit.
  for (i = pM->iNumFaces() - 1; i >= 0; i--) {
    Face *pF = pM->pFFace(i);
    if (pF->qValid() && !pF->qDeleted() && pF->qHasVert(this)) {
      pFHint = pF;
      return pFHint;
    }
  }
  return pFInvalidFace;
}

void Vert::vSetDefaultFlags()
{
  uiDim = 0;
  uiType = eInterior;
  uiUser = 0;
  qDel = qDelReq = qAct = qSurfStruct = qStruct = qShapeOK =
    qSmallAngleCorner = false;
  dLengthScale = -1000;
}

void Vert::vCopyAllFlags(const Vert &V)
{
  uiDim = V.uiDim;
  uiType = V.uiType;
  uiUser = V.uiUser;
  qDel = V.qDel;
  qDelReq = V.qDelReq;
  qAct = V.qAct;
  qSurfStruct = V.qSurfStruct;
  qStruct = V.qStruct;
  qShapeOK = V.qShapeOK;
  qSmallAngleCorner = V.qSmallAngleCorner;
}

bool Vert::qIsBdryVert() const
{
  bool qRetVal = false;
  qRetVal = ((uiType == eBdryApex) ||
	     (uiType == eBdryCurve) ||
	     (uiType == eBdryTwoSide) ||
	     (uiType == eBdry) ||
	     (uiType == ePseudoSurface));
  return qRetVal;
}

bool Vert::qSmoothable() const
{
  return (!qIsStructured() &&
	  !qDeletionRequested() &&
	  !qDeleted() &&
	  !(iVertType() == eBdryApex) &&
	  !(iVertType() == eBdryCurve) &&
	  !(iVertType() == eBdryTwoSide));
  // Technically, bdry curve points -are- smoothable, but that isn't
  // implemented yet.  
}

void Vert::vSetCoords(const int iNumDim, const double adNewLoc[])
{
  assert(iNumDim == 2 || iNumDim == 3);
  if (iSpaceDimen() == 0) vSetDimen(iNumDim);
  // The following assertion is false for ITAPS setting 2D coords...
#ifndef ITAPS
  assert(iNumDim == iSpaceDimen());
#endif
  switch(iNumDim) {
  case 3:
    assert(finite(adNewLoc[2]));
    adLoc[2] = adNewLoc[2];
    // Deliberate fallthrough
  case 2:
    assert(finite(adNewLoc[1]));
    assert(finite(adNewLoc[0]));
    adLoc[1] = adNewLoc[1];
    adLoc[0] = adNewLoc[0];
    break;
  default:
    assert(0);
  }
  vMarkIllShaped();
}

void Vert::vSetCoords(const char * const pcBuffer)
{
  switch (iSpaceDimen()) {
  case 2:
    sscanf(pcBuffer, "%lf %lf", &(adLoc[0]), &(adLoc[1]));
    assert(finite(adLoc[0]));
    assert(finite(adLoc[1]));
    break;
  case 3:
    sscanf(pcBuffer, "%lf %lf %lf", &(adLoc[0]), &(adLoc[1]), &(adLoc[2]));
    assert(finite(adLoc[0]));
    assert(finite(adLoc[1]));
    assert(finite(adLoc[2]));
    break;
  default:
    assert(0);
  }
  vMarkIllShaped();
}

double dDistanceBetween(const Vert * const pV0, const Vert * const pV1)
{
  assert(pV0->iSpaceDimen() == pV1->iSpaceDimen());
  if (pV0->iSpaceDimen() == 2) 
    return dDIST2D(pV0->adCoords(), pV1->adCoords());
  else {
    assert(pV0->iSpaceDimen() == 3);
    return dDIST3D(pV0->adCoords(), pV1->adCoords());
  }
}

Face* findCommonFace(const Vert * const pV0, const Vert * const pV1)
{
  // At this point, can't do much better than finding and comparing
  // neighborhoods.
  std::set<Face*> spF0, spF1;
  {
    std::set<Cell*> spCTmp0, spCTmp1;
    std::set<Vert*> spVTmp0, spVTmp1;
    vNeighborhood(pV0, spCTmp0, spVTmp0, NULL, NULL, &spF0);
    vNeighborhood(pV1, spCTmp1, spVTmp1, NULL, NULL, &spF1);
  }
  Face **apF = new Face*[spF0.size()]; // Can't be any more than this.
  Face **ppFEnd = std::set_intersection(spF0.begin(), spF0.end(),
					spF1.begin(), spF1.end(), apF);
  int iCount = ppFEnd - apF;
  for (int ii = 0; ii < iCount; ii++) {
    Face *pF = apF[ii];
    if (pF->qHasVert(pV0) && pF->qHasVert(pV1))
      return pF;
  }
  return NULL;
}

