#ifndef GR_Mesh2D
#define GR_Mesh2D 1

#include "GR_config.h"
#include "GR_List.h"
#include "GR_Mesh.h"
#include "GR_VarArray2D.h"

 // This type is used to keep track of edges created
 // when doing the initial discretization of patches
typedef struct {
	int  iPatch;
	int  iV0;
	int  iV1;
	Face *pF;
	bool qTriedRecover;
} structTrackEdges;

typedef struct {
  Face* pF;
  Cell* pC;
  Cell* pCOpp;
  Vert* pV0;
  Vert* pV1;
} structFaceList;

typedef struct {
  int  iIndex;
  bool qIsRight;
} structHullOrder;

/**\brief The 2D specialization of the Mesh base class.
 *
 * Functions inherited from Mesh will not be documented again here.
 */
class Mesh2D : public Mesh {
protected:
  /// Variable size array containing faces in the 2D mesh
  VAEdgeFace      VAEdgeF;      

  VABdryEdge      VAEdgeBF; /// Bdry faces

  /// Variable sized array containing triangular cells in the 2D mesh
  VATriCell       VATri; 

  /// Variable sized array containing quadrilateral cells in the 2D mesh
  VAQuadCell      VAQuad;
  
  VAIntBdryEdge   VAIntEdgeBF;  // Internal bdry faces

  /// Variables to keep track of where vertices are inserted.
  int iOnEdge, iOnBdryEdge, iOnBdryEdge2;
  int iInterior, iInternalBdry, iInternalBdry2;
public:
  // Should also keep a copy of the boundary info.. !
  Bdry2D      B2D;
  ///
  Mesh2D(const VAVertex& VA, const int iQualMeas = 2,
	 const int iNumBdryEdges = 0,
	 const int (* const a2iConnectList)[2] = NULL);
  ///
  Mesh2D(const Bdry2D& B2DInfo, const int iQualMeas = 2,
	 bool qInsertExtraVerts = true);
  ///
  Mesh2D(const Mesh2D& M);
  ///
  Mesh2D(const char strInFileName[], const int iQualMeas = 2,
	 const Bdry2D* pB2DInfo = NULL);
  ///
  Mesh2D(const int iQualMeas = 2);
  ///
  ~Mesh2D() {}
  ///
  Mesh2D& operator=(const Mesh2D& M);
  /// Inquiry functions
  eMeshType eType() const {return eMesh2D;}
  ///
  int iNumFaces() const {return VAEdgeF.iSize();}
  ///
  int iNumBdryFaces() const {return VAEdgeBF.iSize();}
  ///
  int iNumIntBdryFaces() const {return VAIntEdgeBF.iSize();}
  ///
  int iNumCells() const {return VATri.iSize() + VAQuad.iSize();}
  /// Return the number of triangular cells.
  int iNumTriCells() const {return VATri.iSize();}

