// Created on: 1999-06-18
// Created by: Sergei ZERTCHANINOV
// Copyright (c) 1999 Matra Datavision
// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#include <BRep_Builder.hxx>
#include <BRep_Tool.hxx>
#include <BRepBuilderAPI_Sewing.hxx>
#include <Geom2d_Curve.hxx>
#include <gp_Pnt.hxx>
#include <ShapeAnalysis_Edge.hxx>
#include <ShapeAnalysis_WireOrder.hxx>
#include <ShapeBuild_ReShape.hxx>
#include <ShapeFix_Face.hxx>
#include <ShapeFix_FaceConnect.hxx>
#include <ShapeFix_Wire.hxx>
#include <Standard_ErrorHandler.hxx>
#include <Standard_Failure.hxx>
#include <TopExp.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Iterator.hxx>
#include <TopoDS_Shell.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Wire.hxx>
#include <TopTools_Array1OfShape.hxx>
#include <TopTools_DataMapIteratorOfDataMapOfShapeShape.hxx>
#include <TopTools_ListOfShape.hxx>

#ifdef OCCT_DEBUG
  #include <TopTools_MapOfShape.hxx>
#endif

//=======================================================================
// function : ShapeFix_FaceConnect
//=======================================================================

ShapeFix_FaceConnect::ShapeFix_FaceConnect() {}

//=================================================================================================

Standard_Boolean ShapeFix_FaceConnect::Add(const TopoDS_Face& aFirst, const TopoDS_Face& aSecond)
{
  if (!aFirst.IsNull() && !aSecond.IsNull())
  {
    // Process first face
    if (myConnected.IsBound(aFirst))
    {
      // Find list for the first face
      TopTools_ListOfShape& theFirstList = myConnected(aFirst);
      // Append second face to the first list
      TopTools_ListIteratorOfListOfShape theIter;
      for (theIter.Initialize(theFirstList); theIter.More(); theIter.Next())
        if (theIter.Value().IsSame(aSecond))
          return Standard_True;
      theFirstList.Append(aSecond);
    }
    else
    {
      // Append second face to the first list
      TopTools_ListOfShape theNewFirstList;
      theNewFirstList.Append(aSecond);
      myConnected.Bind(aFirst, theNewFirstList);
    }

    // Process second face if not same
    if (!aFirst.IsSame(aSecond))
    {
      if (myConnected.IsBound(aSecond))
      {
        // No need to iterate on faces - append first
        myConnected(aSecond).Append(aFirst);
      }
      else
      {
        // Append first face to the second list
        TopTools_ListOfShape theNewSecondList;
        theNewSecondList.Append(aFirst);
        myConnected.Bind(aSecond, theNewSecondList);
      }
    }

    return Standard_True;
  }

  return Standard_False;
}

//=================================================================================================

