// This file contains the C API implementation of the iMesh interface
// with the GRUMMP backend.  Many calls are dispatched to the
// iMesh_GRUMMP class (which will probably eventually morph to
// GRUMMP::Mesh) and its derived classes.
#define IMESH_MAIN
#include "iMesh.h"
#include "iMesh_GRUMMP.hh"
using namespace ITAPS_GRUMMP;

#include <iostream>
#include <vector>
#include "GR_Entity.h"
#include "GR_Vertex.h"
#include "GR_Geometry.h"
#include "GR_Mesh2D.h"
#include "GR_SurfMesh.h"
#include "GR_VolMesh.h"
#include "GR_iMesh_Classes.h"
#include "iMesh_GRUMMP_misc.hh"
#include "iMesh_GRUMMP_EntIter.hh"
#include "iMesh_GRUMMP_Workset.hh"
#include "iMesh_GRUMMP_EntitySet.hh"

static bool isRootSet(iMesh_Instance instance,
		      iBase_EntitySetHandle entity_set_handle)
{
  return (entity_set_handle ==
	  reinterpret_cast<iBase_EntitySetHandle_Private*>(instance->pGB));
}

extern "C" {

  // Error functions
  // The following function is bogus and should be removed from the interface.
  void iMesh_getErrorType(iMesh_Instance instance,
			  int *error_type, int *err);

  void iMesh_getDescription(iMesh_Instance,
			    char *descr, int *err, int descr_len)
  {
    const char *strDescriptions[] = {
      "Success!",
      "Mesh already loaded",
      "No mesh data",
      "File not found",
      "File write error",
      "Nil array",
      "Bad array size",
      "Bad array dimension",
      "Invalid entity handle",
      "Invalid entity count",
      "Invalid entity type",
      "Invalid entity topology",
      "Bad type and topo",
      "Entity creation error",
      "Invalid tag handle",
      "Tag not found",
      "Tag already exists",
      "Tag in use",
      "Invalid entityset handle",
      "Invalid iterator handle",
      "Invalid argument",
      "Memory allocation failed",
      "Not supported",
      "Failure"
    };
    snprintf(descr, descr_len-1, "%s", strDescriptions[*err]);
  }

  void iMesh_newMesh(const char *options_in,
		     iMesh_Instance *instance, int *err, int options_len)
  {
    // From old Mesh factory
    *err = iBase_SUCCESS;
    bool qHasGRUMMP=true, qHas2D=false, qHas3D=false, qHasSurface = false;
    void *pNoWhere = NULL;
    char options[1024];
    NULL_TERMINATE(options_in, options_len, options);
    if (options != NULL && options_len != 0) {
      qHas2D = (pNoWhere != strstr(options, "2D") ||
		pNoWhere != strstr(options, "2d"));
      qHas3D = (pNoWhere != strstr(options, "3D") ||
		pNoWhere != strstr(options, "3d"));
      qHasSurface = (pNoWhere != strstr(options, "SURFACE") ||
		     pNoWhere != strstr(options, "surface"));
      qHasGRUMMP = (pNoWhere != strstr(options, "GRUMMP") ||
		    pNoWhere != strstr(options, "grummp") ||
		    qHas2D || qHas3D || qHasSurface || (options_len == 0));
    }
    if (qHasGRUMMP) {
      if (qHas2D) {
	*instance = new iMesh_Instance_Private(iMesh_Base::ePlane);
      }
      else if (qHasSurface) {
	*instance = new iMesh_Instance_Private(iMesh_Base::eSurface);
      }
      else { // Default to 3D
	*instance = new iMesh_Instance_Private(iMesh_Base::eVolume);
      }
    }
    else {
      *err = iBase_INVALID_ARGUMENT;
    }
  }

  void iMesh_dtor(iMesh_Instance instance, int *err)
  {
    instance->~iMesh_Instance_Private();
    *err = iBase_SUCCESS;
  }

  void iMesh_load(iMesh_Instance instance,
		  const iBase_EntitySetHandle entity_set_handle,
		  const char *name_in, const char* options_in,
		  int *err, int name_len, int options_len)
  {
    iBase_EntitySetHandle root_set;
    *err = iBase_SUCCESS;
    iMesh_getRootSet(instance, &root_set, err);
    if (*err != iBase_SUCCESS) return;
    char name[1024], options[1024];
    NULL_TERMINATE(name_in, name_len, name);
    NULL_TERMINATE(options_in, options_len, options);

    if (entity_set_handle == root_set) {
      if (instance->pGB->pMesh()) {
	*err = iBase_MESH_ALREADY_LOADED;
	return;
      }	
      instance->pGB->load(name, options, err);

      // Set up a tag for vertex type data.  Mesquite wants this
      // exposed, and it does no real harm.
      iBase_TagHandle TH;
      iMesh_createTag(instance, "Vertex type", 1, iBase_INTEGER,
		      &TH, err, 11);
      if (*err != iBase_SUCCESS) return;

      // Now set data for it, by looping over verts and transcribing.
      for (unsigned int iV = 0; iV < instance->pGB->getNumVertices(); iV++) {
	Vert *pV = instance->pGB->pMesh()->pVVert(iV);
	int iType = pV->iVertType();
	iMesh_setIntData(instance, pV, TH, iType, err);
	if (*err != iBase_SUCCESS) return;
      }
    }
    else {
      *err = iBase_NOT_SUPPORTED;
    }

    // Now read the sets.

    // Get a file name for the set file and open it.
    char setName[1024];
    snprintf(setName, 1024, "%s.sets", name);
    FILE* pFSetFile = fopen(setName, "r");

    // Get a file name for the tag file and open it.
    char tagFileName[1024];
    snprintf(tagFileName, 1024, "%s.tags", name);
    FILE* pFTagFile = fopen(tagFileName, "r");

    if (pFSetFile || pFTagFile) {
      // Need a full list of all entity handles
      iBase_EntityHandle *allHandles;
      int allHandles_allocated = 0, allHandles_size;
      iMesh_getEntities(instance, root_set, iBase_ALL_TYPES,
			iMesh_ALL_TOPOLOGIES, &allHandles,
			&allHandles_allocated, &allHandles_size, err);

      int numSets = 0;
      iBase_EntitySetHandle *allSets;
      // Don't try to read the file if it doesn't exist.
      if (pFSetFile) {
	fscanf(pFSetFile, "Total number of sets: %d\n", &numSets);
	allSets = new iBase_EntitySetHandle[numSets];
	// Header info
	for (int setInd = 0; setInd < numSets; setInd++) {
	  int junk;
	  char type[20];
	  int res = fscanf(pFSetFile, "Set #%d; %s\n", &junk, type);
	  assert(res == 2);
	  iMesh_createEntSet(instance, (strstr(type, "un") == NULL),
			     &(allSets[setInd]), err);
	  if (*err != iBase_SUCCESS) {
	    return;
	  }
	}

	// Now read and add members.
	for (int setInd = 0; setInd < numSets; setInd++) {
	  iBase_EntitySetHandle set = allSets[setInd];
	  int junk;

	  // Start by reading entities
	  fscanf(pFSetFile, " Set #%d\nContains entities:", &junk);
	  assert(junk == setInd);
	  int junk2;
	  while (2 == fscanf(pFSetFile, " (%d %d)", &junk, &junk2)) {
	    assert(junk >= 0 && junk < allHandles_size);
	    iMesh_addEntToSet(instance, allHandles[junk],
			      set, err);
	  }

	  // Now read set members
	  int numMembers;
	  fscanf(pFSetFile, " Set members (%d):", &numMembers);
	  for (int ii = 0; ii < numMembers; ii++) {
	    fscanf(pFSetFile, "%d", &junk);
	    assert(junk >= 0 && junk < numSets);
	    iMesh_addEntSet(instance, allSets[junk], set, err);
	  }

	  // Now read children
	  int numChildren;
	  fscanf(pFSetFile, " Child sets (%d):", &numChildren);
	  for (int ii = 0; ii < numChildren; ii++) {
	    fscanf(pFSetFile, "%d", &junk);
	    assert(junk >= 0 && junk < numSets);
	    iMesh_addPrntChld(instance, set, allSets[junk], err);
	  }

	  // Now read parents, though nothing needs to be done.
	  int numParents;
	  fscanf(pFSetFile, " Parent sets (%d):", &numParents);
	  for (int ii = 0; ii < numParents; ii++) {
	    fscanf(pFSetFile, "%d", &junk);
	    assert(junk >= 0 && junk < numSets);
	  }
	}
	fclose(pFSetFile);
      }

      // Don't read the tag file if it doesn't exist
      if (pFTagFile) {
	// Read tag header info, and create tags.
	int numTags;
	fscanf(pFTagFile, " Total number of tags: %d", &numTags);
	iBase_TagHandle allTags[numTags];
	int allTypes[numTags], allSizes[numTags];

	for (int tag = 0; tag < numTags; tag++) {
	  int type, size;
	  fscanf(pFTagFile, " Tag #%*d: Type: %d Size: %d Name: ",
		 &type, &size);
	  char tagName[100];
	  fgets(tagName, 100, pFTagFile);
	  int len = strlen(tagName) - 1;
	  tagName[len-1] = 0;
	  iMesh_createTag(instance, tagName, size, type, &allTags[tag], err, len);
	  allTypes[tag] = type;
	  allSizes[tag] = size;
	  if (*err != iBase_SUCCESS) return;
	}

	int entInd, entTags, scanRes, entsTagged = 0;
	scanRes = fscanf(pFTagFile, " Entity %d has %d tags.\n",
			 &entInd, &entTags);
	while (scanRes == 2) {
	  entsTagged++;
	  iBase_EntityHandle ent = allHandles[entInd];
	  for (int iTag = 0; iTag < entTags; iTag++) {
	    int tagInd;
	    fscanf(pFTagFile, " Tag %d. Value(s): ", &tagInd);
	    iBase_TagHandle tag = allTags[tagInd];
	    int type = allTypes[tagInd];
	    int size = allSizes[tagInd];
	    switch (type) {
	    case iBase_INTEGER:
	      {
		int data[size];
		for (int ii = 0; ii < size; ii++) {
		  fscanf(pFTagFile, "%d", &(data[ii]));
		}
		iMesh_setIntArrData(instance, &ent, 1, tag, data, size, err);
	      }
	      break;
	    case iBase_DOUBLE:
	      {
		double data[size];
		for (int ii = 0; ii < size; ii++) {
		  fscanf(pFTagFile, "%lf", &(data[ii]));
		}
		iMesh_setDblArrData(instance, &ent, 1, tag, data, size, err);
	      }
	      break;
	    case iBase_ENTITY_HANDLE:
	      {
		iBase_EntityHandle data[size];
		for (int ii = 0; ii < size; ii++) {
		  int tagValInd;
		  fscanf(pFTagFile, "%d", &tagValInd);
		  data[ii] = allHandles[tagValInd];
		}
		iMesh_setEHArrData(instance, &ent, 1, tag, data, size, err);
	      }
	      break;
	    case iBase_BYTES:
	      {
		char data[size];
		for (int ii = 0; ii < size; ii++) {
		  fscanf(pFTagFile, "%c", &(data[ii]));
		}
		iMesh_setData(instance, ent, tag, data, size, err);
	      }
	      break;
	    } // End of switch over tag type
	  } // End of loop over tags for this entity
	  scanRes = fscanf(pFTagFile, " Entity %d has %d tags.\n",
			   &entInd, &entTags);
	} // End reading tag data for all entities.
	printf("Found %d entities with tags.\n", entsTagged);

	// Don't bother with set tags if there are no sets
	if (numSets > 0) {
	  // Now repeat for sets.
	  int setInd, setTags, setsTagged = 0;
	  scanRes = fscanf(pFTagFile, "Set %d has %d tags.\n",
			   &setInd, &setTags);
	  while (scanRes == 2) {
	    setsTagged++;
	    iBase_EntitySetHandle set = allSets[setInd];
	    for (int iTag = 0; iTag < setTags; iTag++) {
	      int tagInd;
	      fscanf(pFTagFile, " Tag %d. Value(s): ", &tagInd);
	      iBase_TagHandle tag = allTags[tagInd];
	      int type = allTypes[tagInd];
	      int size = allSizes[tagInd];
	      assert(size == 1 || type == iBase_BYTES);
	      switch (type) {
	      case iBase_INTEGER:
		{
		  int data;
		  fscanf(pFTagFile, "%d", &data);
		  iMesh_setEntSetIntData(instance, set, tag, data, err);
		}
		break;
	      case iBase_DOUBLE:
		{
		  double data;
		  fscanf(pFTagFile, "%lf", &data);
		  iMesh_setEntSetDblData(instance, set, tag, data, err);
		}
		break;
	      case iBase_ENTITY_HANDLE:
		{
		  int tagValInd;
		  fscanf(pFTagFile, "%d", &tagValInd);
		  iBase_EntityHandle data = allHandles[tagValInd];
		  iMesh_setEntSetEHData(instance, set, tag, data, err);
		}
		break;
	      case iBase_BYTES:
		{
		  char data[size];
		  for (int ii = 0; ii < size; ii++) {
		    fscanf(pFTagFile, "%c", &(data[ii]));
		  }
		  iMesh_setEntSetData(instance, set, tag, data, size, err);
		}
		break;
	      } // End of switch over tag type
	    } // End of loop over tags for this setity
	    scanRes = fscanf(pFTagFile, " Set %d has %d tags.\n",
			     &setInd, &setTags);
	  } // End reading tag data for all sets
	  printf("Found %d sets with tags.\n", setsTagged);
	} // Read set tags only if there are sets.
	fclose(pFTagFile);
      } // Done reading tag file
      delete [] allSets;
      free(allHandles);
    } // Done reading sets and tags
  }

  void iMesh_save(iMesh_Instance instance,
		  const iBase_EntitySetHandle entity_set_handle,
		  const char *name_in, const char* options_in,
		  int *err, const int name_len, int options_len)
  {
    // Base public, with switch
    iBase_EntitySetHandle root_set;
    *err = iBase_SUCCESS;
    iMesh_getRootSet(instance, &root_set, err);
    if (*err != iBase_SUCCESS) return;
    char name[1024], options[1024];
    NULL_TERMINATE(name_in, name_len, name);
    NULL_TERMINATE(options_in, options_len, options);

    if (entity_set_handle == root_set) {
      CHECK_FOR_MESH_DATA(instance, err);
      instance->pGB->save(name, options, err);

      // Writing sets.  This requires finding indices for all the
      // entities.  Basically, this uses a binary search in the
      // allHandles array to find where a particular handle is, then
      // pointer arithmetic to convert that to an integer.

      // Get a file name for the set file and open it.
      char setName[1024];
      snprintf(setName, 1024, "%s.sets", name);
      FILE *pFOutFile = fopen(setName, "w");

      iBase_EntitySetHandle *allSetHandles;
      int allSetHandles_allocated = 0, allSetHandles_size;
      iMesh_getEntSets(instance, root_set, 0, &allSetHandles,
		       &allSetHandles_allocated, &allSetHandles_size,
		       err);

      // Start with some summary info about sets, so that they can be
      // created up front on read.
      fprintf(pFOutFile, "Total number of sets: %d\n", allSetHandles_size);

      for (int setInd = 0; setInd < allSetHandles_size; setInd++) {
	iBase_EntitySetHandle setHandle = allSetHandles[setInd];
	int isList;
	iMesh_isList(instance, setHandle, &isList, err);
	fprintf(pFOutFile, "Set #%d; %s\n", setInd,
		isList ? "ordered" : "unordered");
      }

      iBase_EntityHandle *allHandles = NULL;
      int allHandles_allocated = 0, allHandles_size;
      iMesh_getEntities(instance,
			root_set, iBase_ALL_TYPES,
			iMesh_ALL_TOPOLOGIES,
			&allHandles, &allHandles_allocated,
			&allHandles_size, err);
      assert(*err == iBase_SUCCESS);

      // Now write membership and parent-child data
      for (int setInd = 0; setInd < allSetHandles_size; setInd++) {
	iBase_EntitySetHandle setHandle = allSetHandles[setInd];
	fprintf(pFOutFile, "Set #%d\n", setInd);
	// Now iterate over the set to find its entity members
	iMesh_EntityIterator entIter;
	iMesh_initEntIter(instance, setHandle, iBase_ALL_TYPES,
			  iMesh_ALL_TOPOLOGIES, &entIter, err);

	iBase_EntityHandle entHandle;
	int hasData;
	iMesh_getNextEntIter(instance, entIter, &entHandle, &hasData, err);
	fprintf(pFOutFile, "Contains entities: ");
	while (hasData) {
	  int index = findArrayIndex(entHandle, allHandles,
				     allHandles + allHandles_size);
	  int topo;
	  iMesh_getEntTopo(instance, entHandle, &topo, err);
	  fprintf(pFOutFile, "(%d %d)", index, topo);
	  iMesh_getNextEntIter(instance, entIter, &entHandle, &hasData, err);
	}
	fprintf(pFOutFile, "\n");
	iMesh_endEntIter(instance, entIter, err);

	// Now its set members:
	{
	  iBase_EntitySetHandle *mySetHandles;
	  int mySetHandles_allocated = 0, mySetHandles_size;
	  iMesh_getEntSets(instance, setHandle, 0, &mySetHandles,
			   &mySetHandles_allocated, &mySetHandles_size, err);
	  fprintf(pFOutFile, "Set members (%d): ", mySetHandles_size);
	  if (mySetHandles_size > 0) {
	    for (int ii = 0; ii < mySetHandles_size; ii++) {
	      iBase_EntitySetHandle mySH = mySetHandles[ii];
	      int index = findArrayIndex(mySH, allSetHandles,
					 allSetHandles + allSetHandles_size);
	      fprintf(pFOutFile, "%d ", index);
	    }
	  }
	  fprintf(pFOutFile, "\n");
	}

	// Now child sets:
	{
	  iBase_EntitySetHandle *mySetHandles;
	  int mySetHandles_allocated = 0, mySetHandles_size;
	  iMesh_getChldn(instance, setHandle, 0, &mySetHandles,
			 &mySetHandles_allocated, &mySetHandles_size, err);
	  fprintf(pFOutFile, "Child sets (%d): ", mySetHandles_size);
	  if (mySetHandles_size > 0) {
	    for (int ii = 0; ii < mySetHandles_size; ii++) {
	      iBase_EntitySetHandle mySH = mySetHandles[ii];
	      int index = findArrayIndex(mySH, allSetHandles,
					 allSetHandles + allSetHandles_size);
	      fprintf(pFOutFile, "%d ", index);
	    }
	  }
	  fprintf(pFOutFile, "\n");
	}

	// Now parent sets:
	{
	  iBase_EntitySetHandle *mySetHandles;
	  int mySetHandles_allocated = 0, mySetHandles_size;
	  iMesh_getPrnts(instance, setHandle, 0, &mySetHandles,
			 &mySetHandles_allocated, &mySetHandles_size, err);
	  fprintf(pFOutFile, "Parent sets (%d): ", mySetHandles_size);
	  if (mySetHandles_size > 0) {
	    for (int ii = 0; ii < mySetHandles_size; ii++) {
	      iBase_EntitySetHandle mySH = mySetHandles[ii];
	      int index = findArrayIndex(mySH, allSetHandles,
					 allSetHandles + allSetHandles_size);
	      fprintf(pFOutFile, "%d ", index);
	    }
	  }
	  fprintf(pFOutFile, "\n");
	} // Done writing this set
	fprintf(pFOutFile, "\n");
      } // Done writing all sets.
      fclose(pFOutFile);

      // Tags have to be done from somewhere with more direct access to
      // the tag data

      // Get a file name for the tag file and open it.
      char tagName[1024];
      snprintf(tagName, 1024, "%s.tags", name);

      instance->pGB->writeTags(tagName, allHandles, allHandles_size,
			       allSetHandles, allSetHandles_size);

      // Clean up
      free(allHandles);
      free(allSetHandles);
    }
    else {
      *err = iBase_NOT_SUPPORTED;
    }
  }

  void iMesh_getRootSet(iMesh_Instance instance,
			iBase_EntitySetHandle *root_set, int *err)
  {
    // Base public
    *err = iBase_SUCCESS;
    (*root_set) = (iBase_EntitySetHandle)(instance->pGB);
  }

  void iMesh_getGeometricDimension(iMesh_Instance instance,
				   int *geom_dim, int *err)
  {
    // Inline specialized func
    *err = iBase_SUCCESS;
    *geom_dim = instance->pGB->getGeometricDimension();
  }

  void iMesh_setGeometricDimension(iMesh_Instance instance,
				   int geom_dim, int *err)
  {
    *err = iBase_SUCCESS;
    if (instance->pGB->pMesh() == NULL ||
	instance->pGB->pMesh()->iNumVerts() == 0) {
      int geom_dim_now = instance->pGB->getGeometricDimension();
      *err = iBase_SUCCESS;
      if (geom_dim_now != geom_dim) {
	// In this case, kill the old mesh database and create a new one.
	switch (geom_dim) {
	case -3:
	  instance->vInitiMesh(iMesh_Base::eSurface);
	  break;
	case 2:
	  instance->vInitiMesh(iMesh_Base::ePlane);
	  break;
	case 3:
	  instance->vInitiMesh(iMesh_Base::eVolume);
	  break;
	}
      }
    }
    else {
      // In this case, there's already mesh data, and it's too late.
      assert(*err == iBase_SUCCESS);
      *err = iBase_FAILURE;
    }
  }

  void iMesh_getDfltStorage(iMesh_Instance /*instance*/,
			    int *order, int *err)
  {
    // Inline base func
    *err = iBase_SUCCESS;
    *order = iBase_INTERLEAVED;
  }

  void iMesh_getAdjTable (iMesh_Instance instance,
			  int** adjacency_table,
			  /*inout*/ int* adjacency_table_allocated,
			  /*out*/ int* adjacency_table_size, int *err)
  {
    // Derived types
    *err = iBase_SUCCESS;
    TRY_ARRAY_SIZE(adjacency_table, adjacency_table_allocated, 16, int);

    *adjacency_table_size = 16;

    instance->pGB->getAdjTable(*adjacency_table);
  }

  void iMesh_getNumOfType(iMesh_Instance instance,
			  /*in*/ const iBase_EntitySetHandle entity_set_handle,
			  /*in*/ const int entity_type,
			  int *num_type, int *err)
  {
    // Base public
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set_handle, err);

    if (isRootSet(instance, entity_set_handle)) {
      // The case for the entire mesh
      switch (entity_type) {
      case iBase_VERTEX:
	*num_type = (instance->pGB->getNumVertices());
	break;
      case iBase_FACE:
	*num_type = (instance->pGB->getNumTriangles() +
		     instance->pGB->getNumQuadrilaterals());
	break;
      case iBase_REGION:
	*num_type = (instance->pGB->getNumTetrahedra() +
		     instance->pGB->getNumPyramids() +
		     instance->pGB->getNumPrisms() +
		     instance->pGB->getNumHexahedra());
	break;
      case iBase_EDGE:
	*num_type = (instance->pGB->getNumLineSegments());
	break;
      case iBase_ALL_TYPES:
	*num_type = (instance->pGB->getNumVertices() +
		     instance->pGB->getNumLineSegments() +
		     instance->pGB->getNumTriangles() +
		     instance->pGB->getNumQuadrilaterals() +
		     instance->pGB->getNumTetrahedra() +
		     instance->pGB->getNumPyramids() +
		     instance->pGB->getNumPrisms() +
		     instance->pGB->getNumHexahedra());
	break;
      default:
	*err = iBase_INVALID_ENTITY_TYPE;
	*num_type = 0;
	break;
      }
    }
    else {
      // Have an actual entity set argument
      EntitySetBase *pESB = static_cast<EntitySetBase*>(entity_set_handle);
      switch (entity_type) {
      case iBase_VERTEX:
	*num_type =  (pESB->iNumVerts());
	break;
      case iBase_EDGE:
	*num_type =  (pESB->iNumEdges());
	break;
      case iBase_FACE:
	// Pretty cheesey, but works in both 2D and 3D
	*num_type =  (pESB->iNumTriFaces() + pESB->iNumQuadFaces() +
		      pESB->iNumTriCells() + pESB->iNumQuadCells());
	break;
      case iBase_REGION:
	*num_type =  (pESB->iNumTetCells() + pESB->iNumPyrCells() +
		pESB->iNumPrismCells() + pESB->iNumHexCells());
	break;
      case iBase_ALL_TYPES:
	*num_type =  pESB->iNumVerts() + pESB->iNumEdges() +
	  pESB->iNumTriFaces() + pESB->iNumQuadFaces() +
	  pESB->iNumTriCells() + pESB->iNumQuadCells() +
	  pESB->iNumTetCells() + pESB->iNumPyrCells() +
	  pESB->iNumPrismCells() + pESB->iNumHexCells();
	break;
      default:
	*err = iBase_INVALID_ENTITY_TYPE;
	*num_type =  0;
	break;
      }
    }
  }

  void iMesh_getNumOfTopo(iMesh_Instance instance,
			  /*in*/ const iBase_EntitySetHandle entity_set_handle,
			  /*in*/ const int entity_topology,
			  int *num_topo, int *err)
  {
    // Base public
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set_handle, err);

    if (isRootSet(instance, entity_set_handle)) {
      // The case for the entire mesh
      switch (entity_topology) {
      case iMesh_POINT:
	*num_topo = instance->pGB->getNumVertices();
	break;
      case iMesh_LINE_SEGMENT:
	*num_topo = instance->pGB->getNumLineSegments();
	break;
      case iMesh_TRIANGLE:
	*num_topo = instance->pGB->getNumTriangles();
	break;
      case iMesh_QUADRILATERAL:
	*num_topo = instance->pGB->getNumQuadrilaterals();
	break;
      case iMesh_TETRAHEDRON:
	*num_topo = instance->pGB->getNumTetrahedra();
	break;
      case iMesh_PYRAMID:
	*num_topo = instance->pGB->getNumPyramids();
	break;
      case iMesh_PRISM:
	*num_topo = instance->pGB->getNumPrisms();
	break;
      case iMesh_HEXAHEDRON:
	*num_topo = instance->pGB->getNumHexahedra();
	break;
      case iMesh_POLYGON:
      case iMesh_POLYHEDRON:
      case iMesh_SEPTAHEDRON:
	*num_topo = 0;
	break;
      case iMesh_ALL_TOPOLOGIES:
	*num_topo = (instance->pGB->getNumVertices() +
		     instance->pGB->getNumLineSegments() +
		     instance->pGB->getNumTriangles() +
		     instance->pGB->getNumQuadrilaterals() +
		     instance->pGB->getNumTetrahedra() +
		     instance->pGB->getNumPyramids() +
		     instance->pGB->getNumPrisms() +
		     instance->pGB->getNumHexahedra());
	break;
      default:
	*err = iBase_INVALID_ENTITY_TOPOLOGY;
	*num_topo = 0;
	break;
      }
    }
    else {
      // Have an actual entity set argument
      EntitySetBase *pESB = static_cast<EntitySetBase*>(entity_set_handle);
      switch (entity_topology) {
      case iMesh_POINT:
	*num_topo = pESB->iNumVerts();
	break;
      case iMesh_LINE_SEGMENT:
	*num_topo = pESB->iNumEdges();
	break;
      case iMesh_TRIANGLE:
	// This is fairly cheesey, but works for both 2D and 3D, and the
	// cost should be pretty small.
	*num_topo = pESB->iNumTriFaces() + pESB->iNumTriCells();
	break;
      case iMesh_QUADRILATERAL:
	// This is fairly cheesey, but works for both 2D and 3D, and the
	// cost should be pretty small.
	*num_topo = pESB->iNumQuadFaces() + pESB->iNumQuadCells();
	break;
      case iMesh_TETRAHEDRON:
	*num_topo = pESB->iNumTetCells();
	break;
      case iMesh_PYRAMID:
	*num_topo = pESB->iNumPyrCells();
	break;
      case iMesh_PRISM:
	*num_topo = pESB->iNumPrismCells();
	break;
      case iMesh_HEXAHEDRON:
	*num_topo = pESB->iNumHexCells();
	break;
      case iMesh_ALL_TOPOLOGIES:
	*num_topo = (pESB->iNumVerts() + pESB->iNumEdges() +
		     pESB->iNumTriFaces() + pESB->iNumTriCells() +
		     pESB->iNumQuadFaces() + pESB->iNumQuadCells() +
		     pESB->iNumTetCells() + pESB->iNumPyrCells() +
		     pESB->iNumPrismCells() + pESB->iNumHexCells());
	break;
      case iMesh_POLYGON:
      case iMesh_POLYHEDRON:
      case iMesh_SEPTAHEDRON:
	*num_topo = 0;
	break;
      default:
	*err = iBase_INVALID_ENTITY_TOPOLOGY;
	*num_topo = 0;
	break;
      }
    }
  }

  void iMesh_areEHValid(iMesh_Instance instance,
			int doReset, int *areHandlesInvariant, int *err)
  {
    // Base public
    *err = iBase_SUCCESS;
    if (doReset)
      instance->pGB->pMesh()->vPurge();
    *areHandlesInvariant = true;
  }

  void iMesh_getEntities(iMesh_Instance instance,
			 const iBase_EntitySetHandle entity_set_handle,
			 const int entity_type,
			 const int entity_topology,
			 iBase_EntityHandle** entity_handles,
			 int* entity_handles_allocated,
			 int* entity_handles_size,
			 int *err)

  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    EntitySetBase* pESB = static_cast<EntitySetBase*>(entity_set_handle);
    bool isRoot = isRootSet(instance, entity_set_handle);
    bool isOtherSet = instance->pGB->qHasSet(pESB);
    if ( !isRoot && !isOtherSet ) {
      *err = iBase_INVALID_ENTITYSET_HANDLE;
      return;
    }
    CHECK_TYPE_AND_TOPO(entity_type, entity_topology, err);

    if (!isRoot) {
      pESB->vGetEntities(entity_type, entity_topology, entity_handles,
			 entity_handles_allocated, entity_handles_size);
      if (*err != iBase_SUCCESS) return;
    }
    else {
      instance->pGB->getEntities(entity_type, entity_topology, entity_handles,
		       entity_handles_allocated, entity_handles_size,
		       err);
    } // Done dealing with base mesh object
  }

  void iMesh_getVtxArrCoords(iMesh_Instance instance,
				  /*in*/ const iBase_EntityHandle* vertex_handles,
				  /*in*/ const int vertex_handles_size,
				  /*in*/ int storage_order,
				  /*inout*/ double** coords,
				  /*inout*/ int* coords_allocated,
				  /*out*/ int* coords_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);

    for ( int i=0; i<vertex_handles_size; i++ ) {
      CHECK_ENTITY(instance, vertex_handles[i], err);
    }

    // Start by figuring out how much data we'll need to store.
    *coords_size = vertex_handles_size*3;
    TRY_ARRAY_SIZE(coords, coords_allocated, *coords_size, double);

    // Now actually copy the data.
    if (storage_order == iBase_BLOCKED) {
      int iOut0 = 0;
      int iOut1 = iOut0 + vertex_handles_size;
      int iOut2 = iOut1 + vertex_handles_size;
      for (int iVert = 0; iVert < vertex_handles_size; iVert++) {
// #ifndef NDEBUG
	Entity *pE = dynamic_cast<Entity*>(vertex_handles[iVert]);
	if (pE->eEntType() != iBase_VERTEX) {
	  *err = iBase_INVALID_ENTITY_TYPE;
	  return;
	}
	const double * const adCoords = pE->pVVert(0)->adCoords();
// #else
// 	Vert *pV = static_cast<Vert*>(vertex_handles[iVert]);
// 	const double * const adCoords = pV->adCoords();
// #endif
	(*coords)[iOut0++] = adCoords[0];
	(*coords)[iOut1++] = adCoords[1];
	(*coords)[iOut2++] = adCoords[2];
      }
    }
    else {
      int iOut = 0;
      for (int iVert = 0; iVert < vertex_handles_size; iVert++) {
// #ifndef NDEBUG
	Entity *pE = static_cast<Entity*>(vertex_handles[iVert]);
	if (pE->eEntType() != iBase_VERTEX) {
	  *err = iBase_INVALID_ENTITY_TYPE;
	  return;
	}
	const double * const adCoords = pE->pVVert(0)->adCoords();
	(*coords)[iOut++] = adCoords[0];
	(*coords)[iOut++] = adCoords[1];
	(*coords)[iOut++] = adCoords[2];
// #else
// 	Vert *pV = static_cast<Vert*>(vertex_handles[iVert]);
// 	const double * const adCoords = pV->adCoords();
// 	(*coords)[iOut++] = adCoords[0];
// 	(*coords)[iOut++] = adCoords[1];
// 	(*coords)[iOut++] = adCoords[2];
// #endif
      }
    }
  }

  void iMesh_initEntArrIter(iMesh_Instance instance,
			    /*in*/ const iBase_EntitySetHandle entity_set_handle,
			    /*in*/ const int requested_entity_type,
			    /*in*/ const int requested_entity_topology,
			    /*in*/ const int requested_array_size,
			    /*out*/ iMesh_EntityArrIterator* entArr_iterator,
			    int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set_handle, err);
    CHECK_TYPE_AND_TOPO(requested_entity_type, requested_entity_topology, err);
    Workset* pWSNew;

    try {
      if (isRootSet(instance, entity_set_handle)) {
	pWSNew = new MeshWorkset(instance->pGB->pMesh(), requested_entity_type,
				 requested_entity_topology,
				 requested_array_size);
      }
      else {
	EntitySetBase *pESB = static_cast<EntitySetBase*>(entity_set_handle);
	if (pESB->qIsOrdered()) {
	  pWSNew = new EntityListWorkset(dynamic_cast<EntityList*>(pESB),
					 requested_entity_type,
					 requested_entity_topology,
					 requested_array_size);
	}
	else {
	  pWSNew = new EntitySetWorkset(dynamic_cast<EntitySet*>(pESB),
					requested_entity_type,
					requested_entity_topology,
					requested_array_size);
	}
      }
    }
    catch (iBase_ErrorType eErr)
      {
	*err = eErr;
	return;
      }

    assert( pWSNew );
    instance->pGB->addWorkset( pWSNew );
    *entArr_iterator = pWSNew;
  }


  void iMesh_getNextEntArrIter(iMesh_Instance instance,
				    /*in*/ iMesh_EntityArrIterator entArr_iterator,
				    /*inout*/ iBase_EntityHandle** entity_handles,
				    /*inout*/ int* entity_handles_allocated,
				    /*out*/ int* entity_handles_size,
				    int *data_left, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    Workset * pWS = static_cast<Workset*>(entArr_iterator);
    CHECK_ENTARR_ITER(instance, pWS, err);
    TRY(*data_left = pWS->qGetNextBlock(entity_handles, \
					entity_handles_allocated, \
					entity_handles_size););
  }

  void iMesh_resetEntArrIter(iMesh_Instance instance,
			     /*in*/ iMesh_EntityArrIterator entArr_iterator,
			     int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    Workset * pWS = static_cast<Workset*>(entArr_iterator);
    CHECK_ENTARR_ITER(instance, pWS, err);
    TRY(pWS->qReset(););
  }

  void iMesh_endEntArrIter(iMesh_Instance instance,
			   /*in*/ iMesh_EntityArrIterator entArr_iterator,
			   int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    Workset * pWS = static_cast<Workset*>(entArr_iterator);
    CHECK_ENTARR_ITER(instance, pWS, err);
    instance->pGB->removeWorkset(pWS, err);
    delete pWS;
  }

  void iMesh_getEntArrTopo(iMesh_Instance instance,
				/*in*/ const iBase_EntityHandle* entity_handles,
				/*in*/ const int entity_handles_size,
				/*inout*/ int** topology,
				/*inout*/ int* topology_allocated,
				/*out*/ int* topology_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
#ifndef NDEBUG
    for ( int i=0; i<entity_handles_size; i++ ) {
      CHECK_ENTITY(instance, entity_handles[i], err);
    }
#endif

    TRY_ARRAY_SIZE(topology, topology_allocated, entity_handles_size, int);
    for (int i = 0; i < entity_handles_size; i++) {
      Entity *pE = static_cast<Entity*> (entity_handles[i]);
      (*topology)[i] = pE->eEntTopology();
    }
    *topology_size = entity_handles_size;
  }

  void iMesh_getEntArrType(iMesh_Instance instance,
				/*in*/ const iBase_EntityHandle* entity_handles,
				/*in*/ const int entity_handles_size,
				/*inout*/ int** type,
				/*inout*/ int* type_allocated,
				/*out*/ int* type_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
#ifndef NDEBUG
    for ( int i=0; i<entity_handles_size; i++ ) {
      CHECK_ENTITY(instance, entity_handles[i], err);
    }
#endif

    TRY_ARRAY_SIZE(type, type_allocated, entity_handles_size, int);
    for (int i = 0; i < entity_handles_size; i++) {
      Entity *pE = static_cast<Entity*> (entity_handles[i]);
      (*type)[i] = pE->eEntType();
    }
    *type_size = entity_handles_size;
  }

  void iMesh_getEntArrAdj(iMesh_Instance instance,
			       /*in*/ const iBase_EntityHandle* entity_handles,
			       /*in*/ const int entity_handles_size,
			       /*in*/ const int entity_type_requested,
			       /*inout*/ iBase_EntityHandle** adj_entity_handles,
			       /*inout*/ int* adj_entity_handles_allocated,
			       /*out*/ int* adj_entity_handles_size,
			       /*inout*/ int** offset,
			       /*inout*/ int* offset_allocated,
			       /*out*/ int* offset_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);

