#ifndef GR_WatsonInfo
#define GR_WatsonInfo 1

#include <set>
#include <vector>

#include "GR_config.h"
#include "GR_InsertionQueue.h"
#include "GR_Mesh2D.h"
#include "GR_VolMesh.h"

class WatsonInfo {
private:
  // All the data you might need to do Watson insertion into a mesh.
  std::set<Cell*> spCToRemove;
  std::set<BFace*> spBFEncroached;
  std::set<Face*> spFHull, spFToRemove;

  // The InsertionQueueEntry that is causing all the fuss...
  InsertionQueueEntry IQE;

  // The mesh that we're working on refining...
  const Mesh *pM;

  // Location of new point that -might- be inserted
  double adNewPoint[3];

  // Also, storage for newly created entities, so that these can be
  // added to the queue if necessary.  All new cells and bface are added
  // to the appropriate lists.  
  std::vector<Cell*> vecpCNew;
  std::vector<BFace*> vecpBFNew;
  
  WatsonInfo(const WatsonInfo&) :
    spCToRemove(), spBFEncroached(), spFHull(), spFToRemove(), IQE(),
    vecpCNew(), vecpBFNew() {assert(0);}
  WatsonInfo()  :
    spCToRemove(), spBFEncroached(), spFHull(), spFToRemove(), IQE(),
    vecpCNew(), vecpBFNew() {assert(0);}

  bool qIs3DHullOkay(const enum eEncroachType eET) const;
  void vStrip3DHull();
public:
  WatsonInfo(const InsertionQueueEntry& IQEIn, const Mesh* const pMIn);
  ~WatsonInfo() {}

  InsertionQueueEntry::eEntryType eType() const {return IQE.eType();}

  // This function will only be called in one place; it exists to avoid
  // the need for returning the set of encroached BFaces.  Defining it
  // here makes in inline-able, which eliminates all the call overhead.
  int iQueueEncroachedBdryEntities(InsertionQueue& IQ, const double dOffset)
    {
      std::set<BFace*> spBFModEncr(spBFEncroached);
      std::set<BFace*>::iterator iterBF;
      // Remove BFaces incident on an encroached subsegment; they won't
      // survive anyway.
      for (iterBF = spBFModEncr.begin(); iterBF != spBFModEncr.end(); ) {
	BFace *pBF = *iterBF;
	if (IQE.eType() == InsertionQueueEntry::eSubSeg &&
	    (pBF->qHasVert(IQE.pVVert(0)) ||
	     pBF->qHasVert(IQE.pVVert(1)))) {
	  std::set<BFace*>::iterator iterTmp = iterBF++;
	  spBFModEncr.erase(iterTmp);
	}
	else {
	  iterBF++;
	}
      }
      // If a BFace was encroached, it's already queued; don't requeue it.
      if (IQE.eType() == InsertionQueueEntry::eTriBFace ||
	  IQE.eType() == InsertionQueueEntry::eBdryEdge) {
	spBFModEncr.erase(IQE.pBFBFace());
      }
      return (pM->iQueueEncroachedBdryEntities(IQ, spBFModEncr,
					       adCoords(), dOffset));
    }

  // Cells that should be deleted
  int iNumCellsToRemove() const {return spCToRemove.size();}

  // Location of new vertex
  const double *adCoords() const {return adNewPoint;}
  int iAddNewEntitiesToQueue(InsertionQueue& IQ) const;

  friend int VolMesh::iInsertPointWatson(WatsonInfo&);
  friend void VolMesh::vCleanupWatson(WatsonInfo&);

  friend int Mesh2D::iInsertPointWatson(WatsonInfo&);
  friend void Mesh2D::vCleanupWatson(WatsonInfo&);
};

#endif
