#ifndef GR_Vertex
#define GR_Vertex 1

#include "GR_config.h"
#include "GR_Bdry.h"
#include "GR_Entity.h"
#include "GR_Vec.h"

///
class Vert : public Entity {
  ///
  double adLoc[3];
  ///
  Face *pFHint;
  // This is a packed bitfield containing a set of flags, including 10
  // bits for user data.  The mechanics of the packing are handled
  // automatically by the compiler.
  //
  unsigned int uiDim:2, uiType:4, uiUser:10;
  bool qDel:1, qDelReq:1, qAct:1, qSurfStruct:1, qStruct:1,
    qShapeOK:1;
  ///
  // Next three were added by Charles Boivin, on Aug 3rd, 1999
  // Flags to tell you what data you've got.
  bool qIsClosestVert:1, qIsSecondVert:1;

  /// This marks if the vertex is in a small-angle corner
  bool qSmallAngleCorner:1;
  /// This is a pointer to the length-scale influencing vert (old style)
  // or to the nearest vert / patch (new style)
  union {
    Vert* pV1;
    BdryPatch* pBP1;
  };
  // Pointer to the second-nearest vert / patch (new style)
  union {
    Vert* pV2;
    BdryPatch* pBP2;
  };
  /// This is the value of the length-scale for this vertex
  double dLengthScale;
  ///
  // End of input by Charles Boivin

  Vert(const Vert&) : Entity() {
    assert(0);
  }
  Face *pFFace(const int) {assert(0); return pFInvalidFace;}
  Cell *pCCell(const int) {assert(0); return pCInvalidCell;}
  const Face *pFFace(const int) const {assert(0); return pFInvalidFace;}
  const Cell *pCCell(const int) const {assert(0); return pCInvalidCell;}
public:
  ///
  enum VertType {eUnknown = 0, eBdryApex, eBdryCurve, eBdryTwoSide,
		 eBdry, ePseudoSurface, eInterior, eInteriorFixed, eInvalid};
  ///
  Vert(const VertType VT = eInterior) :
    pFHint(pFInvalidFace), uiType(VT)
    // Others set by SetDefaultFlags
    {
      // By default, a vertex has eInterior type, 0 space dimensions, and
      // is smoothable.  All other flags are cleared.
      vSetDefaultFlags();
      adLoc[0] = adLoc[1] = adLoc[2] = 0.;
      vClearLS();
    }
  ///
  ~Vert() {}
  ///
  Vert& operator=(const Vert& V) {
    if (this != &V) {
      adLoc[0] = V.adLoc[0];
      adLoc[1] = V.adLoc[1];
      adLoc[2] = V.adLoc[2];
      pFHint = V.pFHint; // This hint will not be valid unless the face
			 // is updated, too.
      vCopyAllFlags(V);
      vCopyInfluence(&V);
      dLengthScale = V.dLengthScale;
    }
    return (*this);
  }
public:
  /* ITAPS Begin ITAPS Begin ITAPS Begin ITAPS Begin ITAPS Begin ITAPS Begin */
  // The following functions are very useful for GRUMMP support of ITAPS.
  const Vert *pVVert(const int iV) const {
    assert(iV == 0);
    return this;
  }
  Vert *pVVert(const int iV) {
    assert(iV == 0);
    return this;
  }
  void vAllVertHandles(GRUMMP_Entity* []) const {assert(0);}
  void vAllCellHandles(GRUMMP_Entity* []) const {assert(0);}
  void vAllFaceHandles(GRUMMP_Entity* []) const {assert(0);}

