//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//=========================================================================

#include "vtkCMBPrepareForTriangleMesher.h"

#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkFieldData.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPolyData.h"

vtkStandardNewMacro(vtkCMBPrepareForTriangleMesher);

using namespace CmbFaceMesherClasses;

//Internal point structure to store unique points in a map
struct InternalPt
{
  double x, y, z;
  friend bool operator<(const InternalPt& l, const InternalPt& r)
  {
    return l.x != r.x ? (l.x < r.x) : l.y != r.y ? (l.y < r.y) : l.z < r.z;
  }
  InternalPt(double _x, double _y, double _z)
    : x(_x)
    , y(_y)
    , z(_z){};
};

vtkCMBPrepareForTriangleMesher::vtkCMBPrepareForTriangleMesher()
{
  arcArraySize = -1;
  loopArraySize = -1;
  numCells = -1;
  mapInfoInitialized = false;
  numLoopsAdded = 0;
  numArcsAdded = 0;
  numNodesAdded = 0;

  fieldCellArrayOffset = 0;
  fieldCellArraySize = 0;
  fieldArcId = 0;
  fieldLoop1 = 0;
  fieldLoop2 = 0;
  fieldEndpoint1 = 0;
  fieldEndpoint2 = 0;
  fieldLoopInfo = 0;

  cellElementIds = 0;
}

void vtkCMBPrepareForTriangleMesher::InitializeNewMapInfo()
{
  numLoopsAdded = 0;
  numArcsAdded = 0;

  fieldCellArrayOffset = vtkIdTypeArray::New();
  fieldCellArrayOffset->SetName("CellArrayOffset");
  fieldCellArrayOffset->SetNumberOfComponents(1);
  fieldCellArraySize = vtkIdTypeArray::New();
  fieldCellArraySize->SetName("CellArraySize");
  fieldCellArraySize->SetNumberOfComponents(1);
  fieldArcId = vtkIdTypeArray::New();
  fieldArcId->SetName("ArcId");
  fieldArcId->SetNumberOfComponents(1);
  fieldLoop1 = vtkIdTypeArray::New();
  fieldLoop1->SetName("Loop1");
  fieldLoop1->SetNumberOfComponents(1);
  fieldLoop2 = vtkIdTypeArray::New();
  fieldLoop2->SetName("Loop2");
  fieldLoop2->SetNumberOfComponents(1);
  fieldEndpoint1 = vtkIdTypeArray::New();
  fieldEndpoint1->SetName("ArcEndpoint1");
  fieldEndpoint1->SetNumberOfComponents(1);
  fieldEndpoint2 = vtkIdTypeArray::New();
  fieldEndpoint2->SetName("ArcEndpoint2");
  fieldEndpoint2->SetNumberOfComponents(1);

  fieldLoopInfo = vtkIdTypeArray::New();
  fieldLoopInfo->SetName("LoopInfo");
  fieldLoopInfo->SetNumberOfComponents(2);

  cellElementIds = vtkIdTypeArray::New();
  cellElementIds->SetName("ElementIds");
  cellElementIds->SetNumberOfComponents(1);
  //If we know the array size set the number of tuples
  if (this->arcArraySize != -1)
  {
    fieldCellArrayOffset->SetNumberOfTuples(arcArraySize);
    fieldCellArraySize->SetNumberOfTuples(arcArraySize);
    fieldArcId->SetNumberOfTuples(arcArraySize);
    fieldLoop1->SetNumberOfTuples(arcArraySize);
    fieldLoop2->SetNumberOfTuples(arcArraySize);
    fieldEndpoint1->SetNumberOfTuples(arcArraySize);
    fieldEndpoint2->SetNumberOfTuples(arcArraySize);
  }
  if (this->loopArraySize != -1)
  {
    fieldLoopInfo->SetNumberOfTuples(loopArraySize);
  }
  if (this->numCells != -1)
  {
    cellElementIds->SetNumberOfTuples(numCells);
  }
  mapInfoInitialized = true;
}