#ifndef NDEBUG
    for ( int i=0; i<entity_handles_size; i++ ) {
      CHECK_ENTITY(instance, entity_handles[i], err);
    }
#endif

    TRY_ARRAY_SIZE(offset, offset_allocated, entity_handles_size+1, int);
    *offset_size = entity_handles_size+1;

    instance->pGB->getEntArrAdj(entity_handles, entity_handles_size,
			   entity_type_requested,
			   adj_entity_handles, adj_entity_handles_allocated,
			   adj_entity_handles_size,
			   offset, offset_allocated, offset_size, err);
  } // End of iMesh_getEntArrAdj

  void iMesh_createEntSet(iMesh_Instance instance,
			       /*in*/ const int isList,
			       /*out*/ iBase_EntitySetHandle* entity_set_created, int *err)
  {
    *err = iBase_SUCCESS;
    //    CHECK_FOR_MESH_DATA(instance, err);

    EntitySetBase* pESB;
    int iDim;
    iMesh_getGeometricDimension(instance, &iDim, err);

    try {
      if (isList) {
	pESB = new EntityList(iDim);
      }
      else {
	pESB = new EntitySet(iDim);
      }
    } catch (...) {
      // Hard to imagine anything but a memory allocation error...
      *err = iBase_MEMORY_ALLOCATION_FAILED;
    }
    assert(pESB);
    instance->pGB->addEntSet(pESB);
    *entity_set_created = pESB;
  }

  void iMesh_destroyEntSet(iMesh_Instance instance,
				/*in*/ iBase_EntitySetHandle entity_set, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set, err);
    TRY(instance->pGB->destroyEntSet(entity_set););
  }


  void iMesh_isList(iMesh_Instance instance,
			 /*in*/ const iBase_EntitySetHandle entity_set,
			 int *is_list, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set, err);
    *is_list = instance->pGB->isList(entity_set);
  }

  void iMesh_getNumEntSets(iMesh_Instance instance,
				/*in*/ const iBase_EntitySetHandle entity_set_handle,
				/*in*/ const int num_hops,
				int *num_sets, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set_handle, err);
    TRY(*num_sets = instance->pGB->getNumEntSets(entity_set_handle, num_hops););
  }

  void iMesh_getEntSets(iMesh_Instance instance,
			     /*in*/ const iBase_EntitySetHandle entity_set_handle,
			     /*in*/ const int num_hops,
			     /*out*/ iBase_EntitySetHandle** contained_set_handles,
			     /*out*/ int* contained_set_handles_allocated,
			     /*out*/ int* contained_set_handles_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set_handle, err);
    TRY(instance->pGB->getEntSets(entity_set_handle, num_hops, contained_set_handles,\
			contained_set_handles_allocated, \
			contained_set_handles_size););
  }

  void iMesh_addEntToSet(iMesh_Instance instance,
			      /*in*/ const iBase_EntityHandle entity_handle,
			      /*inout*/ iBase_EntitySetHandle entity_set, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);
    CHECK_ENTITY_SET(instance, entity_set, err);
    TRY(instance->pGB->addEntToSet(entity_handle, entity_set););
  }

  void iMesh_rmvEntFromSet(iMesh_Instance instance,
				/*in*/ const iBase_EntityHandle entity_handle,
				/*inout*/ iBase_EntitySetHandle entity_set, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);
    CHECK_ENTITY_SET(instance, entity_set, err);
    TRY(instance->pGB->rmvEntFromSet(entity_handle, entity_set););
  }

  void iMesh_addEntArrToSet(iMesh_Instance instance,
				 /*in*/ const iBase_EntityHandle* entity_handles,
				 /*in*/ const int entity_handles_size,
				 /*inout*/ iBase_EntitySetHandle entity_set, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set, err);
    TRY(instance->pGB->addEntArrToSet(entity_handles, entity_handles_size, entity_set););
  }

  void iMesh_rmvEntArrFromSet(iMesh_Instance instance,
				   /*in*/ const iBase_EntityHandle* entity_handles,
				   /*in*/ const int entity_handles_size,
				   /*inout*/ iBase_EntitySetHandle entity_set, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set, err);
    TRY(instance->pGB->rmvEntArrFromSet(entity_handles, entity_handles_size, entity_set););
  }

  void iMesh_addEntSet(iMesh_Instance instance,
		       /*in*/ iBase_EntitySetHandle entity_set_to_add,
		       /*inout*/ iBase_EntitySetHandle entity_set_handle,
		       int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set_to_add, err);
    CHECK_ENTITY_SET(instance, entity_set_handle, err);
    TRY(instance->pGB->addEntSet(entity_set_to_add, entity_set_handle););
  }

  void iMesh_rmvEntSet(iMesh_Instance instance,
			    /*in*/ const iBase_EntitySetHandle entity_set_to_remove,
			    /*inout*/ iBase_EntitySetHandle entity_set_handle, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set_to_remove, err);
    CHECK_ENTITY_SET(instance, entity_set_handle, err);
    TRY(instance->pGB->rmvEntSet(entity_set_to_remove, entity_set_handle););
  }

  void iMesh_isEntContained(iMesh_Instance instance,
			    /*in*/ const iBase_EntitySetHandle containing_entity_set,
			    /*in*/ const iBase_EntityHandle contained_entity,
			    int *is_contained, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, containing_entity_set, err);
    CHECK_ENTITY(instance, contained_entity, err);
    TRY(*is_contained = instance->pGB->isEntContained(containing_entity_set, contained_entity););
  }

  void iMesh_isEntArrContained( iMesh_Instance instance,
			 /*in*/ iBase_EntitySetHandle containing_set,
			 /*in*/ const iBase_EntityHandle* entity_handles,
			 /*in*/ int num_entity_handles,
		      /*inout*/ int** is_contained,
		      /*inout*/ int* is_contained_allocated,
			/*out*/ int* is_contained_size,
			/*out*/ int* err )
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, containing_set, err);
    TRY_ARRAY_SIZE(is_contained, is_contained_allocated, num_entity_handles,
		   int);
    *is_contained_size = num_entity_handles;
    for (int i = 0; i < num_entity_handles; i++) {
      CHECK_ENTITY(instance, entity_handles[i], err);
      TRY((*is_contained)[i] =
	  instance->pGB->isEntContained(containing_set, entity_handles[i]););
    }
  }

  void iMesh_isEntSetContained(iMesh_Instance instance,
				    /*in*/ const iBase_EntitySetHandle containing_entity_set,
				    /*in*/ const iBase_EntitySetHandle contained_entity_set,
				    int *is_contained, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, containing_entity_set, err);
    CHECK_ENTITY_SET(instance, contained_entity_set, err);
    TRY(*is_contained = instance->pGB->isEntSetContained(containing_entity_set, contained_entity_set););
  }

  void iMesh_addPrntChld(iMesh_Instance instance,
			 /*inout*/ iBase_EntitySetHandle parent_entity_set,
			 /*inout*/ iBase_EntitySetHandle child_entity_set,
			 int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, parent_entity_set, err);
    CHECK_ENTITY_SET(instance, child_entity_set, err);
    TRY(instance->pGB->addPrntChld(parent_entity_set, child_entity_set););
  }

  void iMesh_rmvPrntChld(iMesh_Instance instance,
			 /*inout*/ iBase_EntitySetHandle parent_entity_set,
			 /*inout*/ iBase_EntitySetHandle child_entity_set,
			 int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, parent_entity_set, err);
    CHECK_ENTITY_SET(instance, child_entity_set, err);
    TRY(instance->pGB->rmvPrntChld(parent_entity_set, child_entity_set););
  }

  void iMesh_isChildOf(iMesh_Instance instance,
			    /*in*/ const iBase_EntitySetHandle parent_entity_set,
			    /*in*/ const iBase_EntitySetHandle child_entity_set,
			    int *is_child, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, parent_entity_set, err);
    CHECK_ENTITY_SET(instance, child_entity_set, err);
    TRY(*is_child = instance->pGB->isChildOf(parent_entity_set, child_entity_set););
  }

  void iMesh_getNumChld(iMesh_Instance instance,
			/*in*/ const iBase_EntitySetHandle entity_set,
			/*in*/ const int num_hops,
			int *num_child, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set, err);
    TRY(*num_child = instance->pGB->getNumChld(entity_set, num_hops););
  }



  void iMesh_getNumPrnt(iMesh_Instance instance,
			     /*in*/ const iBase_EntitySetHandle entity_set,
			     /*in*/ const int num_hops,
			     int *num_parent, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set, err);
    TRY(*num_parent = instance->pGB->getNumPrnt(entity_set, num_hops););
  }


  void iMesh_getChldn(iMesh_Instance instance,
			   /*in*/ const iBase_EntitySetHandle from_entity_set,
			   /*in*/ const int num_hops,
			   /*out*/ iBase_EntitySetHandle** entity_set_handles,
			   /*out*/ int* entity_set_handles_allocated,
			   /*out*/ int* entity_set_handles_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, from_entity_set, err);
    TRY(instance->pGB->getChldn (from_entity_set, num_hops, entity_set_handles,\
		       entity_set_handles_allocated, entity_set_handles_size););
  }


  void iMesh_getPrnts(iMesh_Instance instance,
			   /*in*/ const iBase_EntitySetHandle from_entity_set,
			   /*in*/ const int num_hops,
			   /*out*/ iBase_EntitySetHandle** entity_set_handles,
			   /*out*/ int* entity_set_handles_allocated,
			   /*out*/ int* entity_set_handles_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, from_entity_set, err);
    TRY(instance->pGB->getPrnts (from_entity_set, num_hops, entity_set_handles,\
		       entity_set_handles_allocated, entity_set_handles_size););
  }


  void iMesh_setVtxArrCoords(iMesh_Instance instance,
			     /*in*/ const iBase_EntityHandle* vertex_handles,
			     /*in*/ const int vertex_handles_size,
			     /*in*/ const int storage_order,
			     /*in*/ const double* new_coords,
			     /*in*/ const int new_coords_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);