  int iNumVerts() const {return 1;}
  int iNumFaces() const {assert(0); return(0);}
  int iNumCells() const {assert(0); return(0);}
#ifdef ITAPS
  int iTopologicalDimension() const {return 0;}
  int eEntType() const {return iBase_VERTEX;}
  int eEntTopology() const {return iMesh_POINT;}
#endif
  /* ITAPS End ITAPS End ITAPS End ITAPS End ITAPS End ITAPS End ITAPS End */
  ///
  Face *pFHintFace() const;
  ///
  void vSetHintFace(Face *const pF);
  ///
  Face *pFSetHintFace(const Mesh* const pM);
  ///
  void vClearHintFace() {pFHint = pFInvalidFace;}
  ///
  void vSetDefaultFlags();
  ///
  void vCopyAllFlags(const Vert &V);
  ///
  void vCopyAllFlags(const Vert * const pV) {vCopyAllFlags(*pV);}
  ///
  void vSetUserData(const unsigned int uiData) {
    assert(uiData < 1024);
    uiUser = uiData;
  }
  ///
  void vClearUserData() {uiUser = 0;}
  ///
  int iGetUserData() const {return uiUser;}
  ///
  void vSetType(const VertType VT) {uiType = VT;}
  ///
  void vSetType(const unsigned int uiTypeIn) {
    assert(uiTypeIn <= eInvalid);
    uiType = uiTypeIn;
  }
  ///
  int iVertType() const {return (uiType);}
  ///
  bool qIsBdryVert() const;
  ///
  int iSpaceDimen() const {return (uiDim);}
  ///
  void vSetDimen(const int iSD) {
    assert(uiDim == 0);
    assert(iSD == 2 || iSD == 3);
    uiDim = iSD;
  }
  ///
  void vMarkDeleted() { qDel = true; }
  ///
  bool qDeleted() const { return (qDel); }
  ///
  void vMarkActive() { qAct = true; }
  ///
  void vMarkInactive() { qAct = false; }
  ///
  bool qActive() const { return (qAct); }
  ///
  void vMarkToDelete() { qDelReq = true; }
  ///
  void vMarkToKeep() { qDelReq = false; }
  ///
  bool qDeletionRequested() const { return (qDelReq); }
  ///
  void vMarkStructured()
    {
      if (qIsBdryVert())
	qSurfStruct = true;
      else
	qStruct = true;
    }
  ///
  void vClearStructured() {qSurfStruct = qStruct = false;}
  ///
  bool qIsStructured() const
    {
      if (qIsBdryVert())
	return (qSurfStruct);
      else
	return (qStruct);
    }
  ///
  bool qSmoothable() const;
  ///
  bool qWellShaped() const { return (qShapeOK); }
  ///
  void vMarkWellShaped() { qShapeOK = true; }
  ///
  void vMarkIllShaped()  { qShapeOK = false; }
  ///
  double dX() const { return adLoc[0]; }
  ///
  double dY() const { return adLoc[1]; }
  ///
  double dZ() const { return (iSpaceDimen() == 3) ? adLoc[2] : -1.E300; }
  ///
  void vCopyCoords(const Vert * const pV)
    {
      assert(pV->qValid());
      vSetCoords(pV->iSpaceDimen(), pV->adCoords());
    }
  ///
  void vSetCoords(const int iNumDim, const double adNewLoc[]);
  ///
  void vSetCoords(const char * const pcBuffer);
  ///
  bool qValid() const { return (this != pVInvalidVert); } ;
  ///
  const double* adCoords() const { assert(qValid()); return adLoc; }

  /// Return an edge common to two verts, or NULL if there is none.
  friend Face* findCommonFace(const Vert * const pV0, const Vert * const pV1);

  // Following functions added by Charles Boivin on Aug 3rd, 1999

  bool qIsSmallAngleCorner() const {
    return qSmallAngleCorner;
  }

  void vSetSmallAngleCorner(bool qSmall) {
    qSmallAngleCorner = qSmall;
  }

  double dLS() const {
    // Returns the value of the length-scale stored...
    return dLengthScale;
  }

  void vSetLS(double dLength) {
    // Sets the value of the length-scale for the vertex..
    assert(dLength > 0.0);
    // Floor on length scale is dangerous:  can lead to infinite loops!
    //      if (dLength < 0.001) dLength = 0.001;
    dLengthScale = dLength;
  }
  void vClearLS() {
    dLengthScale = -1000;
    qIsClosestVert = qIsSecondVert = false;
    pV1 = pV2 = pVInvalidVert;
  }

  // Stuff required for new-style length scale calculation
  void vSetClosest(Vert * const pVIn) {
    assert(pVIn->qValid());
    pV1 = pVIn;
    qIsClosestVert = true;
  }
  void vSetClosest(BdryPatch * const pBPIn) {
    assert(pBPIn->qValid());
    pBP1 = pBPIn;
    qIsClosestVert = false;
  }
  void vSetClosest(Vert * const pVIn, BdryPatch * const pBPIn) {
    if (pVIn->qValid()) {
      assert(!pBPIn->qValid());
      vSetClosest(pVIn);
    }
    else {
      assert(pBPIn->qValid());
      vSetClosest(pBPIn);
    }
  }
  void vSetSecond(Vert * const pVIn) {
    assert(pVIn->qValid());
    pV2 = pVIn;
    qIsSecondVert = true;
  }
  void vSetSecond(BdryPatch * const pBPIn) {
    assert(pBPIn->qValid());
    pBP2 = pBPIn;
    qIsSecondVert = false;
  }
  void vSetSecond(Vert * const pVIn, BdryPatch * const pBPIn) {
    if (pVIn->qValid()) {
      assert(!pBPIn->qValid());
      vSetSecond(pVIn);
    }
    else {
      assert(pBPIn->qValid());
      vSetSecond(pBPIn);
    }
  }
  void vGetClosest(Vert **ppVOut, BdryPatch **ppBPOut) {
    if (qIsClosestVert) {
      (*ppVOut) = pV1;
      (*ppBPOut) = static_cast<BdryPatch*>(NULL);
    }
    else {
      (*ppVOut) = pVInvalidVert;
      (*ppBPOut) = pBP1;
    }
  }
  void vGetSecond(Vert **ppVOut, BdryPatch **ppBPOut) {
    if (qIsSecondVert) {
      (*ppVOut) = pV2;
      (*ppBPOut) = static_cast<BdryPatch*>(NULL);
    }
    else {
      (*ppVOut) = pVInvalidVert;
      (*ppBPOut) = pBP2;
    }
  }
  void vCopyInfluence(const Vert * const pVIn) {
    qIsClosestVert = pVIn->qIsClosestVert;
    qIsSecondVert = pVIn->qIsSecondVert;
    // Might not be a vertex pointer, but the operation still works...
    pV1 = pVIn->pV1;
    pV2 = pVIn->pV2;
  }
};

double dDistanceBetween(const Vert * const pV1, const Vert * const pV2);

#endif