void vtkCMBPrepareForTriangleMesher::SetNumberOfArcs(const vtkIdType& num)
{
  if (mapInfoInitialized)
  {
    vtkErrorMacro("Field data has already been created.\
        Set the number of arcs before initializing new map field data");
  }
  arcArraySize = num;
}

void vtkCMBPrepareForTriangleMesher::SetNumberOfLoops(const vtkIdType& num)
{
  if (mapInfoInitialized)
  {
    vtkErrorMacro("MapInfohas already been created.\
        Set the number of loops before initializing new map field/cell data");
  }
  loopArraySize = num;
}

void vtkCMBPrepareForTriangleMesher::SetNumberOfCells(const vtkIdType& num)
{
  if (mapInfoInitialized)
  {
    vtkErrorMacro("MapInfo data has already been created.\
        Set the number of loops before initializing new map field/cell data");
  }
  this->numCells = num;
}

//Sets polydata to query for information or set up new field data
void vtkCMBPrepareForTriangleMesher::SetPolyData(vtkPolyData* pd)
{
  this->PolyData = pd;
}

vtkCMBPrepareForTriangleMesher::~vtkCMBPrepareForTriangleMesher()
{
  if (fieldCellArrayOffset)
  {
    fieldCellArrayOffset->Delete();
  }
  if (fieldCellArraySize)
  {
    fieldCellArraySize->Delete();
  }
  if (fieldArcId)
  {
    fieldArcId->Delete();
  }
  if (fieldEndpoint1)
  {
    fieldEndpoint1->Delete();
  }
  if (fieldEndpoint2)
  {
    fieldEndpoint2->Delete();
  }
  if (fieldLoop1)
  {
    fieldLoop1->Delete();
  }
  if (fieldLoop2)
  {
    fieldLoop2->Delete();
  }
  if (fieldLoopInfo)
  {
    fieldLoopInfo->Delete();
  }
  if (cellElementIds)
  {
    cellElementIds->Delete();
  }
}

//Adds a node id to a vtkvertex
//the order in which you created the vtkvertex must be the same in which you
//add the nodes
vtkIdType vtkCMBPrepareForTriangleMesher::AddNode(const vtkIdType& nodeId)
{
  if (this->numCells == -1)
  {
    this->cellElementIds->InsertTuple1(numNodesAdded, nodeId);
  }
  else
  {
    this->cellElementIds->SetTuple1(numNodesAdded, nodeId);
  }
  return numNodesAdded++;
}

//Creates the necessary field data for an arc
vtkIdType vtkCMBPrepareForTriangleMesher::AddArc(const vtkIdType& CellArrayOffset,
  const vtkIdType& CellArraySize, const vtkIdType& ArcId, const vtkIdType& Loop1,
  const vtkIdType& Loop2, const vtkIdType& Endpoint1, const vtkIdType& Endpoint2)
{
  if (this->arcArraySize == -1)
  {
    this->fieldCellArrayOffset->InsertTuple1(numArcsAdded, CellArrayOffset);
    this->fieldCellArraySize->InsertTuple1(numArcsAdded, CellArraySize);
    this->fieldArcId->InsertTuple1(numArcsAdded, ArcId);
    this->fieldLoop1->InsertTuple1(numArcsAdded, Loop1);
    this->fieldLoop2->InsertTuple1(numArcsAdded, Loop2);
    this->fieldEndpoint1->InsertTuple1(numArcsAdded, Endpoint1);
    this->fieldEndpoint2->InsertTuple1(numArcsAdded, Endpoint2);
  }
  else
  {
    this->fieldCellArrayOffset->SetTuple1(numArcsAdded, CellArrayOffset);
    this->fieldCellArraySize->SetTuple1(numArcsAdded, CellArraySize);
    this->fieldArcId->SetTuple1(numArcsAdded, ArcId);
    this->fieldLoop1->SetTuple1(numArcsAdded, Loop1);
    this->fieldLoop2->SetTuple1(numArcsAdded, Loop2);
    this->fieldEndpoint1->SetTuple1(numArcsAdded, Endpoint1);
    this->fieldEndpoint2->SetTuple1(numArcsAdded, Endpoint2);
  }

  //Add element ids as cell data
  //Only will work with VTK_LINES
  vtkIdType startCell = CellArrayOffset / 3;
  vtkIdType numCellsInArc = CellArraySize / 3;
  for (vtkIdType i = 0; i < numCellsInArc; i++)
  {
    //Insert the cell data after verts
    if (this->numCells == -1)
    {
      this->cellElementIds->InsertTuple1(numNodesAdded + startCell + i, ArcId);
    }
    else
    {
      this->cellElementIds->SetTuple1(numNodesAdded + startCell + i, ArcId);
    }
  }

  //Return the arc index
  return numArcsAdded++;
}