  /// Return the number of quadrilateral cells.
  int iNumQuadCells() const {return VAQuad.iSize();}
  ///
  Face*    pFFace(const int i) const;
  ///
  BFace* pBFBFace(const int i) const;
  ///
  IntBdryEdge* pIBEIntBdryEdge(const int i) const;
  ///
  Cell*    pCCell(const int i) const;
  /// Index retrieval
  virtual int iFaceIndex(const Face* const pF) const;
  ///
  virtual int iBFaceIndex(const BFace* const pBF) const;
  ///
  virtual int iCellIndex(const Cell* const pC) const;
  ///
  virtual bool qIsValidEnt(void* pvEnt) const;
  /// Create a flock of null entries
  void vSetupFaces(const int i)     {VAEdgeF .vSetup(i);}
  ///
  void vSetupBFaces(const int i)    {VAEdgeBF.vSetup(i);}
  ///
  void vSetupIntBFaces(const int i)    {VAIntEdgeBF.vSetup(i);}
  ///
  void vSetupTriCells(const int i)  {VATri   .vSetup(i);}
  ///
  void vSetupQuadCells(const int i) {VAQuad  .vSetup(i);}
  /// Add entries to connectivity tables
  Face*    pFNewFace(const int iNV = 2);
  ///
  BFace* pBFNewBFace(const int iNBV = 2);
  ///
  IntBdryEdge* pIBENewIntBdryEdge(const int iNBV = 2);
  ///
  Cell*    pCNewCell(const int iNF = 3);
  /// Purge unused entries
  void vPurgeFaces();
  ///
  void vPurgeBdryFaces();
  ///
  void vPurgeCells();
  ///
  virtual void vPurgeVerts();
  ///
  void vPurge();
  /// Prints a summary of the patches...
  void vCheckPatches();
  /// Take cell-vert data and convert in into a 2D connectivity
  virtual void vConvertFromCellVert(const VATriCellCV& VATriCV,
				    const VAQuadCellCV& VAQuadCV
				    = VAQuadCellCV(),
				    const VAEdgeBFaceCV& VABFCV
				    = VAEdgeBFaceCV());
  /// Switch between triangular and mixed cell types
  void vMakeSimplicial() {assert(VAQuad.iSize()==0); qSimplex = true;}
  /// Merge one connectivity into another
  void vMerge(const Mesh2D& M);
  /// perimeter length or  area:
  virtual double dBoundarySize() const;
  /// interior   area  or volume:
  virtual double dInteriorSize() const;
  ///
  virtual bool qWatertight() const;
/// Validity checking
  virtual bool qValid() const;
/// Ensure that the correct number of verts exist
  void vSetupVerts(const int i) {VAVerts.vSetup(i);}
/// Number of neighbors and their identities
public:
  // Mesh optimization, point insertion, etc
///  virtual void vSwap(const int iMaxPasses = 10);
  bool qBdrySmooth(Vert* const pVBdry,
		   Face* const pFBdry,
		   BdryEdge* const pBE_Init);
  ///
  int iSmooth(const int iMaxPasses = 10);
  ///
  virtual bool qDelaunayize();
  ///
  void vSetSmoothingGoal(const int iGoal)
    {
      SMsetSmoothFunction(pvSmoothData, iGoal);
    }
  ///
  virtual int iReconfigure(Face* const pF);
  /// This one is overridden in SurfMesh.
  virtual bool qInsertPoint(const double adPoint[], Cell* const pCGuess,
			    int * const piSwaps, const bool qSwap = true,
			    const bool qForce = false,
			    Vert* pVNew = pVInvalidVert);
  ///
  bool qInsertPointWatson(const double adNewPoint[2],
			  Cell* const pInitialCell);
  ///
  virtual int iInsertPointWatson(WatsonInfo&);
  ///
  void vInsertWatsonInterior(Vert* const pVNew,
			     const List<Cell*>& LpCToRemove,
			     const List<Face*>& LpFToRemove,
			     const List<Face*>& LpFHull);
  /// The following function is deliberately blank; it's not needed in 2D.
  void vCleanupWatson(WatsonInfo&) {}
  virtual void vGetWatsonData(const double adPoint[3],
			      Cell* const pCSeed,
			      std::set<Cell*>& spCSearch,
			      std::set<Face*>& spFHull,
			      std::set<Face*>& spFToRemove,
			      std::set<BFace*>& spBFEncroached) const;
  // Use this next function to clear out all points lying within a given
  // ball when doing insertion on / near boundaries.  Returns true iff
  // the ball has no interior points left.
  virtual bool qDeleteInteriorPointsInBall(const Cell *pCSeed,
					   const double adLoc[],
					   const double dRadius,
					   int& iNDeleted);
  virtual int iQueueEncroachedBdryEntities(InsertionQueue&,
					   const std::set<BFace*>&) const;
  virtual int iQueueEncroachedBdryEntities(InsertionQueue& IQ,
					   const std::set<BFace*>& spBF,
					   const double adLoc[],
					   const double dOffset = 0) const;
  // The worst shape quality measure allowed.
  virtual double dWorstAllowedCellShape() const
    {
      // These should correspond to minimum angles of 20.7, 25.7, and 30 deg,
      // respectively.
      switch (eEncType) {
      case eBall:
	return sqrt(1./6.); // = sin(20.7 deg) / sin(60 deg)
      case eLens:
	return 0.5;         // = sin(25.7 deg) / sin(60 deg)
      case eNewLens:
	return sqrt(1./3.); // = sin(30 deg) / sin(60 deg)
      default:
	assert(0);
	return 0;
      }
    }
  // Find the cell incident on pV0 that covers the ray towards pV1 (may be
  // a multiple choice question if pV0 and pV1 are adjacent, or if there
  // is a face containing one but not the other and colinear with both.
  virtual Cell* pCBeginPipe(const Vert* const pV0,
			    const Vert* const pV1) const;
public:
  ///
  Face* pFBdryCreateNewCellsAndAssignThem
    (List<Cell*> LpCDelCells, List<structFaceList> LsAllFaces,
     List<int> LiHullFaces, List<int> LiIntFaces,
     List<structHullOrder> LsHullOrder, Vert* pVNew, BFace* pNewBF,
     int iCellType, int iFaceType, int iSide
     );
	///
  int  iInsertPointOnBdryWatson(Cell* pInitialCell, int iFaceIdx,
				const double adNewPoint[2],
				List<BdryEdgeBase*>& LpBEEncroachedBFaces,
				bool qApex);
  ///  Form for when the cell and the face index are unknown..
  int  iInsertPointOnBdryWatson(const double adNewPt[2],
				List<BdryEdgeBase*>& LpBEEncroachedBFaces);
  /// Clear and recompute periodic face pairs.
  void vResetPeriodicData();
  /// PreProcessing for Ruppert - 1
  void vCheckForLargeAngles(const double dMaxAngle = M_PI_2); // 90 deg
  /// PreProcessing for Ruppert - 2
  void vCheckForSmallAngles();
  /// Performs Ruppert's algorithm on the mesh..
  bool qRuppert(const int iNumInsertions = 0);
  ///
  bool qCellRuppertBad(const Cell *pC, bool *qInCorner);
  //
  // The following three functions are used in the experimental
  // anisotropic meshing code.
  void vGenerateAnisotropic(const int iNStructBC, const int aiStructBC[]);
  Vert *pVInsertNearVert(Vert* const pVOldVert, const double adNewPoint[]);
  void vRefineBoundaryToLengthScale(const int iNStructBC,
				    const int aiStructBC[]);
  /// Create a vertex at the given location.
  virtual Vert* createVert(const double dX, const double dY,
			   const double dZ);