#ifndef NDEBUG
    for ( int i=0; i<vertex_handles_size; i++ ) {
      CHECK_ENTITY(instance, vertex_handles[i], err);
    }
#endif
    if ((storage_order != iBase_BLOCKED &&
	 storage_order != iBase_INTERLEAVED) ||
	new_coords_size != 3*vertex_handles_size) {
      *err = iBase_INVALID_ARGUMENT;
      return;
    }

    int iBase = 0;
    for (int i = 0; i < vertex_handles_size; i++) {
      double adNewCoords[3];
      if (storage_order == iBase_BLOCKED) {
	adNewCoords[0] = new_coords[iBase];
	adNewCoords[1] = new_coords[iBase + vertex_handles_size];
	adNewCoords[2] = new_coords[iBase + 2*vertex_handles_size];
	iBase++;
      }
      else {
	adNewCoords[0] = new_coords[iBase];
	adNewCoords[1] = new_coords[iBase + 1];
	adNewCoords[2] = new_coords[iBase + 2];
	iBase += 3;
      }

      Entity *pEVertex =static_cast<Entity*> (vertex_handles[i]);
      Vert *pV = dynamic_cast<Vert*>(pEVertex);
      if (pV == pVInvalidVert) {
	*err = iBase_INVALID_ENTITY_HANDLE;
	continue;
      }
      pV->vSetCoords(3, adNewCoords);
    } // Done with loop over verts
  }

  void iMesh_createVtxArr(iMesh_Instance instance,
			  /*in*/ const int num_verts,
			  /*in*/ const int storage_order,
			  /*in*/ const double* new_coords,
			  /*in*/ const int new_coords_size,
			  /*inout*/ iBase_EntityHandle** new_vertex_handles,
			  /*inout*/ int* new_vertex_handles_allocated,
			  /*inout*/ int* new_vertex_handles_size, int *err)
  {
    *err = iBase_SUCCESS;

    assert(new_coords_size == 3*num_verts);

    TRY_ARRAY_SIZE(new_vertex_handles, new_vertex_handles_allocated, num_verts,
		   iBase_EntityHandle);
    *new_vertex_handles_size = num_verts;

    if (instance->pGB->pMesh() == NULL) {
      instance->pGB->vCreateMesh();
    }

    int iBase = 0;
    for (int i = 0; i < num_verts; i++) {
      double adCoords[3];
      switch (storage_order) {
      case iBase_INTERLEAVED:
	{
	  adCoords[0] = new_coords[iBase];
	  adCoords[1] = new_coords[iBase + 1];
	  adCoords[2] = new_coords[iBase + 2];
	  iBase += 3;
	}
      break;
    case iBase_BLOCKED:
      {
	adCoords[0] = new_coords[iBase];
	adCoords[1] = new_coords[iBase + num_verts];
	adCoords[2] = new_coords[iBase + 2*num_verts];
	iBase++;
      }
      break;
      default:
	*err = iBase_INVALID_ARGUMENT;
	break;
      }
      Vert *pV = instance->pGB->createVert(adCoords[0], adCoords[1],
						    adCoords[2]);
      (*new_vertex_handles)[i] = pV;
    }
  }

  void iMesh_createEntArr(iMesh_Instance instance,
			       /*in*/ const int new_ent_topology,
			       /*in*/ const iBase_EntityHandle* lower_order_ent_handles,
			       /*in*/ const int lower_order_ent_handles_size,
			       /*out*/ iBase_EntityHandle** new_ent_handles,
			       /*out*/ int* new_ent_handles_allocated,
			       /*out*/ int* new_ent_handles_size,
			       /*inout*/ int** status,
			       /*inout*/ int* status_allocated,
			       /*out*/ int* status_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
#ifndef NDEBUG
    for ( int i=0; i<lower_order_ent_handles_size; i++ ) {
      CHECK_ENTITY(instance, lower_order_ent_handles[i], err);
    }
#endif

    instance->pGB->createEntArr
      (new_ent_topology,
       lower_order_ent_handles, lower_order_ent_handles_size,
       new_ent_handles, new_ent_handles_allocated, new_ent_handles_size,
       status, status_allocated, status_size, err);
  }



  void iMesh_deleteEntArr(iMesh_Instance instance,
			  /*in*/ const iBase_EntityHandle* entity_handles,
			  /*in*/ const int entity_handles_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);