vtkIdType vtkCMBPrepareForTriangleMesher::AddLoopWithArcs(
  const vtkIdType& PolyId, const bool& isOuter, const std::vector<vtkIdType>& arcIndexes)
{

  //Adds a loop and then finds the arcs that this loop belongs to and
  //updates their fieldLoop1 and fieldLoop2 field data arrays

  //if this is a new polygon that hasn't been seen before
  vtkIdType loopIndex = -2;
  if (this->loop2loopIndex.find(arcIndexes) == this->loop2loopIndex.end())
  {
    //we only know either the outer or inner loop use at this time
    //put -1 in as a placeholder for now
    loopIndex = isOuter ? this->AddLoop(PolyId, -1) : this->AddLoop(-1, PolyId);
    this->loop2loopIndex[arcIndexes] = loopIndex;
  }
  else
  {
    //we have seen this loop before grab the id so we can update it
    loopIndex = this->loop2loopIndex[arcIndexes];
    double* tup = this->fieldLoopInfo->GetTuple2(loopIndex);
    //Check to make sure there is a place to put the data
    if (!((tup[0] == -1) ^ (tup[1] == -1))) //xor
    {
      vtkErrorMacro("ERROR: loop "
        << loopIndex << " seen before but ids are not set correctly. Would be poly " << PolyId);
    }
    else
    {
      //set tup[0] for outer and tup[1] for inner
      tup[!isOuter] = PolyId;
      fieldLoopInfo->SetTuple2(loopIndex, tup[0], tup[1]);
    }
  }

  //Relate the loop back to the arcs
  std::vector<vtkIdType>::const_iterator arcIndexIter = arcIndexes.begin();
  for (; arcIndexIter != arcIndexes.end(); arcIndexIter++)
  {
    vtkIdType arcIndex = (*arcIndexIter);
    //Make sure the arcIndex is valid
    if (arcIndex >= numArcsAdded)
    {
      vtkErrorMacro("Error cannot create a polygon using arc index "
        << arcIndex << " because there that arc has not been added yet");
    }
    //Get loops the arc is already associated with
    double loop1 = this->fieldLoop1->GetTuple1(arcIndex);
    double loop2 = this->fieldLoop2->GetTuple1(arcIndex);
    //Figure out which field to place the loopIndex
    //only place in the first slot if it is an outer loop
    if (loop1 == -1 && isOuter)
    {
      this->fieldLoop1->SetTuple1(arcIndex, loopIndex);
    }
    else if (loop2 == -1)
    {
      this->fieldLoop2->SetTuple1(arcIndex, loopIndex);
    }
    else
    {
      vtkErrorMacro("ERROR: arcIndexes is an invalid loop list");
    }
  }
  return loopIndex;
}

