/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         https://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef itkQuadEdgeMesh_hxx
#define itkQuadEdgeMesh_hxx
#include <limits>
#include <vector>

namespace itk
{
template <typename TPixel, unsigned int VDimension, typename TTraits>
const typename QuadEdgeMesh<TPixel, VDimension, TTraits>::PointIdentifier
  QuadEdgeMesh<TPixel, VDimension, TTraits>::m_NoPoint = std::numeric_limits<PointIdentifier>::max();

template <typename TPixel, unsigned int VDimension, typename TTraits>
const typename QuadEdgeMesh<TPixel, VDimension, TTraits>::CellIdentifier
  QuadEdgeMesh<TPixel, VDimension, TTraits>::m_NoFace = std::numeric_limits<CellIdentifier>::max();

/**
 * Restore the mesh to its initial state. Useful for data pipeline updates
 * without memory re-allocation.
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
void
QuadEdgeMesh<TPixel, VDimension, TTraits>::Initialize()
{
  itkDebugMacro("Mesh Initialize method ");
  Clear();
  Superclass::Initialize();
}

/**
 * Clear all this mesh by deleting all contained edges which as
 * a side effect deletes adjacent faces
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
void
QuadEdgeMesh<TPixel, VDimension, TTraits>::Clear()
{
  if (this->GetEdgeCells())
  {
    CellsContainerIterator cellIterator = this->GetEdgeCells()->Begin();
    while (!this->GetEdgeCells()->empty())
    {
      auto * edgeToDelete = dynamic_cast<EdgeCellType *>(cellIterator.Value());
      this->LightWeightDeleteEdge(edgeToDelete);
      cellIterator = this->GetEdgeCells()->Begin();
    }
  }

  // Clear the points potentially left behind by LightWeightDeleteEdge():
  if (this->GetPoints())
  {
    this->GetPoints()->clear();
  }
  this->ClearFreePointAndCellIndexesLists(); // to start at index 0
}

template <typename TPixel, unsigned int VDimension, typename TTraits>
void
QuadEdgeMesh<TPixel, VDimension, TTraits>::Graft(const DataObject * data)
{
  this->Superclass::Graft(data);
  const auto * mesh = dynamic_cast<const Self *>(data);

  if (!mesh)
  {
    // pointer could not be cast back down
    itkExceptionMacro("itk::QuadEdgeMesh::CopyInformation() cannot cast " << typeid(data).name() << " to "
                                                                          << typeid(Self *).name());
  }

  this->m_FreePointIndexes = mesh->m_FreePointIndexes;
  this->m_FreeCellIndexes = mesh->m_FreeCellIndexes;
  this->ClearCellsContainer();
  this->m_EdgeCellsContainer = mesh->m_EdgeCellsContainer;
  this->m_NumberOfFaces = mesh->m_NumberOfFaces;
  this->m_NumberOfEdges = mesh->m_NumberOfEdges;
}

/**
 * \brief The one and only method to modify the edge connectivity.
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::Splice(QEPrimal * a, QEPrimal * b) -> PointIdentifier
{
  const bool      SplitingOrigin = a->IsInOnextRing(b);
  PointIdentifier resultingOriginId;

  if (SplitingOrigin)
  {
    // see TODO's entry dated 2006-01-24
    /* We consider the following situation which depicts the Onext()
     * ring around the point Origin (which is both a->GetOrigin() and
     * b->GetOrigin():
     *
     *              \         /
     *               \       /
     *               e3     e2              counter-clockwise
     *                 \   /                Onext() order.
     *                  \ /
     *      -----b------Org------a----
     *                  / \
     *                 /   \
     *                /     \
     *               /       \
     *              /         \
     *            e5           e6
     *            /             \
     *           /               \
     *
     * The result of this method is then:
     *
     *         \         /
     *          \       /
     *          e3     e2
     *            \   /
     *             \ /
     * ----b------newOrg
     *
     *                  Org------a-----
     *                  / \
     *                 /   \
     *                /     \
     *               /       \
     *              /         \
     *            e5           e7
     *            /             \
     *           /               \
     */

    // Handle connectivity at QEQuadEdge level:
    a->Splice(b);

    ////////// Handle the geometrical references:
    // Make sure the Origin's edge entry doesn't point to an entry edge
    // that isn't any more in the Onext ring:
    const PointIdentifier orgId = a->GetOrigin();
    PointType             org = this->GetPoint(orgId);
    org.SetEdge(a);
    this->SetPoint(orgId, org);

    // Create a newOrigin point by duplicating the geometry of Origin...
    PointType newOrigin = org;
    newOrigin.SetEdge(b);
    const PointIdentifier newOriginId = this->AddPoint(newOrigin);