#ifndef NDEBUG
    for ( int i=0; i<entity_handles_size; i++ ) {
      CHECK_ENTITY(instance, entity_handles[i], err);
    }
#endif

    int iLast = entity_handles_size - 1;
    for (int i = 0; i <= iLast; i++) {
      iMesh_deleteEnt(instance, entity_handles[i], err);
    }
  }

  void iMesh_createTag(iMesh_Instance instance,
			    /*in*/ const char* tag_name_in,
			    /*in*/ const int tag_size,
			    /*in*/ const int tag_type,
			    /*out*/ iBase_TagHandle* tag_handle, int *err,
			    /*in*/ const int tag_name_len)
  {
    *err = iBase_SUCCESS;
    char tag_name[1024];
    NULL_TERMINATE(tag_name_in, tag_name_len, tag_name);
    TRY(instance->pGB->createTag(tag_name, tag_name_len, tag_size, tag_type, tag_handle););
  }

  void iMesh_destroyTag(iMesh_Instance instance,
			     /*in*/ iBase_TagHandle tag_handle,
			     /*in*/ const int forced, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_TAG(instance, tag_handle, err);
    TRY(instance->pGB->destroyTag(tag_handle, forced););
  }

  void iMesh_getTagName(iMesh_Instance instance,
			/*in*/ const iBase_TagHandle tag_handle,
			/*out*/ char *name, int *err,
			int name_len)
  {
    *err = iBase_SUCCESS;
    // No need to null terminate name here, as it's got intent out.
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_TAG(instance, tag_handle, err);
    TRY(instance->pGB->getTagName(tag_handle, name, name_len););
  }

  void iMesh_getTagSizeValues(iMesh_Instance instance,
				   /*in*/ const iBase_TagHandle tag_handle,
				   int *tag_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_TAG(instance, tag_handle, err);
    TRY(*tag_size = instance->pGB->getTagSizeValues(tag_handle););
  }


  void iMesh_getTagSizeBytes(iMesh_Instance instance,
				  /*in*/ const iBase_TagHandle tag_handle,
				  int *tag_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_TAG(instance, tag_handle, err);
    TRY(*tag_size = instance->pGB->getTagSizeBytes(tag_handle););
  }


  void iMesh_getTagHandle(iMesh_Instance instance,
			       /*in*/ const char* tag_name_in,
			       iBase_TagHandle *tag_handle, int *err,
			       int tag_name_len)
  {
    *err = iBase_SUCCESS;
    char tag_name[1024];
    NULL_TERMINATE(tag_name_in, tag_name_len, tag_name);
    TRY(*tag_handle = instance->pGB->getTagHandle (tag_name, tag_name_len););
  }


  void iMesh_getTagType(iMesh_Instance instance,
			     /*in*/ const iBase_TagHandle tag_handle,
			     int *tag_type, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_TAG(instance, tag_handle, err);
    TRY(*tag_type = instance->pGB->getTagType(tag_handle););
  }



  void iMesh_setEntSetData(iMesh_Instance instance,
				/*in*/ iBase_EntitySetHandle entity_set_handle,
				/*in*/ const iBase_TagHandle tag_handle,
				/*in*/ const char* tag_value,
				/*in*/ const int tag_value_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set_handle, err);
    CHECK_TAG(instance, tag_handle, err);

    TRY(instance->pGB->setEntSetData ( entity_set_handle, tag_handle, tag_value,\
			     tag_value_size););
  }



  void iMesh_setEntSetIntData(iMesh_Instance instance,
				   /*in*/ iBase_EntitySetHandle entity_set,
				   /*in*/ const iBase_TagHandle tag_handle,
				   /*in*/ const int tag_value, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set, err);
    CHECK_TAG(instance, tag_handle, err);

    TRY(instance->pGB->setEntSetIntData ( entity_set, tag_handle, tag_value););
  }



  void iMesh_setEntSetDblData(iMesh_Instance instance,
				   /*in*/ iBase_EntitySetHandle entity_set,
				   /*in*/ const iBase_TagHandle tag_handle,
				   /*in*/ const double tag_value, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set, err);
    CHECK_TAG(instance, tag_handle, err);

    TRY(instance->pGB->setEntSetDblData ( entity_set, tag_handle, tag_value););
  }


  void iMesh_setEntSetEHData(iMesh_Instance instance,
			     /*in*/ iBase_EntitySetHandle entity_set,
			     /*in*/ const iBase_TagHandle tag_handle,
			     /*in*/ const iBase_EntityHandle tag_value,
			     int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set, err);
    CHECK_ENTITY(instance, tag_value, err);
    CHECK_TAG(instance, tag_handle, err);

    TRY(instance->pGB->setEntSetEHData ( entity_set, tag_handle, tag_value););
  }



  void iMesh_getEntSetData(iMesh_Instance instance,
				/*in*/ const iBase_EntitySetHandle entity_set_handle,
				/*in*/ const iBase_TagHandle tag_handle,
				/*inout*/ char** tag_value,
				/*inout*/ int* tag_value_allocated,
				/*inout*/ int* tag_value_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set_handle, err);
    CHECK_TAG(instance, tag_handle, err);

    TRY(instance->pGB->getEntSetData ( entity_set_handle, tag_handle, tag_value,\
			     tag_value_allocated, tag_value_size););
  }


  void iMesh_getEntSetIntData(iMesh_Instance instance,
				   /*in*/ const iBase_EntitySetHandle entity_set,
				   /*in*/ const iBase_TagHandle tag_handle,
				   int *out_data, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set, err);
    CHECK_TAG(instance, tag_handle, err);

    TRY(*out_data = instance->pGB->getEntSetIntData ( entity_set, tag_handle););
  }


  void iMesh_getEntSetDblData(iMesh_Instance instance,
				   /*in*/ const iBase_EntitySetHandle entity_set,
				   /*in*/ const iBase_TagHandle tag_handle,
				   double *out_data, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set, err);
    CHECK_TAG(instance, tag_handle, err);

    TRY(*out_data = instance->pGB->getEntSetDblData ( entity_set, tag_handle););
  }


  void iMesh_getEntSetEHData(iMesh_Instance instance,
				  /*in*/ const iBase_EntitySetHandle entity_set,
				  /*in*/ const iBase_TagHandle tag_handle,
				  iBase_EntityHandle *out_data, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set, err);
    CHECK_TAG(instance, tag_handle, err);

    TRY(*out_data = instance->pGB->getEntSetEHData ( entity_set, tag_handle););
  }


  void iMesh_getAllEntSetTags(iMesh_Instance instance,
			      /*in*/ const iBase_EntitySetHandle entity_set_handle,
			      /*out*/ iBase_TagHandle** tag_handles,
			      /*out*/ int* tag_handles_allocated,
			      /*out*/ int* tag_handles_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set_handle, err);

    TRY(instance->pGB->getAllEntSetTags ( entity_set_handle, tag_handles, \
				tag_handles_allocated, tag_handles_size););
  }


  void iMesh_rmvEntSetTag(iMesh_Instance instance,
			       /*in*/ iBase_EntitySetHandle entity_set_handle,
			       /*in*/ const iBase_TagHandle tag_handle, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set_handle, err);
    CHECK_TAG(instance, tag_handle, err);

    TRY(instance->pGB->rmvEntSetTag ( entity_set_handle, tag_handle););
  }


  void iMesh_setVtxCoord(iMesh_Instance instance,
			 /*in*/ iBase_EntityHandle vertex_handle,
			 /*in*/ const double x, /*in*/ const double y,
			 /*in*/ const double z, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, vertex_handle, err);