vtkIdType vtkCMBPrepareForTriangleMesher::AddLoop(
  const vtkIdType& OuterPolyId, const vtkIdType& InnerPolyId)
{
  if (this->loopArraySize == -1)
  {
    this->fieldLoopInfo->InsertTuple2(numLoopsAdded, OuterPolyId, InnerPolyId);
  }
  else
  {
    this->fieldLoopInfo->SetTuple2(numLoopsAdded, OuterPolyId, InnerPolyId);
  }
  numLoopsAdded++;
  //Return the loopId
  return numLoopsAdded - 1;
}

void vtkCMBPrepareForTriangleMesher::FinalizeNewMapInfo()
{
  //If one exists then they all should exist
  if (fieldCellArrayOffset)
  {
    this->PolyData->GetFieldData()->AddArray(fieldCellArrayOffset);
    this->PolyData->GetFieldData()->AddArray(fieldCellArraySize);
    this->PolyData->GetFieldData()->AddArray(fieldArcId);
    this->PolyData->GetFieldData()->AddArray(fieldEndpoint1);
    this->PolyData->GetFieldData()->AddArray(fieldEndpoint2);
    this->PolyData->GetFieldData()->AddArray(fieldLoop1);
    this->PolyData->GetFieldData()->AddArray(fieldLoop2);
    this->PolyData->GetFieldData()->AddArray(fieldLoopInfo);
    this->PolyData->GetCellData()->AddArray(cellElementIds);

    //quickly delete the arrays and make sure that the destructor does not
    //delete them a second time by setting the pointers to 0
    fieldCellArrayOffset->FastDelete();
    fieldCellArraySize->FastDelete();
    fieldArcId->FastDelete();
    fieldEndpoint1->FastDelete();
    fieldEndpoint2->FastDelete();
    fieldLoop1->FastDelete();
    fieldLoop2->FastDelete();
    fieldLoopInfo->FastDelete();
    cellElementIds->FastDelete();
    fieldCellArrayOffset = NULL;
    fieldCellArraySize = NULL;
    fieldArcId = NULL;
    fieldEndpoint1 = NULL;
    fieldEndpoint2 = NULL;
    fieldLoop1 = NULL;
    fieldLoop2 = NULL;
    fieldLoopInfo = NULL;
    cellElementIds = NULL;
  }
  else
  {
    vtkWarningMacro("No field data to finalize");
  }
}

// Returns an arc contour as polydata
// toReturn must be an initialized vtkPolyData will be returned as the
// requested contour
void vtkCMBPrepareForTriangleMesher::GetArc(vtkIdType requestedArcId, vtkPolyData* toReturn)
{
  //The input polygon must be in the map format
  vtkFieldData* fieldData = this->PolyData->GetFieldData();
  vtkIdTypeArray* arcIds = vtkIdTypeArray::SafeDownCast(fieldData->GetArray("ArcId"));
  vtkIdType arcIndex = 0;

  //Find the correct index into the field data arrays
  bool foundId = false;
  for (; arcIndex < arcIds->GetNumberOfTuples(); arcIndex++)
  {
    if (arcIds->GetTuple1(arcIndex) == requestedArcId)
    {
      foundId = true;
      break;
    }
  }
  if (!foundId)
  {
    vtkWarningMacro("A valid arc id was not selected");
  }

  //Use index to get arc properties
  vtkIdType cellStart = fieldData->GetArray("CellArrayOffset")->GetTuple1(arcIndex);
  vtkIdType cellArraySize = fieldData->GetArray("CellArraySize")->GetTuple1(arcIndex);

  //Get input info
  vtkCellArray* lines = this->PolyData->GetLines();
  //Create output info
  vtkCellArray* linesToAdd = vtkCellArray::New();
  vtkPoints* pointsToAdd = vtkPoints::New();
  //Allocate an exepcted number of lines and points
  linesToAdd->Allocate(cellArraySize);
  pointsToAdd->SetNumberOfPoints((2 * (cellArraySize)) / 3);

  double p[3]; //temporary point
  vtkIdType lineToInsert[2] = { -1, -1 };
  std::map<InternalPt, vtkIdType> pt2ptId; //map to make sure no duplicate inserts
  vtkIdType insertAt = 0;

  //Traverse all cells in arc and add them to output
  if (lines)
  {
    vtkIdType *pts, npts;
    lines->SetTraversalLocation(cellStart);
    while (lines->GetTraversalLocation() <
        (cellArraySize + cellStart) && /*Check The lines are within bounds*/
      lines->GetNextCell(npts, pts) /*Check There are lines*/)
    {
      for (vtkIdType j = 0; j < npts; j++)
      {
        //Grab the points this edge uses
        this->PolyData->GetPoint(pts[j], p);
        InternalPt edgePoint(p[0], p[1], p[2]);
        //Check and see if point has already been seen
        std::map<InternalPt, vtkIdType>::iterator foundPt = pt2ptId.find(edgePoint);
        if (foundPt == pt2ptId.end())
        {
          //Point has not been seen
          pointsToAdd->SetPoint(insertAt, p);
          foundPt = pt2ptId.insert(std::pair<InternalPt, vtkIdType>(edgePoint, insertAt++)).first;
        }
        std::swap(lineToInsert[0], lineToInsert[1]);
        lineToInsert[1] = foundPt->second;
        if (j > 0)
        {
          //Insert the previous two points as a line
          linesToAdd->InsertNextCell(2, lineToInsert);
        }
      }
    }
  }
  //Remove any extra allocated space
  pointsToAdd->SetNumberOfPoints(insertAt);
  pointsToAdd->Squeeze();
  linesToAdd->Squeeze();

  //Set output properties
  toReturn->SetPoints(pointsToAdd);
  toReturn->SetLines(linesToAdd);
  pointsToAdd->FastDelete();
  linesToAdd->FastDelete();
}

