#ifndef GR_InsertionQueue
#define GR_InsertionQueue 1

#include <set>
#include <map>
#include <limits.h>
#include "GR_config.h"
#include "GR_events.h"
#include "GR_misc.h"
#include "GR_Cell.h"
#include "GR_InsertionQueueEntry.h"
#include "GR_Mesh.h"

class InsertionQueue {
private:
  std::multimap<double, InsertionQueueEntry> MMap;
  double dWorstAllowable;
  int iMaxCellCount;
  // If we've cleaned up initial encroachment in this queue, then
  // there's no longer any need to check whether new bdry entities are
  // encroached by points already in the mesh --- they can't be, because
  // no points are ever inserted where they will encroach anything.
  bool qIsInitialEncroachmentFixed;
  Mesh *pM;
  // Don't see any good reason to allow default or copy construction, or
  // operator=.
  InsertionQueue() {assert(0);}
  InsertionQueue(const InsertionQueue&) {assert(0);}
  InsertionQueue operator=(const InsertionQueue&) {assert(0);return(*this);}
public:
  InsertionQueue(Mesh * const pMIn,
		 const double dTarget = InsertionQueueEntry::dInvalidPriority);
  ~InsertionQueue() {}
  InsertionQueueEntry IQETopValidEntry()
    {
      SUMAA_LOG_EVENT_BEGIN(INSERT_QUEUE_MANIP);
      // Grab the first entry in the multimap, and return the data.
      // MMap.begin() returns an iterator which (through overloading of
      // operator ->) can access the contents of the pair<double,
      // InsertionQueueEntry> that constitutes the first mmap entry.
      assert(!qIsQueueEmpty());
      InsertionQueueEntry IQETop = MMap.begin()->second;
      vMessage(4, "Grabbed queue entry with quality measure %f\n", MMap.begin()->first);
      if (IQETop.eType() == InsertionQueueEntry::eTetCell ||
	  IQETop.eType() == InsertionQueueEntry::eTriCell)
	qIsInitialEncroachmentFixed = true;
      SUMAA_LOG_EVENT_END(INSERT_QUEUE_MANIP);
      return IQETop;
    }
private:
  void vCleanQueue()
    // Remove all null entries from the top of the queue.
    {
      if (qIsQueueEmpty()) return;
      vMessage(4, "Cleaning queue of length %d...", iQueueLength());
      InsertionQueueEntry IQETop = MMap.begin()->second;
      while (!qIsQueueEmpty() && !IQETop.qStillInMesh()) {
	vMessage(5, "Popping entry for entity eliminated from mesh.\n");
	MMap.erase(MMap.begin());
	if(!MMap.empty())
	  IQETop = MMap.begin()->second;
      }
      vMessage(4, "now of length %d.\n", iQueueLength());
    }
public:
  double dTopPriority()
    {
      assert(!qIsQueueEmpty());
      // Grab the first entry in the multimap, and return its priority.
      // MMap.begin() returns an iterator which (through overloading of
      // operator ->) can access the contents of the pair<double,

      // InsertionQueueEntry> that constitutes the first mmap entry.
      return MMap.begin()->first;
    }
  void vPopTopEntry()
    {
      SUMAA_LOG_EVENT_BEGIN(INSERT_QUEUE_MANIP);
      assert(!qIsQueueEmpty());
      MMap.erase(MMap.begin());
      vCleanQueue();
      SUMAA_LOG_EVENT_END(INSERT_QUEUE_MANIP);
    }
private:
  int iQueueLength() const {return (int(MMap.size()));}
public:
  bool qIsQueueEmpty() { return MMap.empty(); }
  bool qAddEntry(const InsertionQueueEntry IQE, const double dOffset = 0)
    {
      // Return value of insert is an iterator; not currently checked.
      double dKey = IQE.dEvaluatePriority() + dOffset;
      if (IQE.eType() == InsertionQueueEntry::eTriCell) {
	TriCell *pTC = dynamic_cast<TriCell*>(IQE.pCCell());
	assert(pTC);
	double adCircCent[2];
	pTC->vCircumcenter(adCircCent);
      }
      SUMAA_LOG_EVENT_BEGIN(INSERT_QUEUE_MANIP);
      if (dKey < dWorstAllowable
	  /* This hack is designed to fix interior tets.  It works.  But
	  most of the bad ones tend to be at the boundaries, so it
	  doesn't have much net effect. */
	  /* || */
/* 	  (IQE.eType() == InsertionQueueEntry::eTetCell && */
/* 	   (!IQE.pCCell()->pVVert(0)->qIsBdryVert() && */
/* 	    !IQE.pCCell()->pVVert(1)->qIsBdryVert() && */
/* 	    !IQE.pCCell()->pVVert(2)->qIsBdryVert() && */
/* 	    !IQE.pCCell()->pVVert(3)->qIsBdryVert()) && */
/* 	   dKey < dWorstAllowable*2) */) {
	if (IQE.eType() == InsertionQueueEntry::eTetCell ||
	    IQE.eType() == InsertionQueueEntry::eTriCell) {
	  double adCent[3]; adCent[2] = 0;
	  IQE.pCCell()->vCentroid(adCent);
	  vMessage(3, "Key: %10.6f.  Centroid: (%10.6f, %10.6f, %10.6f)\n",
		   dKey, adCent[0], adCent[1], adCent[2]);
	}
	MMap.insert(std::pair<const double, InsertionQueueEntry>(dKey, IQE));
 	SUMAA_LOG_EVENT_END(INSERT_QUEUE_MANIP); 
	return true;
      }
      else {
	if (IQE.eType() == InsertionQueueEntry::eTetCell ||
	    IQE.eType() == InsertionQueueEntry::eTriCell)
	  IQE.pCCell()->vMarkWellShaped();
	SUMAA_LOG_EVENT_END(INSERT_QUEUE_MANIP);
	return false;
      }
    }
  // Can be used both to add all initially encroached BFaces and
  // subsegments to the insertion queue and to subsequently add
  // newly-created entities that are still encroached.
  void vSetQualityTarget(const double dNewTarget)
    {dWorstAllowable = dNewTarget;}
  double dQualityTarget() const {return dWorstAllowable;}
  void vSetMaxCells(const int iMax)
    {iMaxCellCount = iMax;}
  int iMaxCellsAllowed() const {return iMaxCellCount;}
  void vQualityRefine(const int iMaxVertsAdded = INT_MAX);
  void vBuildQueue(const bool qBdryGuaranteedOK = false);
};

#endif