  /// Create an edge face from two vertices.
  Face* createFace(Vert * const pV0, Vert * const pV1);

  bool qCreateQuadFromTris(Cell* const pCA, Cell* const pCB);
  bool qCreateQuadFromVerts(Vert* const pVA, Vert* const pVB,
			    Vert* const pVC, Vert* const pVD);
  ///
  void vSetLSChecking(bool qCheck);
  ///
  bool qRecoverEdge(const int iV0, const int iV1);
  bool qRecoverEdge(Vert* const pV0, Vert* const pV1, Face*& pFConnecting);
  ///
  bool qRemoveEdge(Face * const pF);
  ///
  void vSteiner(const int iMaxVerts, const int qSwapEachInsert = 1);
  ///
  void vRefineToLengthScale(const bool qKeepSameSize,
			    const int iMaxPasses = 20);
  ///
  bool qRemoveVertCheaply(Vert* const pV, int& iNewSwaps);
  ///
  virtual bool qRemoveVertByContraction(Vert* const pV, int& iNewSwaps,
		   Vert * apVPreferred[] = NULL,
		   const int iNPref = 0);
  virtual bool qRemoveVert(Vert* const pV, int& iNewSwaps);
  ///
  virtual void vIdentifyVertexTypesGeometrically() const;
  virtual double dCoarsenConstant() const {return 0.45;}
  ///
  virtual int iNumBdryPatches() {return B2D.iNumPatches();}
  void vBuildBdryPatches();
  ///
  virtual bool qDoSwap(const Vert* const pVVertA,
		       const Vert* const pVVertB,
		       const Vert* const pVVertC,
		       const Vert* const pVVertD,
		       const Vert* const pVVertE = pVInvalidVert) const;
  ///
  int iSwap(const int iMaxPasses = 10, const bool qAlreadyMarked = false);
  ///
  int iFaceSwap(Face* const pF);
protected:
  ///
  int iInsertOnBdryFace(Vert* const pVNew, Face* const pF,
			Cell* const pC, const bool qSwap, const bool qForce);
  ///
  int iInsertOnFace(Vert* const pVNew, Face* const pF, Cell* const pC,
		    const bool qSwap = true, const bool qForce = false);
  ///
  int iInsertInInterior(Vert* const pVNew, Cell* const pC,
			const bool qSwap);
  ///
  int iInsertOnInternalBdryFace(Vert* const pVNew, Face* const pF,
				const bool qSwap, const bool qForce);
	///
  void vGetBdry2DInfo(const Bdry2D& B2DInfo);