bool vtkCMBPrepareForTriangleMesher::IsValidForReading()
{
  vtkFieldData* fieldData;
  bool hasValidFields = false;
  if (this->PolyData && (fieldData = this->PolyData->GetFieldData()))
  {
    hasValidFields = fieldData->HasArray("Loop1") != 0;
    hasValidFields = hasValidFields && (fieldData->HasArray("Loop2") != 0);
    hasValidFields = hasValidFields && (fieldData->HasArray("LoopInfo") != 0);
    hasValidFields = hasValidFields && (fieldData->HasArray("ArcEndpoint1") != 0);
    hasValidFields = hasValidFields && (fieldData->HasArray("ArcEndpoint2") != 0);
    hasValidFields = hasValidFields && (fieldData->HasArray("ArcId") != 0);
    hasValidFields = hasValidFields && (fieldData->HasArray("CellArrayOffset") != 0);
    hasValidFields = hasValidFields && (fieldData->HasArray("CellArraySize") != 0);
    hasValidFields = hasValidFields && (fieldData->HasArray("Loop1") != 0);
  }
  return hasValidFields;
}

bool vtkCMBPrepareForTriangleMesher::GetPolyId2ModelFaceRepMap(
  std::map<vtkIdType, ModelFaceRep*>& pid2Face)
{
  // loop index to index into arc field data
  std::map<vtkIdType, std::vector<vtkIdType> > loopId2ArcIndex;

  bool valid = this->IsValidForReading();
  valid = valid && this->BuildLoopId2ArcIndexMap(loopId2ArcIndex);
  valid = valid && this->BuildPolygonId2ModelFaceMap(loopId2ArcIndex, pid2Face);
  return valid;
}