    // ...and inform Onext ring of b that their Origin() have changed:
    for (auto it = b->BeginGeomOnext(); it != b->EndGeomOnext(); ++it)
    {
      it.Value()->SetOrigin(newOriginId);
    }
    resultingOriginId = newOriginId;
  }
  else
  {
    // see TODO's entry dated 2006-01-24
    /* We consider the following situation which depicts the Onext()
     * rings around the point Origin = a->GetOrigin() and
     * oldOrigin = b->GetOrigin():
     *
     *         \         /
     *          \       /
     *          e3     e2
     *            \   /
     *             \ /
     * ----b------oldOrg
     *
     *                  Org------a-----
     *                  / \
     *                 /   \
     *                /     \
     *               /       \
     *              /         \
     *            e5           e7
     *            /             \
     *           /               \
     *
     *
     * The result of this method is then:
     *
     *              \         /
     *               \       /
     *               e3     e2              counter-clockwise
     *                 \   /                Onext() order.
     *                  \ /
     *      -----b------Org------a----
     *                  / \
     *                 /   \
     *                /     \
     *               /       \
     *              /         \
     *            e5           e6
     *            /             \
     *           /               \
     *
     * Note: in this case we must handle the geometry first and
     *       then the connectivity.
     */

    // Since this is the geometrical version of Splice() we
    // have additional geometrical information that we should use
    // to check the correctness of the situation.

    /////////////////////////////////////////////////////////////
    // First, consider the vertices: Origin and oldOrigin must be different.
    const PointIdentifier oldOriginId = b->GetOrigin();
    const PointIdentifier orgId = a->GetOrigin();

    if (oldOriginId == orgId)
    {
      itkDebugMacro("Trying to fuse the same point!");
      return m_NoPoint;
    }

    /** \todo Compare the geometry of the two points and accept
     * splicing when their geometry matches. We could fix
     * an epsilon threshold distance above which the two points
     * are considered distinct.
     * PointType org = this->GetPoint(orgId);
     */
    PointType oldOrigin = this->GetPoint(oldOriginId);


    /////////////////////////////////////////////////////////////
    /* We are done with the vertices and we might need to consider the
     * possible initial adjacent face[s]. We shall accept to proceed
     * with Splicing if and only if the following conditions are met:
     * [1] a and b both share the SAME Left face,
     * [2] a and b and in the same Lnext() ring,
     * [3] a and b are not too close followers in the Lnext() ring
     *    [this is to avoid to create a face with only two edges which
     *     is equivalent to two different edges adjacent to the same two
     *     vertices].
     *
     *                   V ---<-b---- V
     *                  /              \
     *                 /                \
     *                /              a.Lnext().Lnext()
     *               /                    \
     *              /        Face          \
     *             V                        V
     *              \     a.Splice(b)      /
     *               \     is OK          /
     *                \               a.Lnext()
     *                 \                /
     *                  \              /
     *                   V ----a->--- V
     *
     * Basically, we accept to proceed with splitting if there is a
     * single face on the left and this face is at least a hexagon
     * and the vertices we wish to splice are at least two vertices aside.
     */

    const FaceRefType aLeftFace = a->GetLeft();
    const FaceRefType bLeftFace = b->GetLeft();

    bool MustReconstructFace = false;
    if ((aLeftFace == m_NoFace && bLeftFace != m_NoFace) || (aLeftFace != m_NoFace && bLeftFace == m_NoFace))
    {
      itkDebugMacro("Face on one side but not the other. Cancel.");
      return m_NoPoint;
    }

    if (aLeftFace != m_NoFace && bLeftFace != m_NoFace)
    {
      if ((aLeftFace == bLeftFace) && (a->GetLnext() != b) && (a->GetLnext()->GetLnext() != b) &&
          (b->GetLnext() != a) && (b->GetLnext()->GetLnext() != a) && (a->IsInLnextRing(b)) && (b->IsInLnextRing(a)))
      {
        this->DeleteFace(aLeftFace);
        MustReconstructFace = true;
      }
      else
      {
        itkDebugMacro("Face is not at least and hexagon.");
        return m_NoPoint;
      }
    }

    // Notice that when aLeftFace == m_NoFace and bLeftFace == m_NoFace
    // we simply proceed... (with MustReconstructFace initially set to
    // false.

    ///////////////////////////////////////////////////////////////
    // Handle connectivity at QEQuadEdge level:
    a->Splice(b);

    ///////////////////////////////////////////////////////////////
    // Back to dealing with the geometrical references. First
    // make sure the oldOrigin's edge entry won't be used any more:
    oldOrigin.SetEdge((QEPrimal *)nullptr);
    this->SetPoint(oldOriginId, oldOrigin);

    // We need to inform the edges ranging from a->Onext() to b that
    // their Origin() have changed. Let's over do it (read, be lazy) and
    // inform the full Onext() ring:
    for (auto it = a->BeginGeomOnext(); it != a->EndGeomOnext(); ++it)
    {
      it.Value()->SetOrigin(orgId);
    }
    resultingOriginId = oldOriginId;

    ///////////////////////////////////////////////////////////////
    // Now that we are done with the handling of the geometry of
    // vertices proceed with the geometry of the faces. When we
    // are splitting a face (through Splicing) we must construct two
    // new faces:
    if (MustReconstructFace)
    {
      this->AddFace(a);
      this->AddFace(b);
    }
  }

  this->Modified();
  return resultingOriginId;
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
void
QuadEdgeMesh<TPixel, VDimension, TTraits>::SetCell(CellIdentifier itkNotUsed(cId), CellAutoPointer & cell)
{
  // NOTE ALEX: should add some checking to be sure everything went fine
  EdgeCellType *    qe = nullptr;
  PolygonCellType * pe = nullptr;

  // The QuadEdgeMeshCellTypes first
  if ((qe = dynamic_cast<EdgeCellType *>(cell.GetPointer())) != nullptr)
  {
    // NOTE ALEX: here
    this->AddEdge(qe->GetQEGeom()->GetOrigin(), qe->GetQEGeom()->GetDestination());
    cell.ReleaseOwnership();
    delete qe;
  }
  else if ((pe = dynamic_cast<PolygonCellType *>(cell.GetPointer())) != nullptr)
  {
    PointIdList                   points;
    PointIdInternalIterator       pit = pe->InternalPointIdsBegin();
    const PointIdInternalIterator pend = pe->InternalPointIdsEnd();
    while (pit != pend)
    {
      points.push_back(*pit);
      ++pit;
    }
    // NOTE ALEX: here
    this->AddFaceWithSecurePointList(points);
    cell.ReleaseOwnership();
    delete pe;
  }
  else // non-QE cell, i.e. original itk cells for example
  {
    const PointIdentifier numPoint = cell->GetNumberOfPoints();
    PointIdIterator       pointId = cell->PointIdsBegin();
    PointIdIterator       endId = cell->PointIdsEnd();
    // Edge
    if (numPoint == 2)
    {
      if ((pointId) && (endId) && (pointId != endId))
      {
        PointIdIterator temp = pointId++;
        // NOTE ALEX: here
        this->AddEdge(*pointId, *temp);
      }
    }
    // polygons
    else if (cell->GetDimension() == 2)
    {
      PointIdList points;
      while (pointId != endId)
      {
        points.push_back(*pointId);
        ++pointId;
      }
      // NOTE ALEX: here
      this->AddFace(points);
    }
    cell.ReleaseOwnership();
    delete (cell.GetPointer());
  }
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::FindFirstUnusedPointIndex() -> PointIdentifier
{
  PointIdentifier       pid = 0;
  const PointIdentifier maxpid = this->GetNumberOfPoints();

  if (!m_FreePointIndexes.empty())
  {
    // find the first valid free ID
    do
    {
      pid = m_FreePointIndexes.front();
      if (pid < maxpid)
      {
        m_FreePointIndexes.pop();
        return pid;
      }

      m_FreePointIndexes.pop();

    } while (!m_FreePointIndexes.empty());
  }

  if (m_FreePointIndexes.empty())
  {
    pid = this->GetNumberOfPoints();
    if (pid != 0)
    {
      PointsContainerConstIterator last = this->GetPoints()->End();
      --last;
      pid = last.Index() + 1;
    }
  }
  return pid;
}


/**
 *  The point container being a map, after deleting a point
 *  it is very likely that one index will be missing.
 *  This method "squeeze" the indexes by relocating the points
 *  and their data from the end of their respective container
 *  to the "empty" locations.
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
void
QuadEdgeMesh<TPixel, VDimension, TTraits>::SqueezePointsIds()
{

  // sanity check
  if (m_FreePointIndexes.empty())
  {
    return;
  }

  // Get hold on the last point in the container
  const PointsContainerPointer points = this->GetPoints();
  PointsContainerConstIterator last = points->End();
  --last;

  // Check if there is any data
  const PointDataContainerPointer pointData = this->GetPointData();
  const bool                      HasPointData = (pointData->Size() != 0);

  // if there is get hold on the last point's data
  PointDataContainerIterator lastData = pointData->End();
  if (HasPointData)
  {
    --lastData;
  }

  // for all the free indexes and while there is any gap
  while ((!m_FreePointIndexes.empty()) && (last.Index() >= this->GetNumberOfPoints()))
  {

    // duplicate last point into the empty slot and pop the id from freeID list
    PointIdentifier FilledPointID = AddPoint(GetPoint(last.Index()));

    // same thing for the data if any
    if (HasPointData)
    {
      pointData->SetElement(FilledPointID, pointData->GetElement(lastData.Index()));
    }

    // make sure that all the edges/faces now refer to the new ID
    // i.e. enforce the integrity at the QE level now.
    QEType * EdgeRingEntry = GetPoint(last.Index()).GetEdge();
    if (EdgeRingEntry)
    {

      QEType * EdgeRingIter = EdgeRingEntry;
      do
      {
        EdgeRingIter->SetOrigin(FilledPointID);
        EdgeRingIter = EdgeRingIter->GetOnext();
      } while (EdgeRingIter != EdgeRingEntry);
    }

    // pop the duplicated point from the container, increment iterator
    points->DeleteIndex(last.Index());
    last = points->End();
    --last;

    // same thing for data, if any
    if (HasPointData)
    {
      pointData->DeleteIndex(lastData.Index());
      lastData = pointData->End();
      --lastData;
    }
  }
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::AddPoint(const PointType & p) -> PointIdentifier
{
  const PointIdentifier pid = this->FindFirstUnusedPointIndex();

  this->SetPoint(pid, p);
  return pid;
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
void
QuadEdgeMesh<TPixel, VDimension, TTraits>::DeletePoint(const PointIdentifier & pid)
{
  // We suppose point index is valid
  // otherwise we should test with
  // this->GetPoints()->IndexExists( pid );
  PointType pointToDelete = this->GetPoint(pid);

  // Check that there is no cell that use this point anymore
  // i.e. that the o-next-ring is empty
  if (pointToDelete.GetEdge())
  {
    itkDebugMacro("Point is not isolated.");
    return;
  }

  // Remove the point from the points container
  this->GetPoints()->DeleteIndex(pid);

  // Check if there is associated pointData and eventually delete them
  if (this->GetPointData()->Size() > 0)
  {
    this->GetPointData()->DeleteIndex(pid);
  }

  // store the delete index to later squeeze the ID list
  // needed to write files that expect incremental IDs
  // like vtk
  m_FreePointIndexes.push(pid);
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::GetPoint(const PointIdentifier & pid) const -> PointType
{
  return this->GetPoints()->GetElement(pid);
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::GetVector(const PointIdentifier & pid) const -> VectorType
{
  return this->GetPoint(pid).GetVectorFromOrigin();
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::FindFirstUnusedCellIndex() -> CellIdentifier
{
  CellIdentifier cid;

  if (m_FreeCellIndexes.empty())
  {
    cid = this->GetNumberOfCells();

    if (cid != 0)
    {
      CellsContainerIterator last = this->GetCells()->End();
      --last;
      cid = last.Index() + 1;
    }
  }
  else
  {
    cid = m_FreeCellIndexes.front();
    m_FreeCellIndexes.pop();
  }

  return cid;
}

/**
 *\brief  Construct a new edge ending at points with identifiers given
 *        as arguments.
 * @param  orgPid first endpoint (origin) of the edge to Add.
 * @param destPid second endpoint (destination) of the edge to Add.
 * @sa \ref GeometricalQuadEdge::InsertAfterNextBorderEdgeWithUnsetLeft
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::AddEdge(const PointIdentifier & orgPid, const PointIdentifier & destPid)
  -> QEPrimal *
{
  // Make sure the points are different
  if (orgPid == destPid)
  {
    itkDebugMacro("Creating an edge between the same point.");
    return (QEPrimal *)nullptr;
  }

  // Make sure the points are already in the QuadEdgeMesh container:
  if (!(this->GetPoints()->IndexExists(orgPid)) || !(this->GetPoints()->IndexExists(destPid)))
  {
    itkDebugMacro("One of the points not in the PointSet.");
    return (QEPrimal *)nullptr;
  }

  // Make sure the edge is not already in the container
  QEPrimal * e = this->FindEdge(orgPid, destPid);
  if (e != (QEPrimal *)nullptr)
  {
    itkDebugMacro("Edge already in QuadEdgeMesh.");
    return e;
  }

  // Check if the points have room to receive a new edge
  QEPrimal * eOrigin = this->GetPoint(orgPid).GetEdge();

  if (eOrigin)
  {
    if (eOrigin->IsOriginInternal())
    {
      itkDebugMacro("No room for a new edge in the Origin() ring.");
      return (QEPrimal *)nullptr;
    }
  }

  QEPrimal * eDestination = this->GetPoint(destPid).GetEdge();

  if (eDestination)
  {
    if (eDestination->IsOriginInternal())
    {
      itkDebugMacro("No room for a new edge in the Destination() ring.");
      return (QEPrimal *)nullptr;
    }
  }

  return AddEdgeWithSecurePointList(orgPid, destPid);
}

template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::AddEdgeWithSecurePointList(const PointIdentifier & orgPid,
                                                                      const PointIdentifier & destPid) -> QEPrimal *
{
  const PointsContainerPointer points = this->GetPoints();

  PointType & pOrigin = points->ElementAt(orgPid);
  PointType & pDestination = points->ElementAt(destPid);

  QEPrimal * eOrigin = pOrigin.GetEdge();
  QEPrimal * eDestination = pDestination.GetEdge();

  // Ok, there's room and the points exist
  auto *     newEdge = new EdgeCellType();
  QEPrimal * newEdgeGeom = newEdge->GetQEGeom();

  newEdgeGeom->SetOrigin(orgPid);
  newEdgeGeom->SetDestination(destPid);

  if (!eOrigin)
  {
    pOrigin.SetEdge(newEdgeGeom);
  }
  else
  {
    eOrigin->InsertAfterNextBorderEdgeWithUnsetLeft(newEdgeGeom);
  }

  if (!eDestination)
  {
    pDestination.SetEdge(newEdgeGeom->GetSym());
  }
  else
  {
    eDestination->InsertAfterNextBorderEdgeWithUnsetLeft(newEdgeGeom->GetSym());
  }

  // Add it to the container
  this->PushOnContainer(newEdge);

  return newEdgeGeom;
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
void
QuadEdgeMesh<TPixel, VDimension, TTraits>::PushOnContainer(EdgeCellType * newEdge)
{
  CellIdentifier eid = 0;

  if (!this->GetEdgeCells()->empty())
  {
    CellsContainerConstIterator last = this->GetEdgeCells()->End();
    --last;
    eid = last.Index() + 1;
  }
  newEdge->SetIdent(eid);
  CellAutoPointer pEdge;
  pEdge.TakeOwnership(newEdge);
  this->SetEdgeCell(eid, pEdge);
  ++m_NumberOfEdges;
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
void
QuadEdgeMesh<TPixel, VDimension, TTraits>::DeleteEdge(const PointIdentifier & orgPid, const PointIdentifier & destPid)
{
  // Check if the edge exists
  QEPrimal * e = this->FindEdge(orgPid, destPid);

  if (e == (QEPrimal *)nullptr)
  {
    itkDebugMacro("Edge missing in mesh.");
    return;
  }

  this->DeleteEdge(e);
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
void
QuadEdgeMesh<TPixel, VDimension, TTraits>::DeleteEdge(QEPrimal * e)
{
  const PointIdentifier & orgPid = e->GetOrigin();
  const PointIdentifier & destPid = e->GetDestination();

  const PointsContainerPointer points = this->GetPoints();

  // Check if the Origin point's edge ring entry should be changed
  PointType & pOrigin = points->ElementAt(orgPid);

  if (pOrigin.GetEdge() == e)
  {
    if (!e->IsOriginDisconnected())
    {
      pOrigin.SetEdge(e->GetOprev());
    }
    else
    {
      pOrigin.SetEdge((QEPrimal *)nullptr);
    }
  }

  // Same for the Destination point
  PointType & pDestination = points->ElementAt(destPid);

  if (pDestination.GetEdge() == e->GetSym())
  {
    if (!e->IsDestinationDisconnected())
    {
      pDestination.SetEdge(e->GetLnext());
    }
    else
    {
      pDestination.SetEdge((QEPrimal *)nullptr);
    }
  }

  // This container serves to avoid the MS .net bug when
  // one wants to delete a map element using a map::iterator.
  // Normally, if we delete a map element using an iterator,
  // it should keep the iterator validity but .net doesn't
  // like it, so we delay the cell deletion to a later loop.
  using DeleteCellsCont = std::list<CellIdentifier>;
  DeleteCellsCont cellsToDelete;

  // Delete all references to 'e' in the cell container
  CellsContainerIterator       cit = this->GetCells()->Begin();
  const CellsContainerIterator cend = this->GetCells()->End();

  while (cit != cend)
  {
    auto * pcell = dynamic_cast<PolygonCellType *>(cit.Value());
    bool   toDelete = false;
    if (pcell != (PolygonCellType *)nullptr)
    {
      QEPrimal *                            edge = pcell->GetEdgeRingEntry();
      typename QEPrimal::IteratorGeom       it = edge->BeginGeomLnext();
      const typename QEPrimal::IteratorGeom end = edge->EndGeomLnext();

      while (it != end && !toDelete)
      {
        toDelete = ((it.Value() == e) || (it.Value()->GetSym() == e));
        ++it;
      }

      if (toDelete)
      {
        --m_NumberOfFaces;
        // handle QE level, i.e. for the polygon, just unset the faces
        it = edge->BeginGeomLnext();
        while (it != end)
        {
          it.Value()->UnsetLeft();
          ++it;
        }
      }
    }

    // if the current face is to be deleted,
    // put it in the second container
    // and keep the Id for next cell insertion
    if (toDelete)
    {
      cellsToDelete.push_back(cit.Index());
      m_FreeCellIndexes.push(cit.Index());
    }
    ++cit;
  }

  // we checked all the cells in the container
  // now delete the elements in the map
  auto       dit = cellsToDelete.begin();
  const auto dend = cellsToDelete.end();
  while (dit != dend)
  {
    const CellType * cellToBeDeleted = this->GetCells()->GetElement(*dit);
    delete cellToBeDeleted;
    this->GetCells()->DeleteIndex(*dit);
    ++dit;
  }

  // now delete the edge in the edge container
  CellType * edgeCellToDelete = this->GetEdgeCells()->ElementAt(e->GetIdent());
  this->GetEdgeCells()->DeleteIndex(e->GetIdent());
  delete edgeCellToDelete;
  --m_NumberOfEdges;

  // Now, disconnect it and let the garbage collector do the rest
  this->Modified();
}

/**
 * Delete the incoming edge and all LOCAL references to this edge.
 * By local we mean the ones we can reasonably be aware of i.e.
 * the adjacent faces (that we also delete) and the adjacent points
 * (when the incoming edge is their Onext ring entry).
 * This is to be opposed to \ref DeleteEdge that searches for ALL
 * references to the incoming edge (which is a much heavier process
 * because one as to make an exhaustive search in the CellContainer).
 * \note: when deleting the adjacent faces we also handle the
 *        suppression of the references to those faces in the Lnext()
 *        and Rnext() rings.
 * \warning Nothing is done to remove the potential isolated points
 *        left by this edge deletion (the caller might want to recycle
 *        them). Hence it is the caller's responsibility to manage the
 *        clean-up of adjacent points (when necessary).
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
void
QuadEdgeMesh<TPixel, VDimension, TTraits>::LightWeightDeleteEdge(EdgeCellType * edgeCell)
{
  if (!edgeCell)
  {
    return;
  }

  QEPrimal * e = edgeCell->GetQEGeom();

  if (!e)
  {
    return;
  }
  const PointIdentifier & orgPid = e->GetOrigin();
  const PointIdentifier & destPid = e->GetDestination();

  const PointsContainerPointer points = this->GetPoints();

  if (orgPid != e->m_NoPoint && destPid != e->m_NoPoint)
  {
    // ------------------------------------------------------------------
    // First make sure the points are not pointing to the edge we are
    // trying to delete.

    // Check if the Origin point's edge ring entry is the edge we are
    // trying to delete. When this is the case shift the Origin edge entry
    // to another edge and when no other edge is available leave it
    // to nullptr.
    PointType & pOrigin = points->ElementAt(orgPid);

    if (pOrigin.GetEdge() == e)
    {
      if (!e->IsOriginDisconnected())
      {
        pOrigin.SetEdge(e->GetOprev());
      }
      else
      {
        pOrigin.SetEdge((QEPrimal *)nullptr);
      }
    }

    // Same thing for the Destination point:
    PointType & pDestination = points->ElementAt(destPid);

    if (pDestination.GetEdge() == e->GetSym())
    {
      if (!e->IsDestinationDisconnected())
      {
        pDestination.SetEdge(e->GetLnext());
      }
      else
      {
        pDestination.SetEdge((QEPrimal *)nullptr);
      }
    }
    // ------------------------------------------------------------------
    // Second we need to destroy the adjacent faces (both GetLeft()
    // and GetRight() when they exist) because their very definition
    // makes reference to the edge we are trying to delete:
    if (e->IsLeftSet())
    {
      this->DeleteFace(e->GetLeft());
    }

    if (e->IsRightSet())
    {
      this->DeleteFace(e->GetRight());
    }

    /////////////////////////////////////////////////////////////////
    // Third we need to remove from the container the EdgeCell
    // representing the edge we are trying to destroy at the itk
    // level.
    this->GetEdgeCells()->DeleteIndex(edgeCell->GetIdent());
    edgeCell->SetIdent(0);

    // Eventually, we disconnect (at the QuadEdge level) the edge we
    // are trying to delete and we delete it.
    e->Disconnect();
  }

  --m_NumberOfEdges;
  delete edgeCell;
  this->Modified();
}

template <typename TPixel, unsigned int VDimension, typename TTraits>
void
QuadEdgeMesh<TPixel, VDimension, TTraits>::LightWeightDeleteEdge(QEPrimal * e)
{
  if (!e)
  {
    return;
  }
  const PointIdentifier & orgPid = e->GetOrigin();
  if (orgPid == e->m_NoPoint)
  {
    // org not set
    return;
  }

  const PointIdentifier & destPid = e->GetDestination();
  if (destPid == e->m_NoPoint)
  {
    // dest not set
    return;
  }

  const CellIdentifier LineIdent = e->GetIdent();
  if (LineIdent != m_NoPoint)
  {
    auto * edgeCell = dynamic_cast<EdgeCellType *>(this->GetEdgeCells()->GetElement(LineIdent));
    this->LightWeightDeleteEdge(edgeCell);
  }
  else
  {
    itkDebugMacro("Edge Not found. LineIdent not set?");
    return;
  }
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
void
QuadEdgeMesh<TPixel, VDimension, TTraits>::DeleteFace(FaceRefType faceToDelete)
{
  const CellsContainerPointer cells = this->GetCells();
  CellType *                  c = nullptr;

  if (!cells->GetElementIfIndexExists(faceToDelete, &c))
  {
    itkDebugMacro("No such face in container");
    return;
  }

  auto * cellToDelete = dynamic_cast<PolygonCellType *>(c);
  if (!cellToDelete)
  {
    itkDebugMacro("This Id does not correspond to a face (should be an edge)");
    return;
  }

  // Iterate on the edges adjacent to face and remove references to
  // to this face:
  QEPrimal * e = cellToDelete->GetEdgeRingEntry();

  if (faceToDelete != e->GetLeft())
  {
    e = e->GetSym();
  }

  if (faceToDelete != e->GetLeft())
  {
    itkDebugMacro("Neither e nor e->Sym() are the correct face");
    return;
  }

  typename QEPrimal::IteratorGeom       it = e->BeginGeomLnext();
  const typename QEPrimal::IteratorGeom end = e->EndGeomLnext();

  while (it != end)
  {
    it.Value()->UnsetLeft();
    ++it;
  }

  cells->DeleteIndex(faceToDelete);
  delete cellToDelete;

  --m_NumberOfFaces;

  this->Modified();
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::GetEdge() const -> QEPrimal *
{
  if (this->GetEdgeCells()->empty())
  {
    return (QEPrimal *)nullptr;
  }

  const CellsContainer *            edgeCells = this->GetEdgeCells();
  const CellsContainerConstIterator cit = edgeCells->Begin();
  auto *                            e = dynamic_cast<EdgeCellType *>(cit.Value());

  return e->GetQEGeom();
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::GetEdge(const CellIdentifier & eid) const -> QEPrimal *
{
  CellType * c = nullptr;

  if (!this->GetEdgeCells()->GetElementIfIndexExists(eid, &c))
  {
    itkDebugMacro("No such edge in container");
    return (QEPrimal *)nullptr;
  }

  auto * e = dynamic_cast<EdgeCellType *>(c);
  return e->GetQEGeom();
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::FindEdge(const PointIdentifier & pid0) const -> QEPrimal *
{
  PointType p = this->GetPoint(pid0);

  return p.GetEdge();
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::FindEdge(const PointIdentifier & pid0, const PointIdentifier & pid1) const
  -> QEPrimal *
{
  QEPrimal * initialEdge = this->GetPoint(pid0).GetEdge();

  if (initialEdge)
  {
    typename QEPrimal::IteratorGeom       it = initialEdge->BeginGeomOnext();
    const typename QEPrimal::IteratorGeom end = initialEdge->EndGeomOnext();
    while (it != end)
    {
      if (it.Value()->GetDestination() == pid1)
      {
        return it.Value();
      }
      ++it;
    }
  }
  return static_cast<QEPrimal *>(nullptr);
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::FindEdgeCell(const PointIdentifier & pid0,
                                                        const PointIdentifier & pid1) const -> EdgeCellType *
{
  auto *     result = (EdgeCellType *)nullptr;
  QEPrimal * EdgeGeom = FindEdge(pid0, pid1);

  if (EdgeGeom != (QEPrimal *)nullptr)
  {
    const CellIdentifier LineIdent = EdgeGeom->GetIdent();
    if (LineIdent != m_NoPoint)
    {
      result = dynamic_cast<EdgeCellType *>(this->GetEdgeCells()->GetElement(LineIdent));
    }
  }
  return result;
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::AddFace(const PointIdList & points) -> QEPrimal *
{
  const size_t N = points.size();

#ifndef NDEBUG

  // Check that there are no duplicate points
  for (size_t i = 0; i < N; ++i)
  {
    PointIdentifier       count{};
    const PointIdentifier pointId = points[i];
    for (const auto & point : points)
    {
      if (point == pointId)
      {
        ++count;
      }
    }
    if (count != 1)
    {
      itkDebugMacro("Point " << i << " is duplicated");
      return (QEPrimal *)nullptr;
    }
  }

  PointsContainerPointer pointsContainer = this->GetPoints();

  // Check that all points exist
  for (size_t i = 0; i < N; ++i)
  {
    if (!pointsContainer->IndexExists(points[i]))
    {
      itkDebugMacro("Point " << i << " is missing in the mesh");
      return (QEPrimal *)nullptr;
    }
  }
#endif

  // Check if existing edges have no face on the left.
  for (size_t i = 0; i < N; ++i)
  {
    const PointIdentifier pid0 = points[i];
    const PointIdentifier pid1 = points[(i + 1) % N];

    QEPrimal * edge = this->FindEdge(pid0, pid1);

    if (edge)
    {
      if (edge->IsLeftSet())
      {
        itkDebugMacro("Edge [" << i << ' ' << ((i + 1) % N) << " has a left face.");
        return (QEPrimal *)nullptr;
      }
    }
  }

  return AddFaceWithSecurePointList(points);
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::AddFaceWithSecurePointList(const PointIdList & points) -> QEPrimal *
{
  return AddFaceWithSecurePointList(points, true);
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::AddFaceWithSecurePointList(const PointIdList & points, bool CheckEdges)
  -> QEPrimal *
{
  const auto numberOfPoints = static_cast<PointIdentifier>(points.size());

  using QEList = std::vector<QEPrimal *>;
  QEList FaceQEList(numberOfPoints, nullptr);

  // Now create edge list and create missing edges if needed.
  for (PointIdentifier i = 0; i < numberOfPoints; ++i)
  {
    const PointIdentifier pid0 = points[i];
    const PointIdentifier pid1 = points[(i + 1) % numberOfPoints];
    QEPrimal *            edge = this->FindEdge(pid0, pid1);

    if (!edge && CheckEdges)
    {
      QEPrimal * entry = this->AddEdgeWithSecurePointList(pid0, pid1);
      if (entry == (QEPrimal *)nullptr)
      {
        return entry;
      }
      FaceQEList[i] = entry;
    }
    else
    {
      // FIXME throw exception here if !edge
      FaceQEList[i] = edge;
    }
  }

  // Reorder all Onext rings
  QEPrimal * e0 = FaceQEList.back();

  auto fIt = FaceQEList.begin();
  auto fEnd = FaceQEList.end();

  while (fIt != fEnd)
  {
    QEPrimal * e1 = e0->GetSym();
    e0 = *fIt;

    e0->ReorderOnextRingBeforeAddFace(e1);
    ++fIt;
  }

  // all edges are ready to receive a face on the left
  QEPrimal * entry = FaceQEList.front();

  if (!entry)
  {
    // FIXME throw exception here instead
    itkDebugMacro("entry == nullptr");
    return (QEPrimal *)nullptr;
  }

  this->AddFace(entry);

  return entry;
}

/**
 * We here make the strong assumption that the caller was wise enough
 * to build/handle the connectivity at the QE level. This method
 * simply creates a new PolygonCell and assigns it as the left face
 * of all edges in the Lnext ring of the incoming argument.
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
void
QuadEdgeMesh<TPixel, VDimension, TTraits>::AddFace(QEPrimal * entry)
{
  // Create the cell and add it to the container
  auto *               faceCell = new PolygonCellType(entry);
  const CellIdentifier fid = this->FindFirstUnusedCellIndex();

  faceCell->SetIdent(fid);

  // Associate the above generated CellIndex as the default FaceRefType
  // of the new face [ i.e. use the itk level CellIdentifier as the
  // GeometricalQuadEdge::m_Origin of dual edges (edges of type QEDual) ].
  typename QEPrimal::IteratorGeom       it = entry->BeginGeomLnext();
  const typename QEPrimal::IteratorGeom end = entry->EndGeomLnext();

  while (it != end)
  {
    it.Value()->SetLeft(fid);
    ++it;
  }

  ++m_NumberOfFaces;
  CellAutoPointer face;
  face.TakeOwnership(faceCell);
  this->Superclass::SetCell(fid, face);
}

/**
 * Add a triangle face to this QuadEdgeMesh.
 * @param aPid \ref PointIdentifier of first point
 * @param bPid \ref PointIdentifier of second point
 * @param cPid \ref PointIdentifier of third point
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::AddFaceTriangle(const PointIdentifier & aPid,
                                                           const PointIdentifier & bPid,
                                                           const PointIdentifier & cPid) -> QEPrimal *
{
  PointIdList points(3);

  points[0] = aPid;
  points[1] = bPid;
  points[2] = cPid;
  return this->AddFace(points);
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
QuadEdgeMesh<TPixel, VDimension, TTraits>::QuadEdgeMesh()
  : m_EdgeCellsContainer(CellsContainer::New())
  , m_NumberOfFaces(0)
  , m_NumberOfEdges(0)
{}

template <typename TPixel, unsigned int VDimension, typename TTraits>
QuadEdgeMesh<TPixel, VDimension, TTraits>::~QuadEdgeMesh()
{
  this->ClearCellsContainer();
}

template <typename TPixel, unsigned int VDimension, typename TTraits>
void
QuadEdgeMesh<TPixel, VDimension, TTraits>::ClearCellsContainer()
{
  if (m_EdgeCellsContainer->GetReferenceCount() == 1)
  {
    CellsContainerIterator       EdgeCell = m_EdgeCellsContainer->Begin();
    const CellsContainerIterator EdgeEnd = m_EdgeCellsContainer->End();
    while (EdgeCell != EdgeEnd)
    {
      const CellType * EdgeCellToBeDeleted = EdgeCell->Value();
      delete EdgeCellToBeDeleted;
      ++EdgeCell;
    }
    m_EdgeCellsContainer->Initialize();
  }
}

/**
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::ComputeEdgeLength(QEPrimal * e) -> CoordinateType
{
  const PointsContainer * points = this->GetPoints();

  const PointType org = points->GetElement(e->GetOrigin());
  const PointType dest = points->GetElement(e->GetDestination());

  return org.EuclideanDistanceTo(dest);
}

/**
 * \brief Compute the total number of USED points. This differs from
 * \ref Mesh::GetNumberOfPoints() that will return the total number of
 * points including the ones that have no entry in the edge ring.
 *
 * \note This method is an optional utility of the class: its
 * understanding is not useful at first contact with the class.
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::ComputeNumberOfPoints() const -> PointIdentifier
{
  const PointsContainer * points = this->GetPoints();

  if (!points)
  {
    itkDebugMacro("No point container");
    return 0;
  }

  PointIdentifier                    numberOfPoints{};
  PointsContainerConstIterator       pointIterator = points->Begin();
  const PointsContainerConstIterator pointEnd = points->End();

  while (pointIterator != pointEnd)
  {
    if (pointIterator.Value().GetEdge())
    {
      ++numberOfPoints;
    }
    ++pointIterator;
  }

  return numberOfPoints;
}

/**
 * \brief Compute the total number of faces.
 *
 * \note This method is an optional utility of the class: its
 * understanding is not useful at first contact with the class.
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::ComputeNumberOfFaces() const -> CellIdentifier
{
  CellIdentifier                    numberOfFaces{};
  CellsContainerConstIterator       cellIterator = this->GetCells()->Begin();
  const CellsContainerConstIterator cellEnd = this->GetCells()->End();

  PointIdentifier NumOfPoints;

  while (cellIterator != cellEnd)
  {
    NumOfPoints = cellIterator.Value()->GetNumberOfPoints();
    if (NumOfPoints > 2)
    {
      ++numberOfFaces;
    }
    ++cellIterator;
  }

  return numberOfFaces;
}

/**
 * \brief Compute the total number of edges.
 *
 * \note This method is an optional utility of the class: it's
 *       understanding is not useful at first contact with the class.
 */
template <typename TPixel, unsigned int VDimension, typename TTraits>
auto
QuadEdgeMesh<TPixel, VDimension, TTraits>::ComputeNumberOfEdges() const -> CellIdentifier
{
  return static_cast<CellIdentifier>(this->GetEdgeCells()->size());
}
} // namespace itk

#endif