#ifndef NDEBUG
    int iDim;
    iMesh_getGeometricDimension(instance, &iDim, err);
    if (iDim == 2 && z != 0) {
      *err = iBase_INVALID_ARGUMENT;
      return;
    }
#endif
    double adNewCoords[] = {x, y, z};
    Entity *pEVertex =static_cast<Entity*> (vertex_handle);
    Vert *pV = dynamic_cast<Vert*>(pEVertex);
    if (pV == pVInvalidVert)
      *err = iBase_INVALID_ENTITY_HANDLE;
    else
      pV->vSetCoords(3, adNewCoords);
  }

  void iMesh_createVtx(iMesh_Instance instance,
			    /*in*/ const double x, /*in*/ const double y,
			    /*in*/ const double z,
			    /*out*/ iBase_EntityHandle* new_vertex_handle, int *err)
  {
    *err = iBase_SUCCESS;

    if (instance->pGB->pMesh() == NULL) {
      instance->pGB->vCreateMesh();
    }

    Vert *pV = instance->pGB->createVert(x, y, z);
    *new_vertex_handle = pV;
  }

  void iMesh_createEnt(iMesh_Instance instance,
			    /*in*/ const int new_entity_topology,
			    /*in*/ const iBase_EntityHandle* lower_order_entity_handles,
			    /*in*/ const int lower_order_entity_handles_size,
			    /*out*/ iBase_EntityHandle* new_entity_handle,
			    /*out*/ int* status, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
#ifndef NDEBUG
    for ( int i=0; i<lower_order_entity_handles_size; i++ ) {
      CHECK_ENTITY(instance, lower_order_entity_handles[i], err);
    }
#endif

    instance->pGB->createEnt(new_entity_topology, lower_order_entity_handles,
		   lower_order_entity_handles_size,
		   new_entity_handle, status, err);
  }

  void iMesh_deleteEnt(iMesh_Instance instance,
			    /*in*/ iBase_EntityHandle entity_handle, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);

    instance->pGB->deleteEnt(entity_handle, err);
  }

  void iMesh_getArrData(iMesh_Instance instance,
			/*in*/ const iBase_EntityHandle* entity_handles,
			/*in*/ const int entity_handles_size,
			/*in*/ const iBase_TagHandle tag_handle,
			/*inout*/ char** tag_values,
			/*inout*/int* tag_values_allocated,
			/*out*/ int* tag_values_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_TAG(instance, tag_handle, err);
    TRY(instance->pGB->getArrData(entity_handles, entity_handles_size, tag_handle,\
			tag_values, tag_values_allocated, tag_values_size););
  }


  void iMesh_getIntArrData(iMesh_Instance instance,
			   /*in*/ const iBase_EntityHandle* entity_handles,
			   /*in*/ const int entity_handles_size,
			   /*in*/ const iBase_TagHandle tag_handle,
			   /*inout*/ int** tag_values,
			   /*inout*/ int* tag_values_allocated,
			   /*out*/ int* tag_values_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_TAG(instance, tag_handle, err);
    TRY(instance->pGB->getIntArrData(entity_handles, entity_handles_size, tag_handle,\
			   tag_values, tag_values_allocated, tag_values_size););
  }


  void iMesh_getDblArrData(iMesh_Instance instance,
			   /*in*/ const iBase_EntityHandle* entity_handles,
			   /*in*/ const int entity_handles_size,
			   /*in*/ const iBase_TagHandle tag_handle,
			   /*inout*/ double** tag_values,
			   /*inout*/ int* tag_values_allocated,
			   /*out*/ int* tag_values_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_TAG(instance, tag_handle, err);
    TRY(instance->pGB->getDblArrData(entity_handles, entity_handles_size, tag_handle,\
			   tag_values, tag_values_allocated, tag_values_size););
  }


  void iMesh_getEHArrData(iMesh_Instance instance,
			  /*in*/ const iBase_EntityHandle* entity_handles,
			  /*in*/ const int entity_handles_size,
			  /*in*/ const iBase_TagHandle tag_handle,
			  /*inout*/ iBase_EntityHandle** tag_values,
			  /*inout*/ int* tag_values_allocated,
			  /*out*/ int* tag_values_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_TAG(instance, tag_handle, err);
    TRY(instance->pGB->getEHArrData (entity_handles, entity_handles_size, tag_handle,\
			   tag_values, tag_values_allocated, tag_values_size););
  }


  void iMesh_setArrData(iMesh_Instance instance,
			/*in*/ const iBase_EntityHandle* entity_handles,
			/*in*/ const int entity_handles_size,
			/*in*/ const iBase_TagHandle tag_handle,
			/*in*/ const char* tag_values,
			/*in*/ const int tag_values_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_TAG(instance, tag_handle, err);
    TRY(instance->pGB->setArrData ( entity_handles, entity_handles_size, tag_handle,\
			  tag_values, tag_values_size););
  }


  void iMesh_setIntArrData(iMesh_Instance instance,
			   /*in*/ const iBase_EntityHandle* entity_handles,
			   /*in*/ const int entity_handles_size,
			   /*in*/ const iBase_TagHandle tag_handle,
			   /*in*/ const int* tag_values,
			   /*in*/ const int tag_values_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_TAG(instance, tag_handle, err);

    TRY(instance->pGB->setIntArrData ( entity_handles, entity_handles_size, tag_handle, \
			     tag_values, tag_values_size););
  }


  void iMesh_setDblArrData(iMesh_Instance instance,
			   /*in*/ const iBase_EntityHandle* entity_handles,
			   /*in*/ const int entity_handles_size,
			   /*in*/ const iBase_TagHandle tag_handle,
			   /*in*/ const double* tag_values,
			   /*in*/ const int tag_values_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_TAG(instance, tag_handle, err);
    TRY(instance->pGB->setDblArrData ( entity_handles, entity_handles_size, tag_handle,
			     tag_values, tag_values_size););
  }


  void iMesh_setEHArrData(iMesh_Instance instance,
			  /*in*/ const iBase_EntityHandle* entity_handles,
			  /*in*/ const int entity_handles_size,
			  /*in*/ const iBase_TagHandle tag_handle,
			  /*in*/ const iBase_EntityHandle* tag_values,
			  /*in*/ const int tag_values_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_TAG(instance, tag_handle, err);

    TRY(instance->pGB->setEHArrData ( entity_handles, entity_handles_size, tag_handle,\
			    tag_values, tag_values_size););
  }


  void iMesh_rmvArrTag(iMesh_Instance instance,
		       /*in*/ const iBase_EntityHandle* entity_handles,
		       /*in*/ const int entity_handles_size,
		       /*in*/ const iBase_TagHandle tag_handle, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_TAG(instance, tag_handle, err);

    TRY(instance->pGB->rmvArrTag ( entity_handles, entity_handles_size, tag_handle););
  }


  void iMesh_getData(iMesh_Instance instance,
			  /*in*/ const iBase_EntityHandle entity_handle,
			  /*in*/ const iBase_TagHandle tag_handle,
			  /*inout*/ char** tag_value,
			  /*inout*/ int *tag_value_allocated,
			  /*out*/ int *tag_value_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);
    CHECK_TAG(instance, tag_handle, err);
    TRY(instance->pGB->getData(entity_handle, tag_handle, tag_value,\
		     tag_value_allocated, tag_value_size););
  }


  void iMesh_getIntData(iMesh_Instance instance,
			/*in*/ const iBase_EntityHandle entity_handle,
			/*in*/ const iBase_TagHandle tag_handle,
			int *out_data, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);
    CHECK_TAG(instance, tag_handle, err);
    TRY(*out_data = instance->pGB->getIntData(entity_handle, tag_handle););
  }


  void iMesh_getDblData(iMesh_Instance instance,
			     /*in*/ const iBase_EntityHandle entity_handle,
			     /*in*/ const iBase_TagHandle tag_handle,
			     double *out_data, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);
    CHECK_TAG(instance, tag_handle, err);
    TRY(*out_data = instance->pGB->getDblData(entity_handle, tag_handle););
  }


  void iMesh_getEHData(iMesh_Instance instance,
			    /*in*/ const iBase_EntityHandle entity_handle,
			    /*in*/ const iBase_TagHandle tag_handle,
			    iBase_EntityHandle *out_data, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);
    CHECK_TAG(instance, tag_handle, err);
    TRY(*out_data = instance->pGB->getEHData(entity_handle, tag_handle););
  }


  void iMesh_setData(iMesh_Instance instance,
			  /*in*/ iBase_EntityHandle entity_handle,
			  /*in*/ const iBase_TagHandle tag_handle,
			  /*in*/ const char* tag_value,
			  /*in*/ const int tag_value_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);
    CHECK_TAG(instance, tag_handle, err);
    TRY(instance->pGB->setData(entity_handle, tag_handle, tag_value, tag_value_size););
  }


  void iMesh_setIntData(iMesh_Instance instance,
			     /*in*/ iBase_EntityHandle entity_handle,
			     /*in*/ const iBase_TagHandle tag_handle,
			     /*in*/ const int tag_value, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);
    CHECK_TAG(instance, tag_handle, err);
    TRY(instance->pGB->setIntData(entity_handle, tag_handle, tag_value););
  }


  void iMesh_setDblData(iMesh_Instance instance,
			/*in*/ iBase_EntityHandle entity_handle,
			/*in*/ const iBase_TagHandle tag_handle,
			/*in*/ const double tag_value, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);
    CHECK_TAG(instance, tag_handle, err);
    TRY(instance->pGB->setDblData(entity_handle, tag_handle, tag_value););
  }


  void iMesh_setEHData(iMesh_Instance instance,
			    /*in*/ iBase_EntityHandle entity_handle,
			    /*in*/ const iBase_TagHandle tag_handle,
			    /*in*/ const iBase_EntityHandle tag_value, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);
    CHECK_ENTITY(instance, tag_value, err);
    CHECK_TAG(instance, tag_handle, err);
    TRY(instance->pGB->setEHData(entity_handle, tag_handle, tag_value););
  }


  void iMesh_getAllTags(iMesh_Instance instance,
			     /*in*/ const iBase_EntityHandle entity_handle,
			     /*inout*/ iBase_TagHandle** tag_handles,
			     /*inout*/ int* tag_handles_allocated,
			     /*out*/ int* tag_handles_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);
    TRY(instance->pGB->getAllTags(entity_handle, tag_handles, tag_handles_allocated,
			tag_handles_size););
  }


  void iMesh_rmvTag(iMesh_Instance instance,
			 /*in*/ iBase_EntityHandle entity_handle,
			 /*in*/ const iBase_TagHandle tag_handle, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);
    CHECK_TAG(instance, tag_handle, err);
    TRY(instance->pGB->rmvTag(entity_handle, tag_handle););
  }


  void iMesh_initEntIter(iMesh_Instance instance,
			 /*in*/ const iBase_EntitySetHandle entity_set_handle,
			 /*in*/ const int requested_entity_type,
			 /*in*/ const int requested_entity_topology,
			 /*out*/ iMesh_EntityIterator* entity_iterator,
			 int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set_handle, err);
    CHECK_TYPE_AND_TOPO(requested_entity_type, requested_entity_topology, err);
    EntIter* pEINew;

    try {
      if (isRootSet(instance, entity_set_handle)) {
	pEINew = new MeshIter(instance->pGB->pMesh(), requested_entity_type,
			      requested_entity_topology);
      }
      else {
	EntitySetBase *pESB = static_cast<EntitySetBase*>(entity_set_handle);
	if (pESB->qIsOrdered()) {
	  assert( dynamic_cast<EntityList*>(pESB) );
	  pEINew = new EntityListIter(dynamic_cast<EntityList*>(pESB),
				      requested_entity_type,
				      requested_entity_topology);
	}
	else {
	  assert( dynamic_cast<EntitySet*>(pESB) );
	  pEINew = new EntitySetIter(dynamic_cast<EntitySet*>(pESB),
				     requested_entity_type,
				     requested_entity_topology);
	}
      }
    }
    catch (iBase_ErrorType eErr) {
      *err = eErr;
      return;
    }
    instance->pGB->addEntIter( pEINew );
    *entity_iterator = pEINew;
  }

  void iMesh_getNextEntIter(iMesh_Instance instance,
			    /*in*/ iMesh_EntityIterator entity_iterator,
			    /*out*/ iBase_EntityHandle* entity_handle,
			    int *has_data, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    EntIter * pEI = static_cast<EntIter*>(entity_iterator);
    CHECK_ENT_ITER(instance, pEI, err);
    bool qRetVal = pEI->qIncrement();
    *entity_handle = pEI->pvData();
    *has_data = qRetVal;
  }


  void iMesh_resetEntIter(iMesh_Instance instance,
			  /*in*/ iMesh_EntityIterator entity_iterator,
			  int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    EntIter * pEI = static_cast<EntIter*>(entity_iterator);
    CHECK_ENT_ITER(instance, pEI, err);
    TRY(pEI->qReset());
  }


  void iMesh_endEntIter(iMesh_Instance instance,
			     /*in*/ iMesh_EntityIterator entity_iterator, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    EntIter * pEI = static_cast<EntIter*>(entity_iterator);
    CHECK_ENT_ITER(instance, pEI, err);
    instance->pGB->removeEntIter(pEI, err);
  }


  void iMesh_getEntTopo(iMesh_Instance instance,
			     /*in*/ const iBase_EntityHandle entity_handle,
			     int *out_topo, int *err)
   {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);
    Entity *pE = static_cast<Entity*> (entity_handle);
    *out_topo = pE->eEntTopology();
  }


  void iMesh_getEntType(iMesh_Instance instance,
			     /*in*/ const iBase_EntityHandle entity_handle,
			     int *out_type, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);
    Entity *pE = static_cast<Entity*> (entity_handle);
    *out_type = pE->eEntType();
  }


  void iMesh_getVtxCoord(iMesh_Instance instance,
			      /*in*/ const iBase_EntityHandle vertex_handle,
			      /*out*/ double *x, /*out*/ double *y, /*out*/ double *z, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, vertex_handle, err);

    Entity *pE = static_cast<Entity*>(vertex_handle);
    if (pE->eEntType() != iBase_VERTEX) {
      *err = iBase_INVALID_ENTITY_TYPE;
      return;
    }
    const double * const adCoords = pE->pVVert(0)->adCoords();
    *x = adCoords[0];
    *y = adCoords[1];
    *z = adCoords[2];
  }

  void iMesh_getEntAdj(iMesh_Instance instance,
			    /*in*/ const iBase_EntityHandle entity_handle,
			    /*in*/ const int entity_type_requested,
			    /*inout*/ iBase_EntityHandle** adj_entity_handles,
			    /*inout*/ int* adj_entity_handles_allocated,
			    /*out*/ int* adj_entity_handles_size, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);

    instance->pGB->getEntAdj(entity_handle, entity_type_requested,
		   adj_entity_handles, adj_entity_handles_allocated,
		   adj_entity_handles_size, err);
  }

  void iMesh_getEnt2ndAdj( iMesh_Instance instance,
			   iBase_EntityHandle entity_handle,
			   int bridge_entity_type,
			   int requested_entity_type,
			   iBase_EntityHandle** adj_entity_handles,
			   int* adj_entity_handles_allocated,
			   int* adj_entity_handles_size,
			   int* err )
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY(instance, entity_handle, err);

    instance->pGB->getEnt2ndAdj(entity_handle, bridge_entity_type,
				requested_entity_type,
				adj_entity_handles,
				adj_entity_handles_allocated,
				adj_entity_handles_size, err);
  }

  void iMesh_getEntArr2ndAdj(iMesh_Instance instance,
			     iBase_EntityHandle const* entity_handles,
			     int entity_handles_size,
			     int bridge_entity_type,
			     int requested_entity_type,
			     iBase_EntityHandle** adj_entity_handles,
			     int* adj_entity_handles_allocated,
			     int* adj_entity_handles_size,
			     int** offset,
			     int* offset_allocated,
			     int* offset_size,
			     int* err )
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
#ifndef NDEBUG
    for ( int i=0; i<entity_handles_size; i++ ) {
      CHECK_ENTITY(instance, entity_handles[i], err);
    }