//Create a mapping from loop ids to a vector of arc indices.
//loop ids are simply the index of the loop in the LoopInfo
//field data array. The field data arrays loop1 and loop2
//store the ids that are used to index into the LoopInfo
//array
bool vtkCMBPrepareForTriangleMesher::BuildLoopId2ArcIndexMap(
  std::map<vtkIdType, std::vector<vtkIdType> >& loopId2ArcIndex)
{
  vtkFieldData* fData = this->PolyData->GetFieldData();
  vtkIdTypeArray* fLoop1 = vtkIdTypeArray::SafeDownCast(fData->GetArray("Loop1"));
  vtkIdTypeArray* fLoop2 = vtkIdTypeArray::SafeDownCast(fData->GetArray("Loop2"));

  typedef std::map<vtkIdType, std::vector<vtkIdType> >::iterator LoopIterator;

  for (int i = 0; i < fLoop1->GetNumberOfTuples(); i++)
  {
    vtkIdType loop1 = fLoop1->GetTuple1(i);
    vtkIdType loop2 = fLoop2->GetTuple1(i);
    //If there is a loop this arc is a part of
    if (loop1 != -1)
    {
      LoopIterator it = loopId2ArcIndex.find(loop1);
      if (it == loopId2ArcIndex.end())
      {
        //New loop seen create a vector to store the arcs associate with this loop
        std::pair<std::map<vtkIdType, std::vector<vtkIdType> >::iterator, bool> pr =
          loopId2ArcIndex.insert(
            std::pair<vtkIdType, std::vector<vtkIdType> >(loop1, std::vector<vtkIdType>()));
        it = pr.first;
        (*it).second.reserve(fLoop1->GetNumberOfTuples());
      }
      //Add an arc to this loop that has already been seen
      (*it).second.push_back(i);
    }
    //If there is a second loop this arc is a part of
    if (loop1 != loop2 && loop2 != -1)
    {
      std::map<vtkIdType, std::vector<vtkIdType> >::iterator it = loopId2ArcIndex.find(loop2);
      if (it == loopId2ArcIndex.end())
      {
        //New loop seen create a vector to store the arcs associate with this loop
        std::pair<std::map<vtkIdType, std::vector<vtkIdType> >::iterator, bool> pr =
          loopId2ArcIndex.insert(
            std::pair<vtkIdType, std::vector<vtkIdType> >(loop2, std::vector<vtkIdType>()));
        it = pr.first;
        (*it).second.reserve(fLoop1->GetNumberOfTuples());
      }
      //Add an arc to this loop that has already been seen
      (*it).second.push_back(i);
    }
  }
  //Resize vectors of arcs to be the actual size
  std::map<vtkIdType, std::vector<vtkIdType> >::iterator it = loopId2ArcIndex.begin();
  for (; it != loopId2ArcIndex.end(); it++)
  {
    (*it).second.resize((*it).second.size());
  }

  return true;
}