  bool qEnsurePointIsInterior(List <BdryEdgeBase*>& LpBEEncroachedBFaces,
			      int iWhichEdge,
			      Vert *pVNew,
			      bool qFix);


protected:
  virtual void vReorderVerts_RCM(void);
  virtual void vReorderCells(void);
  virtual void vReorderFaces(void);
public:
  // The following added by Charles Boivin on Aug 3, 1999
  void vGetNeighborVertsAndFaces(Vert* pV,
				 List<Vert*>& LpVNear,
				 List<Face*>& LpFNear );

  void vWriteBdryPatchFile(const char strBaseFileName[],
			   const char strExtraFileSuffix[] = "");

  
};

/** Unlikely ever to be called from user code. */
void vWriteFile_Mesh2D(Mesh2D & Mesh,
		       const char strBaseFileName[],
		       const char strExtraFileSuffix[] = "");
void vWriteNative(Mesh2D& OutMesh,
		  const char strBaseFileName[],
		  const bool qHighOrder = true,
		  const char strExtraFileSuffix[] = "");
void vWriteFEA(Mesh2D& OutMesh,
	       const char strBaseFileName[],
	       const int iOrder = 2,
	       const char strExtraFileSuffix[] = "");

///
void vReadFile_Mesh2D(const char * const strBaseFileName,
		      int& iNumVerts,
		      int& iNumFaces,
		      int& iNumCells,
		      int& iNumBdryFaces,
		      int& iNumIntBdryFaces,
		      bool& qFaceVert,
		      bool& qCellVert,
		      bool& qFaceCell,
		      bool& qCellFace,
		      bool& qCellRegion,
		      bool& qBFaceFace,
		      bool& qBFaceVert,
		      bool& qBFaceBC,
		      bool& qIntBFaceFace,
		      bool& qIntBFaceVert,
		      bool& qIntBFaceBC,
		      VAVertex& VAVerts,
		      int (*&a2iFaceVert)[2],
		      int (*&a2iFaceCell)[2],
		      int (*&a2iCellVert)[3],
		      int (*&a2iCellFace)[3],
		      int *&aiCellRegion,
		      int *&aiBFaceFace,
		      int *&aiBFaceBC,
		      int (*&a2iBFaceVert)[2],
		      int (*&a2iIntBFaceFace)[2],
		      int *&aiIntBFaceBC,
		      int (*&a2iIntBFaceVert)[2]);

#endif
