#ifndef GR_SurfMesh
#define GR_SurfMesh 1

#include "GR_config.h"
#include "GR_Mesh2D.h"
#include "GR_VarArraySurf.h"

///
class SurfMesh : public Mesh2D {
  // MultiEdge's are needed to allow non-manifold surface meshes.
  VAMultiEdge VAME;
  int iOnMultiEdge;
  /// surface geometry data
  Bdry3D *pB3D;
  /// Tie-in from cells in surface mesh to boundary patches.  This array
  // should be variable-sized, but I couldn't be bothered on the first
  // cut at this code.  (CFO-G, 11/2000)
  BdryPatch3D **apBPatch;
  int iBPatchSize;
public:
  /// Read a surface mesh from a file.  Currently not needed or coded.
  // SurfMesh(const char strInFileName[]);
  ///
  SurfMesh(const SurfMesh& SM);
  ///
  SurfMesh(Bdry3D * const pB3D, const int iQualMeas = 2);
  ///
  SurfMesh(const char strBaseFileName[], const int iQualMeas = 2);
  ///
  SurfMesh(VAVertex& VA, const int iQualMeas = 2) :
    Mesh2D(iQualMeas), VAME(), iOnMultiEdge(0),
    pB3D(NULL), apBPatch(NULL), iBPatchSize(0)
    { 
      // Don't pass the vertex list to the Mesh2D constructor, because
      // it will try to triangulate the point set as if it were 2D.
      vSetVerts(&VA);

      apBPatch = new BdryPatch3D*[50];
      iBPatchSize = 50;
      // Can't set up faces and cells here, because there's not enough
      // info to do this correctly.
    }
  SurfMesh(const int iQualMeas = 2) :
    Mesh2D(iQualMeas), VAME(), iOnMultiEdge(0),
    pB3D(NULL), apBPatch(NULL), iBPatchSize(0)
      { }
  ///
  ~SurfMesh()
    {
      if (apBPatch) {
	delete [] apBPatch;
	apBPatch = NULL;
      }
    }
  ///
  SurfMesh& operator=(const SurfMesh& SM);
  ///
  int iNumMultiEdges() const {return VAME.iSize();}
  ///
  MultiEdge* pMENewMultiEdge();
  ///
  MultiEdge* pMEMultiEdge(const int i) const
    {
      assert(i >= 0 && i < VAME.iSize());
      return VAME.pTEntry(i);
    }
  /// Create a vertex at the given location.
  virtual Vert* createVert(const double dX, const double dY,
			   const double dZ);
  ///
  void vConvertFromCellVert(const VATriCellCV& VATriCV,
			    const VAQuadCellCV& VAQuadCV
			    = VAQuadCellCV(),
			    const VAEdgeBFaceCV& VABFCV
			    = VAEdgeBFaceCV());
  ///
  Bdry3D * pBoundaryData() const {return pB3D;}
  ///
  BdryPatch3D * pBdryPatch(const int iCell) const
    {
      assert(iCell >= 0 && iCell < iNumCells());
      return apBPatch[iCell];
    }
  BdryPatch3D * pBdryPatch(const Cell* const pC) const
    {
      int iCell = iCellIndex(pC);
      return apBPatch[iCell];
    }
  void vSetBdryPatch(const Cell* const pC, BdryPatch3D* const pBP)
    {
      int iCell = iCellIndex(pC);
      assert(iCell >= 0 && iCell < iNumCells());
      assert(pBP != NULL);
      if (iCell >= iBPatchSize) {
	BdryPatch3D **apBPTemp = apBPatch;
	apBPatch = new BdryPatch3D*[iBPatchSize*2];
	// Copy the data into the new array
	for (int iTmp = 0; iTmp < iBPatchSize; iTmp++) {
	  apBPatch[iTmp] = apBPTemp[iTmp];
	}
	iBPatchSize *= 2;
	delete [] apBPTemp;
      }
      apBPatch[iCell] = pBP;
    }
  void vSetBdryPatch(const int iCell, BdryPatch3D* const pBP)
    {
      assert(iCell >= 0 && iCell < iBPatchSize);
      assert(pBP != NULL);
      if (iCell >= iBPatchSize) {
	BdryPatch3D **apBPTemp = apBPatch;
	apBPatch = new BdryPatch3D*[iBPatchSize*2];
	// Copy the data into the new array
	for (int iTmp = 0; iTmp < iBPatchSize; iTmp++) {
	  apBPatch[iTmp] = apBPTemp[iTmp];
	}
	iBPatchSize *= 2;
	delete [] apBPTemp;
      }
      apBPatch[iCell] = pBP;
    }
  ///
public:
  ///
  eMeshType eType() const {return eSurfMesh;}
  // Smoothing not yet implemented for surface meshes
  int iSmooth(const int iMaxPasses = 10){assert(0 && iMaxPasses); return 0;}
  ///
  int iReconfigure(Face* const pF);
  ///
  bool qInsertPoint(const double adPoint[3], Cell* const pC,
		   int * const piSwaps, const bool qSwap = true,
		   const bool qForce = false, Vert* pVNew = pVInvalidVert);
  ///
  int iInsertOnFace(Vert* const pVNew, Face* const pF, Cell* const pC,
		    const bool qSwap);
  ///
  int iInsertOnMultiEdge(Vert* const pVNew, MultiEdge* const pME,
			 const bool qSwap);
  ///
  int iInsertInInterior(Vert* const pVNew, Cell* const pC,
			const bool qSwap);
  ///
  void vMarkSmallAngles();
  ///
  void vProtectSmallAngles();
  ///
  int iFaceSwap(Face* const pF);
  ///
  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;
public:
  ///
  double dBoundarySize() const;
  ///
  double dInteriorSize() const;  
  ///
  bool qWatertight() const;
  ///
  void vPurgeVerts();
};

void vWriteFile_Surface(SurfMesh & Mesh,
			const char strBaseFileName[],
			const char strExtraFileSuffix[] = "");

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

#endif