bool vtkCMBPrepareForTriangleMesher::BuildPolygonId2ModelFaceMap(
  const std::map<vtkIdType, std::vector<vtkIdType> >& loopId2ArcIndex,
  std::map<vtkIdType, ModelFaceRep*>& pid2Face)
{
  //TODO: meshEdgeReps can be created more than once, its not a huge problem
  // because the data just gets copied multiple times from them anyway
  //But it may be a slight improvement to save edges once you create them for later use

  //Check to see if there is node information
  std::map<vtkIdType, std::pair<double, double> > nodeId2point;
  if (this->PolyData->GetCellData()->HasArray("ElementIds"))
  {
    vtkIdTypeArray* elementIds =
      vtkIdTypeArray::SafeDownCast(this->PolyData->GetCellData()->GetArray("ElementIds"));
    const vtkIdType size = this->PolyData->GetNumberOfCells();
    for (int i = 0; i < size; i++)
    {
      vtkCell* cell = this->PolyData->GetCell(i);
      if (cell->GetCellType() == VTK_VERTEX)
      {
        double pt[3];
        cell->GetPoints()->GetPoint(0, pt);
        vtkIdType nodeId = elementIds->GetTuple1(i);
        nodeId2point[nodeId] = std::make_pair(pt[0], pt[1]);
      }
      else
      {
        break; // no more vertex data is available
      }
    }
  }

  vtkFieldData* fieldData = this->PolyData->GetFieldData();
  vtkIdTypeArray* fLoopInfo = vtkIdTypeArray::SafeDownCast(fieldData->GetArray("LoopInfo"));
  vtkIdTypeArray* fEndpoint1 = vtkIdTypeArray::SafeDownCast(fieldData->GetArray("ArcEndpoint1"));
  vtkIdTypeArray* fEndpoint2 = vtkIdTypeArray::SafeDownCast(fieldData->GetArray("ArcEndpoint2"));
  vtkIdTypeArray* fArcId = vtkIdTypeArray::SafeDownCast(fieldData->GetArray("ArcId"));
  vtkIdTypeArray* fCellOffset =
    vtkIdTypeArray::SafeDownCast(fieldData->GetArray("CellArrayOffset"));
  vtkIdTypeArray* fArraySize = vtkIdTypeArray::SafeDownCast(fieldData->GetArray("CellArraySize"));

  if (!fEndpoint1 || !fEndpoint2 || !fArcId || !fCellOffset || !fArraySize || !fLoopInfo)
  {
    //no point going any fargher no loop info
    return false;
  }

  //Populate the ModelFaceRep's in pid2Face
  for (int loopId = 0; loopId < fLoopInfo->GetNumberOfTuples(); loopId++)
  {
    double* polyId = fLoopInfo->GetTuple2(loopId);
    //iterate twice: loopType = 0 is the outer loop
    //               loopType = 1 is the inner loop
    for (int loopType = 0; loopType < 2; loopType++)
    {
      // -1 and 0 are invalid ids, skip them
      if (polyId[loopType] < 1)
        continue;

      std::map<vtkIdType, ModelFaceRep*>::iterator currFace = pid2Face.find(polyId[loopType]);
      if (currFace == pid2Face.end())
      {
        //Create a new face for a polygon id we haven't seen
        std::pair<std::map<vtkIdType, ModelFaceRep*>::iterator, bool> pr = pid2Face.insert(
          std::pair<vtkIdType, ModelFaceRep*>(polyId[loopType], new ModelFaceRep()));
        currFace = pr.first;
      }

      ModelLoopRep loop(loopId, loopType == 1);
      //iterate over each arc index in the loop
      std::map<vtkIdType, std::vector<vtkIdType> >::const_iterator loopVector =
        loopId2ArcIndex.find(loopId);
      std::vector<vtkIdType>::const_iterator loopIter = loopVector->second.begin();

      for (; loopIter != loopVector->second.end(); loopIter++)
      {
        vtkIdType arcIndex = (*loopIter);

        vtkIdType arcId = fArcId->GetTuple1(arcIndex);
        ModelEdgeRep edge(arcId);
        //Add the points for this arc by specifying the range in the
        //cell data where this arc exists
        vtkIdType cellOffset = fCellOffset->GetTuple1(arcIndex);
        vtkIdType cellSize = fArraySize->GetTuple1(arcIndex);
        edge.setMeshPoints(this->PolyData, cellOffset, cellSize);

        //If there is node information add it
        if (nodeId2point.size() > 0)
        {
          vtkIdType nodeId1 = fEndpoint1->GetTuple1(arcIndex);
          vtkIdType nodeId2 = fEndpoint2->GetTuple1(arcIndex);
          std::pair<double, double> nodePt1_pair = nodeId2point[nodeId1];
          std::pair<double, double> nodePt2_pair = nodeId2point[nodeId2];
          double nodePt1[2] = { nodePt1_pair.first, nodePt1_pair.second };
          double nodePt2[2] = { nodePt2_pair.first, nodePt2_pair.second };
          edge.addModelVert(nodeId1, nodePt1);
          edge.addModelVert(nodeId2, nodePt2);
        }
        //add the newly created edge to the loop
        loop.addEdge(edge);
      }
      //add the newly created loop to the face
      currFace->second->addLoop(loop);
    }
  }

  return true;
}

void vtkCMBPrepareForTriangleMesher::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  //TODO: add more to this
  os << "vtkCMBPrepareForTriangleMesher\n";
}