TopoDS_Shell ShapeFix_FaceConnect::Build(const TopoDS_Shell& shell,
                                         const Standard_Real sewtoler,
                                         const Standard_Real fixtoler)
{
  TopoDS_Shell result = shell;

  /***************************************************************
  / INITIAL PREPARATIONS
  / Fill map of original free edges,
  / fill maps of resulting free and shared edges
  ***************************************************************/

  // Clear maps of free and shared edges
  myOriFreeEdges.Clear();
  myResFreeEdges.Clear();
  myResSharEdges.Clear();

  TopTools_DataMapOfShapeShape theFreeEdges;
  TopoDS_Shape                 theEdge, theFace;

  // Fill map of free edges / faces
  for (TopoDS_Iterator itf(result); itf.More(); itf.Next())
  {
    theFace = itf.Value();
    for (TopExp_Explorer expe(theFace, TopAbs_EDGE); expe.More(); expe.Next())
    {
      theEdge = expe.Current();
      if (theFreeEdges.IsBound(theEdge))
        theFreeEdges.UnBind(theEdge);
      else
        theFreeEdges.Bind(theEdge, theFace);
    }
  }

  // Fill maps of original and resulting edges
  for (TopTools_DataMapIteratorOfDataMapOfShapeShape theFEIter(theFreeEdges); theFEIter.More();
       theFEIter.Next())
  {
    // Get pair (face / free edge)
    theEdge = theFEIter.Key(), theFace = theFEIter.Value();
    // Process faces with bad connectivities only
    if (myConnected.IsBound(theFace) && !BRep_Tool::Degenerated(TopoDS::Edge(theEdge)))
    {
      // Add to the map of original free edges
      if (myOriFreeEdges.IsBound(theFace))
      {
        // Append free edge to the existing list
        myOriFreeEdges(theFace).Append(theEdge);
      }
      else
      {
        // Append free edge to the new list
        TopTools_ListOfShape theNewList;
        theNewList.Append(theEdge);
        myOriFreeEdges.Bind(theFace, theNewList);
      }
      // Add to the maps of intermediate free and resulting edges
      if (!myResFreeEdges.IsBound(theEdge))
      {
        TopTools_ListOfShape theFree, theShared;
        theFree.Append(theEdge);
        myResFreeEdges.Bind(theEdge, theFree);
        myResSharEdges.Bind(theEdge, theShared);
      }
    }
  }

  // Clear the temporary map of free edges
  theFreeEdges.Clear();

#ifdef OCCT_DEBUG
  //-------------------------------
  // szv debug - preparation results
  //-------------------------------
  if (!myOriFreeEdges.IsEmpty())
  {
    std::cout << std::endl << "FACE CONNECT PREPARATION RESULTS:" << std::endl;
    std::cout << "---------------------------------" << std::endl;
    Standard_Integer freenum = 0, facenum = 0;
    for (TopTools_DataMapIteratorOfDataMapOfShapeListOfShape theOFIter(myOriFreeEdges);
         theOFIter.More();
         theOFIter.Next())
    {
      freenum += theOFIter.Value().Extent();
      facenum++;
    }
    std::cout << "TOTAL: " << facenum << " faces containing " << freenum << " free edges"
              << std::endl;
  }
  //-------------------------------
#endif

  /***************************************************************
  / APPLY SEWING ON CONNECTED FACES
  / Change maps of original free edges and resulting shared edges
  ***************************************************************/

  if (!myOriFreeEdges.IsEmpty())
  {

    // Allocate array of faces to be sewed
    TopoDS_Shape           theFirstFace, theSecondFace;
    TopTools_Array1OfShape theFacesToSew(1, 2);
    Standard_Integer       theNumOfFacesToSew = 0;
    Standard_Boolean       skip_pair          = Standard_False;

    TopTools_ListIteratorOfListOfShape theOriginalIter, theResultsIter;
    TopoDS_Shape                       theAuxE, theOrigE, theAuxF;

    BRep_Builder theBuilder;

    TopTools_DataMapOfShapeListOfShape theProcessed;

    for (TopTools_DataMapIteratorOfDataMapOfShapeListOfShape theConnectedIter(myConnected);
         theConnectedIter.More();
         theConnectedIter.Next())
    {
      // Process first face only if it is in the map of faces / free edges
      theFirstFace = theConnectedIter.Key();
      if (myOriFreeEdges.IsBound(theFirstFace))
      {

        // Place first face into the array
        theFacesToSew.SetValue(1, theFirstFace);
        theNumOfFacesToSew = 1;
        // Create the list of processed faces
        TopTools_ListOfShape theProcessedList;

        // Explore the list of connected faces
        const TopTools_ListOfShape&        theConnectedList = theConnectedIter.Value();
        TopTools_ListIteratorOfListOfShape theConnectedListIter;
        for (theConnectedListIter.Initialize(theConnectedList); theConnectedListIter.More();
             theConnectedListIter.Next())
        {
          // Process second face only if it is in the map of faces / free edges
          theSecondFace = theConnectedListIter.Value();
          if (myOriFreeEdges.IsBound(theSecondFace))
          {

            // Place second face into the array
            theFacesToSew.SetValue(2, theSecondFace);
            // Add second face to the list of processed faces
            theProcessedList.Append(theSecondFace);

            // Skip the pair if already processed
            skip_pair = Standard_False;
            if (theProcessed.IsBound(theSecondFace))
            {
              TopTools_ListOfShape&              theProcCnxList = theProcessed(theSecondFace);
              TopTools_ListIteratorOfListOfShape theProcCnxListIter;
              for (theProcCnxListIter.Initialize(theProcCnxList);
                   theProcCnxListIter.More() && !skip_pair;
                   theProcCnxListIter.Next())
                if (theFirstFace.IsSame(theProcCnxListIter.Value()))
                  skip_pair = Standard_True;
            }
            if (!skip_pair)
            {

              // Process second face for the pair of different faces only
              if (theFirstFace.IsSame(theSecondFace))
              {
#ifdef OCCT_DEBUG
                std::cout << "Warning: ShapeFix_FaceConnect::Build: Self-connected face"
                          << std::endl;
#endif
              }
              else
                theNumOfFacesToSew = 2;

              TopTools_DataMapOfShapeShape theSewerWires;
              BRepBuilderAPI_Sewing        theSewer(sewtoler);

              // Prepare set of faces containing free edges
              Standard_Integer i = 1;
              for (i = 1; i <= theNumOfFacesToSew; i++)
              {
                // Prepare empty face to fill with free edges
                TopoDS_Shape theFaceToSew = theFacesToSew(i);
                theAuxF                   = theFaceToSew.EmptyCopied();
                // Fill empty face with free edges
                for (theOriginalIter.Initialize(myOriFreeEdges(theFaceToSew));
                     theOriginalIter.More();
                     theOriginalIter.Next())
                {
                  for (theResultsIter.Initialize(myResFreeEdges(theOriginalIter.Value()));
                       theResultsIter.More();
                       theResultsIter.Next())
                  {
                    // Bind free edge to wire to find results later
                    theAuxE = theResultsIter.Value();
                    TopoDS_Wire theAuxW;
                    theBuilder.MakeWire(theAuxW);
                    theBuilder.Add(theAuxW, theAuxE);
                    theBuilder.Add(theAuxF, theAuxW);
                    theSewerWires.Bind(theAuxE, theAuxW);
                    theSewer.Add(theAuxW);
                  }
                }
                // Add constructed face to sewer
                theSewer.Add(theAuxF);
              }

              // Perform sewing on the list of free edges
              Standard_Boolean sewing_ok = Standard_True;
              {
                try
                {
                  OCC_CATCH_SIGNALS theSewer.Perform();
                }
                catch (Standard_Failure const&)
                {
                  sewing_ok = Standard_False;
                }
              }
              if (sewing_ok)
                if (theSewer.SewedShape().IsNull())
                  sewing_ok = Standard_False;

              if (sewing_ok)
              {
                TopTools_DataMapOfShapeShape theResultEdges;

                // Find modified edges for the faces
                for (i = 1; i <= theNumOfFacesToSew; i++)
                {
                  for (theOriginalIter.Initialize(myOriFreeEdges(theFacesToSew(i)));
                       theOriginalIter.More();
                       theOriginalIter.Next())
                  {
                    // Get original free edge
                    theOrigE                             = theOriginalIter.Value();
                    TopTools_ListOfShape& theOldFreeList = myResFreeEdges(theOrigE);
                    theResultsIter.Initialize(theOldFreeList);
                    while (theResultsIter.More())
                    {
                      theAuxE = theSewerWires(theResultsIter.Value());
                      // Process modified edges
                      if (theSewer.IsModified(theAuxE))
                      {
                        // Fill map of result edges
                        for (TopExp_Explorer expe(theSewer.Modified(theAuxE), TopAbs_EDGE);
                             expe.More();
                             expe.Next())
                        {
                          theAuxE = expe.Current();
                          // Check edge for being shared
                          if (theResultEdges.IsBound(theAuxE))
                          {
                            // Edge was shared - move in results list
                            myResSharEdges(theResultEdges(theAuxE)).Append(theAuxE);
                            myResSharEdges(theOrigE).Append(theAuxE);
                            theResultEdges.UnBind(theAuxE);
                          }
                          else
                            theResultEdges.Bind(theAuxE, theOrigE);
                        }
                        // Remove modified free edge from the list
                        theOldFreeList.Remove(theResultsIter);
                      }
                      else
                        theResultsIter.Next();
                    }
                  }
                }

                // Put free edges back to the lists of results
                for (TopTools_DataMapIteratorOfDataMapOfShapeShape theResIter(theResultEdges);
                     theResIter.More();
                     theResIter.Next())
                {
                  theAuxE = theResIter.Key();
                  myResFreeEdges(theResIter.Value()).Append(theAuxE);
                }
              }
            }
          }
        }

        // Bind the list of processed faces to the processed face
        theProcessed.Bind(theFirstFace, theProcessedList);
      }
    }

    // Clear the temporary map of processed faces
    theProcessed.Clear();

#ifdef OCCT_DEBUG
    //-------------------------------
    // szv debug - sewing results
    //-------------------------------
    std::cout << std::endl << "FACE CONNECT SEWING RESULTS:" << std::endl;
    std::cout << "----------------------------" << std::endl;
    std::cout << "Sewing tolerance was set to " << sewtoler << std::endl;
    Standard_Integer totfree = 0, totshared = 0;
    for (TopTools_DataMapIteratorOfDataMapOfShapeListOfShape theOF2Iter(myOriFreeEdges);
         theOF2Iter.More();
         theOF2Iter.Next())
    {
      TopTools_ListIteratorOfListOfShape theOFL2Iter;
      for (theOFL2Iter.Initialize(theOF2Iter.Value()); theOFL2Iter.More(); theOFL2Iter.Next())
      {
        totfree += myResFreeEdges(theOFL2Iter.Value()).Extent();
        totshared += myResSharEdges(theOFL2Iter.Value()).Extent();
      }
    }
    std::cout << "TOTAL: " << totfree << " free, " << totshared << " shared edges" << std::endl;
    //-------------------------------
#endif

    /***************************************************************
    / PERFORM EDGES REPLACEMENT
    ***************************************************************/

    TopTools_DataMapOfShapeShape       theRepEdges;
    TopTools_DataMapOfShapeListOfShape theRepVertices;
    TopTools_DataMapOfShapeShape       theOldVertices;
    TopTools_DataMapOfShapeListOfShape theNewVertices;

    // Replace old edges by resulting ones
    TopoDS_Wire   theNewW;
    TopoDS_Vertex theOldV1, theOldV2, theNewV1, theNewV2, theNewV;
    gp_Pnt        theOldP1, theOldP2;
    Standard_Real dist1, dist2, curdist1, curdist2;
    for (TopTools_DataMapIteratorOfDataMapOfShapeListOfShape theOEIter(myOriFreeEdges);
         theOEIter.More();
         theOEIter.Next())
    {
      // Iterate on original free edges
      for (theOriginalIter.Initialize(theOEIter.Value()); theOriginalIter.More();
           theOriginalIter.Next())
      {
        TopoDS_Edge theOldE = TopoDS::Edge(theOriginalIter.Value());

        // Prepare empty wire to add new edges for reshape
        theBuilder.MakeWire(theNewW);

        // Explore new edges and vertices
        Standard_Boolean emptywire = Standard_True;
        for (Standard_Integer i = 1; i <= 2; i++)
        {
          // Select list of free or shared edges
          if (i == 1)
            theResultsIter.Initialize(myResFreeEdges(theOldE));
          else
            theResultsIter.Initialize(myResSharEdges(theOldE));
          // Iterate on new edges
          for (; theResultsIter.More(); theResultsIter.Next())
          {
            theAuxE = theResultsIter.Value();
            if (!theAuxE.IsSame(theOldE))
            {
              // Add new edge to the wire
              theBuilder.Add(theNewW, theAuxE);
              emptywire = Standard_False;
            }
          }
        }

        if (!emptywire)
        {

          // Get vertices on old and new edges
          TopExp::Vertices(theOldE, theOldV1, theOldV2);
          theOldP1 = BRep_Tool::Pnt(theOldV1);
          theOldP2 = BRep_Tool::Pnt(theOldV2);

          // Process vertices for replacing
          dist1 = -1.;
          dist2 = -1.;
          for (TopExp_Explorer expv(theNewW, TopAbs_VERTEX); expv.More(); expv.Next())
          {
            TopoDS_Vertex theNewVtx = TopoDS::Vertex(expv.Current());
            gp_Pnt        theNewPt  = BRep_Tool::Pnt(theNewVtx);
            curdist1                = theOldP1.Distance(theNewPt);
            curdist2                = theOldP2.Distance(theNewPt);
            if (dist1 < 0 || curdist1 < dist1)
            {
              dist1    = curdist1;
              theNewV1 = theNewVtx;
            }
            if (dist2 < 0 || curdist2 < dist2)
            {
              dist2    = curdist2;
              theNewV2 = theNewVtx;
            }
          }

          // Place results in map for replacing
          if (!theOldV1.IsSame(theNewV1))
          {
            if (theRepVertices.IsBound(theOldV1))
            {
              TopTools_ListOfShape&              theList1 = theRepVertices(theOldV1);
              TopTools_ListIteratorOfListOfShape theIter1;
              Standard_Boolean                   found = Standard_False;
              for (theIter1.Initialize(theList1); theIter1.More(); theIter1.Next())
                if (theIter1.Value().IsSame(theNewV1))
                {
                  found = Standard_True;
                  break;
                }
              if (!found)
                theList1.Append(theNewV1);
            }
            else
            {
              TopTools_ListOfShape theNewList1;
              theNewList1.Append(theNewV1);
              theRepVertices.Bind(theOldV1, theNewList1);
            }
          }
          if (!theOldV2.IsSame(theNewV2))
          {
            if (theRepVertices.IsBound(theOldV2))
            {
              TopTools_ListOfShape&              theList2 = theRepVertices(theOldV2);
              TopTools_ListIteratorOfListOfShape theIter2;
              Standard_Boolean                   found = Standard_False;
              for (theIter2.Initialize(theList2); theIter2.More(); theIter2.Next())
                if (theIter2.Value().IsSame(theNewV2))
                {
                  found = Standard_True;
                  break;
                }
              if (!found)
                theList2.Append(theNewV2);
            }
            else
            {
              TopTools_ListOfShape theNewList2;
              theNewList2.Append(theNewV2);
              theRepVertices.Bind(theOldV2, theNewList2);
            }
          }

          // Bind edge to replace
          theRepEdges.Bind(theOldE, theNewW);
        }
      }
    }

    if (!theRepEdges.IsEmpty())
    {

      Handle(ShapeBuild_ReShape) theReShape = new ShapeBuild_ReShape;

      // Replace edges
      for (TopTools_DataMapIteratorOfDataMapOfShapeShape theREIter(theRepEdges); theREIter.More();
           theREIter.Next())
      {
        theReShape->Replace(theREIter.Key() /*.Oriented(TopAbs_FORWARD)*/,
                            theREIter.Value() /*.Oriented(TopAbs_FORWARD)*/);
      }
      // smh#8
      TopoDS_Shape tmpReShape = theReShape->Apply(result);
      result                  = TopoDS::Shell(tmpReShape);
      if (theReShape->Status(ShapeExtend_OK))
      {
#ifdef OCCT_DEBUG
        std::cout << "Warning: ShapeFix_FaceConnect::Build: Edges not replaced by ReShape"
                  << std::endl;
#endif
      }
      else if (theReShape->Status(ShapeExtend_FAIL1))
      {
#ifdef OCCT_DEBUG
        std::cout << "Error: ShapeFix_FaceConnect::Build: ReShape failed on edges" << std::endl;
#endif
      }
      else
      {

        Handle(ShapeFix_Wire)        SFW = new ShapeFix_Wire;
        Handle(ShapeFix_Face)        SFF = new ShapeFix_Face;
        ShapeAnalysis_Edge           SAE;
        Standard_Real                f, l;
        Handle(Geom2d_Curve)         c2d;
        Handle(ShapeExtend_WireData) sewd;

        // Perform necessary fixes on subshapes
        // smh#8
        TopoDS_Shape emptyCopiedShell = result.EmptyCopied();
        TopoDS_Shell theShell         = TopoDS::Shell(emptyCopiedShell);
        for (TopoDS_Iterator itf1(result); itf1.More(); itf1.Next())
        {
          TopoDS_Face newface = TopoDS::Face(itf1.Value());
          // smh#8
          TopoDS_Shape emptyCopiedFace = newface.EmptyCopied();
          TopoDS_Face  EmpFace         = TopoDS::Face(emptyCopiedFace);
          for (TopoDS_Iterator itw(newface); itw.More(); itw.Next())
          {
            if (itw.Value().ShapeType() != TopAbs_WIRE)
              continue;
            TopoDS_Wire theWire = TopoDS::Wire(itw.Value());

            sewd = new ShapeExtend_WireData(theWire);
            ShapeAnalysis_WireOrder SAWO(Standard_False, 0);
            for (Standard_Integer i = 1; i <= sewd->NbEdges(); i++)
            {

              // smh#8
              TopoDS_Shape tmpFace = EmpFace.Oriented(TopAbs_FORWARD);
              if (!SAE.PCurve(sewd->Edge(i), TopoDS::Face(tmpFace), c2d, f, l))
                continue;
              SAWO.Add(c2d->Value(f).XY(), c2d->Value(l).XY());
            }
            SAWO.Perform();

            SFW->Load(sewd);
            SFW->FixReorder(SAWO);
            SFW->FixReorder();

            SFW->SetFace(EmpFace);
            SFW->SetPrecision(fixtoler);
            SFW->SetMaxTolerance(sewtoler);

            SFW->FixEdgeCurves();
            SFW->FixSelfIntersection();
            theWire = SFW->Wire();
            theBuilder.Add(EmpFace, theWire);
          }
          // #ifdef AIX  CKY : applies to all platforms
          SFF->Init(EmpFace);
          //	  SFF->Init(TopoDS::Face(EmpFace));

          TopTools_DataMapOfShapeListOfShape MapWires;
          MapWires.Clear();
          if (SFF->FixOrientation(MapWires))
            EmpFace = SFF->Face();
          theBuilder.Add(theShell, EmpFace);
        }
        theShell.Closed(BRep_Tool::IsClosed(theShell));
        result = theShell;

        if (!theRepVertices.IsEmpty())
        {

          // Prepare vertices to replace
          TopoDS_Shape theOld, theNew, theRep, theAux;
          for (TopTools_DataMapIteratorOfDataMapOfShapeListOfShape theRV1Iter(theRepVertices);
               theRV1Iter.More();
               theRV1Iter.Next())
          {
            // Get the old vertex, create empty list of replaced vertices
            theOld = theRV1Iter.Key();
            TopTools_ListOfShape theNewList;
            // Explore the list of new vertices
            TopTools_ListIteratorOfListOfShape theN1Iter;
            for (theN1Iter.Initialize(theRV1Iter.Value()); theN1Iter.More(); theN1Iter.Next())
            {
              theNew = theN1Iter.Value();
              if (theOldVertices.IsBound(theNew))
              {
                // Vertex has a replacing vertex in the map
                theRep = theOldVertices(theNew);
                if (!theRep.IsSame(theOld))
                {
                  // Vertex is not in current list
                  theOldVertices.Bind(theRep, theOld);
                  theNewList.Append(theRep);
                  TopTools_ListIteratorOfListOfShape theN3Iter;
                  for (theN3Iter.Initialize(theNewVertices(theRep)); theN3Iter.More();
                       theN3Iter.Next())
                  {
                    theAux                 = theN3Iter.Value();
                    theOldVertices(theAux) = theOld;
                    theNewList.Append(theAux);
                  }
                  theNewVertices.UnBind(theRep);
                }
              }
              else
              {
                theOldVertices.Bind(theNew, theOld);
                theNewList.Append(theNew);
              }
            }
            theNewVertices.Bind(theOld, theNewList);
          }

          // Update vertices positions and tolerances
          TopoDS_Vertex theNewVert, theOldVert;
          for (TopTools_DataMapIteratorOfDataMapOfShapeListOfShape theRV2Iter(theNewVertices);
               theRV2Iter.More();
               theRV2Iter.Next())
          {
            theNewVert = TopoDS::Vertex(theRV2Iter.Key());
            // Calculate the vertex position
            gp_Pnt theLBound, theRBound, thePosition;
            theLBound = theRBound = BRep_Tool::Pnt(theNewVert);
            TopTools_ListIteratorOfListOfShape theN2Iter;
            for (theN2Iter.Initialize(theRV2Iter.Value()); theN2Iter.More(); theN2Iter.Next())
            {
              thePosition       = BRep_Tool::Pnt(TopoDS::Vertex(theN2Iter.Value()));
              Standard_Real val = thePosition.X();
              if (val < theLBound.X())
                theLBound.SetX(val);
              else if (val > theRBound.X())
                theRBound.SetX(val);
              val = thePosition.Y();
              if (val < theLBound.Y())
                theLBound.SetY(val);
              else if (val > theRBound.Y())
                theRBound.SetY(val);
              val = thePosition.Z();
              if (val < theLBound.Z())
                theLBound.SetZ(val);
              else if (val > theRBound.Z())
                theRBound.SetZ(val);
            }
            thePosition                = gp_Pnt((theLBound.XYZ() + theRBound.XYZ()) / 2.);
            Standard_Real theTolerance = 0., curtoler;
            // Calculate the vertex tolerance
            for (theN2Iter.Initialize(theRV2Iter.Value()); theN2Iter.More(); theN2Iter.Next())
            {
              theOldVert = TopoDS::Vertex(theN2Iter.Value());
              curtoler =
                thePosition.Distance(BRep_Tool::Pnt(theOldVert)) + BRep_Tool::Tolerance(theOldVert);
              if (curtoler > theTolerance)
                theTolerance = curtoler;
            }
            curtoler =
              thePosition.Distance(BRep_Tool::Pnt(theNewVert)) + BRep_Tool::Tolerance(theNewVert);
            if (curtoler > theTolerance)
              theTolerance = curtoler;
            theBuilder.UpdateVertex(theNewVert, thePosition, theTolerance);
          }

          // Replace vertices
          theReShape->Clear();
          for (TopTools_DataMapIteratorOfDataMapOfShapeShape theNVIter(theOldVertices);
               theNVIter.More();
               theNVIter.Next())
            theReShape->Replace(theNVIter.Key().Oriented(TopAbs_FORWARD),
                                theNVIter.Value().Oriented(TopAbs_FORWARD));
          // smh#8
          TopoDS_Shape tmpshape = theReShape->Apply(result);
          result                = TopoDS::Shell(tmpshape);

          if (theReShape->Status(ShapeExtend_FAIL1))
          {
#ifdef OCCT_DEBUG
            std::cout << "Error: ShapeFix_FaceConnect::Build: ReShape failed on vertices"
                      << std::endl;
#endif
          }
        }

#ifdef OCCT_DEBUG
        //-------------------------------
        // szv debug - reshape results
        //-------------------------------
        std::cout << std::endl << "FACE CONNECT REPLACEMENT RESULTS:" << std::endl;
        std::cout << "---------------------------------" << std::endl;
        TopTools_MapOfShape theTmpMap;
        Standard_Integer    toteold = 0, totenew = 0;
        for (TopTools_DataMapIteratorOfDataMapOfShapeShape theR1Iter(theRepEdges); theR1Iter.More();
             theR1Iter.Next())
        {
          toteold++;
          if (!theTmpMap.Contains(theR1Iter.Value()))
          {
            theTmpMap.Add(theR1Iter.Value());
            for (TopoDS_Iterator itw(TopoDS::Wire(theR1Iter.Value())); itw.More(); itw.Next())
              totenew++;
          }
        }
        Standard_Integer totvold = 0, totvnew = 0;
        for (TopTools_DataMapIteratorOfDataMapOfShapeShape theR2Iter(theOldVertices);
             theR2Iter.More();
             theR2Iter.Next())
        {
          totvold++;
          if (!theTmpMap.Contains(theR2Iter.Value()))
          {
            theTmpMap.Add(theR2Iter.Value());
            totvnew++;
          }
        }
        std::cout << "TOTAL: " << toteold << " edges, " << totvold << " vertices replaced by "
                  << totenew << " edges, " << totvnew << " vertices" << std::endl
                  << std::endl;
        //-------------------------------
#endif
      }
    }
  }

  return result;
}

//=================================================================================================

void ShapeFix_FaceConnect::Clear()
{
  myConnected.Clear();
  myOriFreeEdges.Clear();
  myResFreeEdges.Clear();
  myResSharEdges.Clear();
}