#endif

    instance->pGB->getEntArr2ndAdj(entity_handles, entity_handles_size,
				   bridge_entity_type, requested_entity_type,
				   adj_entity_handles,
				   adj_entity_handles_allocated,
				   adj_entity_handles_size,
				   offset, offset_allocated,
				   offset_size, err );
  }

  void iMesh_subtract(iMesh_Instance instance,
			   /*in*/ const iBase_EntitySetHandle entity_set_1,
			   /*in*/ const iBase_EntitySetHandle entity_set_2,
			   /*out*/ iBase_EntitySetHandle* result_entity_set, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set_1, err);
    CHECK_ENTITY_SET(instance, entity_set_2, err);
    TRY(instance->pGB->subtract(entity_set_1, entity_set_2, result_entity_set););
  }

  void iMesh_intersect(iMesh_Instance instance,
			    /*in*/ const iBase_EntitySetHandle entity_set_1,
			    /*in*/ const iBase_EntitySetHandle entity_set_2,
			    /*out*/ iBase_EntitySetHandle* result_entity_set, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set_1, err);
    CHECK_ENTITY_SET(instance, entity_set_2, err);
    TRY(instance->pGB->intersect(entity_set_1, entity_set_2, result_entity_set););
  }

  void iMesh_unite(iMesh_Instance instance,
			/*in*/ const iBase_EntitySetHandle entity_set_1,
			/*in*/ const iBase_EntitySetHandle entity_set_2,
			/*out*/ iBase_EntitySetHandle* result_entity_set, int *err)
  {
    *err = iBase_SUCCESS;
    CHECK_FOR_MESH_DATA(instance, err);
    CHECK_ENTITY_SET(instance, entity_set_1, err);
    CHECK_ENTITY_SET(instance, entity_set_2, err);
    TRY(instance->pGB->unite(entity_set_1, entity_set_2, result_entity_set););
  }

  // The following implementation is courtesy of Jason Kraftcheck.

  void iMesh_getAdjEntIndices(iMesh_Instance instance,
			      /*in*/    iBase_EntitySetHandle entity_set_handle,
			      /*in*/    int entity_type_requestor,
			      /*in*/    int entity_topology_requestor,
			      /*in*/    int entity_type_requested,
			      /*inout*/ iBase_EntityHandle** entity_handles,
			      /*inout*/ int* entity_handles_allocated,
			      /*out*/   int* entity_handles_size,
			      /*inout*/ iBase_EntityHandle** adj_entity_handles,
			      /*inout*/ int* adj_entity_handles_allocated,
			      /*out*/   int* adj_entity_handles_size,
			      /*inout*/ int** adj_entity_indices,
			      /*inout*/ int* adj_entity_indices_allocated,
			      /*out*/   int* adj_entity_indices_size,
			      /*inout*/ int** offset,
			      /*inout*/ int* offset_allocated,
			      /*out*/   int* offset_size,
			      /*out*/   int *err)
  {
    const int allocated_entity_handles = (*entity_handles_allocated == 0);
    const int allocated_indices = (*adj_entity_indices_allocated == 0);
    const int allocated_offset = (*offset_allocated == 0);

    // get source entities
    iMesh_getEntities( instance,
		       entity_set_handle,
		       entity_type_requestor,
		       entity_topology_requestor,
		       entity_handles,
		       entity_handles_allocated,
		       entity_handles_size,
		       err );
    if (iBase_SUCCESS != *err)
      return;

    // get adjacencies
    iBase_EntityHandle* all_adj_handles = 0;
    int size = 0, alloc = 0;
    iMesh_getEntArrAdj( instance,
			*entity_handles, *entity_handles_size,
			entity_type_requested,
			&all_adj_handles, &alloc, &size,
			offset, offset_allocated, offset_size,
			err );
    if (*err != iBase_SUCCESS) {
      if (allocated_entity_handles) {
	free( *entity_handles );
	*entity_handles = 0;
	*entity_handles_allocated = 0;
      }
      return;
    }

    // allocate or check size of adj_entity_indices
    *adj_entity_indices_size = size;
    if (allocated_indices) {
      *adj_entity_indices = reinterpret_cast<int*>
	(malloc(sizeof(iBase_EntityHandle)*size));
      if (!*adj_entity_indices)
	*err = iBase_MEMORY_ALLOCATION_FAILED;
      else
	*adj_entity_indices_allocated = size;
    }
    else if (*adj_entity_indices_allocated < size) {
      *err = iBase_BAD_ARRAY_DIMENSION;
    }
    if (iBase_SUCCESS != *err) {
      free( all_adj_handles );
      if (allocated_entity_handles) {
	free( *entity_handles );
	*entity_handles = 0;
	*entity_handles_allocated = 0;
      }
      if (allocated_offset) {
	free( *offset );
	*offset = 0;
	*offset_allocated = 0;
      }
      return;
    }

    // Now create an array of unique sorted handles from all_adj_handles.
    // We need to create a copy because we still need all_adj_handles.  We
    // will eventually need to copy the resulting unique list into
    // adj_entity_handles, so if adj_entity_handles is already allocated and
    // of sufficient size, use it rather than allocating another temporary.
    iBase_EntityHandle* unique_adj = 0;
    if (*adj_entity_handles_allocated >= size) {
      unique_adj = *adj_entity_handles;
    }
    else {
      unique_adj = reinterpret_cast<iBase_EntityHandle*>
	(malloc(sizeof(iBase_EntityHandle) * size));
    }
    std::copy( all_adj_handles, all_adj_handles+size, unique_adj );
    std::sort( unique_adj, unique_adj + size );
    *adj_entity_handles_size = std::unique( unique_adj, unique_adj + size )
      - unique_adj;

    // If we created a temporary array for unique_adj rather than using
    // already allocated space in adj_entity_handles, allocate
    // adj_entity_handles and copy the unique handle list into it
    if (*adj_entity_handles != unique_adj) {
      if (!*adj_entity_handles_allocated) {
	*adj_entity_handles = reinterpret_cast<iBase_EntityHandle*>
	  (malloc(sizeof(iBase_EntityHandle) * *adj_entity_handles_size));
	if (!*adj_entity_handles)
	  *err = iBase_MEMORY_ALLOCATION_FAILED;
	else
	  *adj_entity_handles_allocated = *adj_entity_handles_size;
      }
      else if (*adj_entity_handles_allocated < *adj_entity_handles_size)
	*err = iBase_BAD_ARRAY_DIMENSION;
      if (iBase_SUCCESS != *err) {
	free( unique_adj );
	free( all_adj_handles );
	if (allocated_entity_handles) {
	  free( *entity_handles );
	  *entity_handles = 0;
	  *entity_handles_allocated = 0;
	}
	if (allocated_offset) {
	  free( *offset );
	  *offset = 0;
	  *offset_allocated = 0;
	}
	if (allocated_indices) {
	  free( *adj_entity_indices );
	  *adj_entity_indices = 0;
	  *adj_entity_indices_allocated = 0;
	}
	return;
      }

      std::copy( unique_adj, unique_adj + *adj_entity_handles_size,
		 *adj_entity_handles );
      free( unique_adj );
      unique_adj = *adj_entity_handles;
    }

    // convert from adjacency list to indices into unique_adj
    for (int i = 0; i < *adj_entity_indices_size; ++i)
      (*adj_entity_indices)[i] = std::lower_bound( unique_adj,
						   unique_adj + *adj_entity_handles_size, all_adj_handles[i] )
	- unique_adj;
    free( all_adj_handles );
  }
} // end extern C

