// Created on: 1994-08-25
// Created by: Jacques GOUSSARD
// Copyright (c) 1994-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.

// IFV 04.06.99 - PRO18974 - processing of INTERNAL shapes.

#include <BRepTools_Modification.hxx>
#include <BRepTools_Modifier.hxx>
#include <Standard_Transient.hxx>
#include <NCollection_List.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Iterator.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopTools_ShapeMapHasher.hxx>
#include <NCollection_DataMap.hxx>
#include <NCollection_IndexedDataMap.hxx>

#include <Geom2d_Line.hxx>
#include <BRep_Builder.hxx>
#include <BRep_Tool.hxx>
#include <TopoDS.hxx>
#include <BRepTools.hxx>
#include <TopAbs.hxx>
#include <TopExp.hxx>
#include <gp_Pnt.hxx>

#include <gp.hxx>

#include <Standard_NullObject.hxx>
#include <BRepTools_TrsfModification.hxx>
#include <Message_ProgressScope.hxx>
#include <Geom_Surface.hxx>

static void SetShapeFlags(const TopoDS_Shape& theInSh, TopoDS_Shape& theOutSh);

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

BRepTools_Modifier::BRepTools_Modifier(bool theMutableInput)
    : myDone(false),
      myMutableInput(theMutableInput)
{
}

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

BRepTools_Modifier::BRepTools_Modifier(const TopoDS_Shape& S)
    : myShape(S),
      myDone(false),
      myMutableInput(false)
{
  Put(S);
}

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

BRepTools_Modifier::BRepTools_Modifier(const TopoDS_Shape&                        S,
                                       const occ::handle<BRepTools_Modification>& M)
    : myShape(S),
      myDone(false),
      myMutableInput(false)
{
  Put(S);
  Perform(M);
}

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

void BRepTools_Modifier::Init(const TopoDS_Shape& S)
{
  myShape = S;
  myDone  = false;
  Put(S);
}

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

#ifdef DEBUG_Modifier
static NCollection_IndexedMap<TopoDS_Shape, TopTools_ShapeMapHasher> MapE, MapF;
#endif

void BRepTools_Modifier::Perform(const occ::handle<BRepTools_Modification>& M,
                                 const Message_ProgressRange&               theProgress)
{
  if (myShape.IsNull())
  {
    throw Standard_NullObject();
  }
#ifdef DEBUG_Modifier
  MapE.Clear();
  MapF.Clear();
  TopExp::MapShapes(myShape, TopAbs_EDGE, MapE);
  TopExp::MapShapes(myShape, TopAbs_FACE, MapF);
#endif
  NCollection_DataMap<TopoDS_Shape, TopoDS_Shape, TopTools_ShapeMapHasher>::Iterator theIter(myMap);

  Message_ProgressScope aPS(theProgress, "Converting Shape", 2);

  NCollection_IndexedDataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher>
    aMVE, aMEF;
  TopExp::MapShapesAndAncestors(myShape, TopAbs_VERTEX, TopAbs_EDGE, aMVE);
  TopExp::MapShapesAndAncestors(myShape, TopAbs_EDGE, TopAbs_FACE, aMEF);

  CreateNewVertices(aMVE, M);

  FillNewCurveInfo(aMEF, M);

  FillNewSurfaceInfo(M);

  if (!myMutableInput)
  {
    CreateOtherVertices(aMVE, aMEF, M);
  }

  bool aNewGeom;
  Rebuild(myShape, M, aNewGeom, aPS.Next());

  if (!aPS.More())
  {
    // The processing was broken
    return;
  }

  if (myShape.ShapeType() == TopAbs_FACE)
  {
    if (myShape.Orientation() == TopAbs_REVERSED)
    {
      myMap(myShape).Reverse();
    }
    else
    {
      myMap(myShape).Orientation(myShape.Orientation());
    }
  }
  else
  {
    myMap(myShape).Orientation(myShape.Orientation());
  }

  // Update the continuities

  BRep_Builder aBB;

  /*
    bool RecomputeTriangles = false;
    double MaxDeflection = RealFirst();
    occ::handle<Poly_Triangulation> Tr;
    occ::handle<Poly_Polygon3D> Po;
    TopLoc_Location Loc;
  */

  for (int ii = 1; ii <= aMEF.Extent(); ii++)
  {
    const TopoDS_Edge& CurE = TopoDS::Edge(aMEF.FindKey(ii));
    const TopoDS_Edge& NewE = TopoDS::Edge(myMap(CurE));
    if (!CurE.IsSame(NewE))
    {
      NCollection_List<TopoDS_Shape>::Iterator it;
      it.Initialize(aMEF.FindFromKey(CurE));
      TopoDS_Face F1, F2;
      while (it.More() && F2.IsNull())
      {
        if (F1.IsNull())
        {
          F1 = TopoDS::Face(it.Value());
        }
        else
        {
          F2 = TopoDS::Face(it.Value());
        }
        it.Next();
      }
      if (!F2.IsNull())
      {
        const TopoDS_Face& newf1   = TopoDS::Face(myMap(F1));
        const TopoDS_Face& newf2   = TopoDS::Face(myMap(F2));
        GeomAbs_Shape      Newcont = M->Continuity(CurE, F1, F2, NewE, newf1, newf2);
        if (Newcont > GeomAbs_C0)
        {
          aBB.Continuity(NewE, newf1, newf2, Newcont);
        }
      }
    }
    theIter.Next();
  }
  /*
    if (RecomputeTriangles) {
      BRepMesh_IncrementalMesh(myMap(myShape),MaxDeflection);
    }
  */

  myDone = true;
}

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

void BRepTools_Modifier::Put(const TopoDS_Shape& S)
{
  if (!myMap.IsBound(S))
  {
    myMap.Bind(S, TopoDS_Shape());
    for (TopoDS_Iterator theIterator(S, false); theIterator.More(); theIterator.Next())
    {

      Put(theIterator.Value());
    }
  }
}

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

bool BRepTools_Modifier::Rebuild(const TopoDS_Shape&                        S,
                                 const occ::handle<BRepTools_Modification>& M,
                                 bool&                                      theNewGeom,
                                 const Message_ProgressRange&               theProgress)
{
#ifdef DEBUG_Modifier
  int iF = MapF.Contains(S) ? MapF.FindIndex(S) : 0;
  int iE = MapE.Contains(S) ? MapE.FindIndex(S) : 0;
#endif
  TopAbs_ShapeEnum ts     = S.ShapeType();
  TopoDS_Shape&    result = myMap(S);
  if (!result.IsNull())
  {
    theNewGeom = myHasNewGeom.Contains(S);
    return !S.IsSame(result);
  }
  bool               rebuild = false, RevWires = false;
  TopAbs_Orientation ResOr = TopAbs_FORWARD;
  BRep_Builder       B;
  double             tol;
  bool               No3DCurve = false; // in fact, if we have no
  // 3D geometry modification, it is necessary to test the existence of a 3D curve.

  // new geometry ?

  switch (ts)
  {
    case TopAbs_FACE: {
      rebuild = myNSInfo.IsBound(TopoDS::Face(S));
      if (rebuild)
      {
        const NewSurfaceInfo& aNSinfo = myNSInfo(TopoDS::Face(S));
        RevWires                      = aNSinfo.myRevWires;
        B.MakeFace(TopoDS::Face(result),
                   aNSinfo.mySurface,
                   aNSinfo.myLoc.Predivided(S.Location()),
                   aNSinfo.myToler);
        result.Location(S.Location(), false);
        if (aNSinfo.myRevFace)
        {
          ResOr = TopAbs_REVERSED;
        }
        // set specifics flags of a Face
        B.NaturalRestriction(TopoDS::Face(result), BRep_Tool::NaturalRestriction(TopoDS::Face(S)));
      }

      // update triangulation on the copied face
      occ::handle<Poly_Triangulation> aTriangulation;
      if (M->NewTriangulation(TopoDS::Face(S), aTriangulation))
      {
        if (rebuild)
        { // the copied face already exists => update it
          B.UpdateFace(TopoDS::Face(result), aTriangulation);
        }
        else
        { // create new face with bare triangulation
          B.MakeFace(TopoDS::Face(result), aTriangulation);
          result.Location(S.Location(), false);
        }
        rebuild = true;
      }
    }
    break;

    case TopAbs_EDGE: {
      rebuild = myNCInfo.IsBound(TopoDS::Edge(S));
      if (rebuild)
      {
        const NewCurveInfo& aNCinfo = myNCInfo(TopoDS::Edge(S));
        if (aNCinfo.myCurve.IsNull())
        {
          B.MakeEdge(TopoDS::Edge(result));
          B.Degenerated(TopoDS::Edge(result), BRep_Tool::Degenerated(TopoDS::Edge(S)));
          B.UpdateEdge(TopoDS::Edge(result), aNCinfo.myToler); // OCC217
          No3DCurve = true;
        }
        else
        {
          B.MakeEdge(TopoDS::Edge(result),
                     aNCinfo.myCurve,
                     aNCinfo.myLoc.Predivided(S.Location()),
                     aNCinfo.myToler);
          No3DCurve = false;
        }
        result.Location(S.Location(), false);
        //	result.Orientation(S.Orientation());

        // set specifics flags of an Edge
        B.SameParameter(TopoDS::Edge(result), BRep_Tool::SameParameter(TopoDS::Edge(S)));
        B.SameRange(TopoDS::Edge(result), BRep_Tool::SameRange(TopoDS::Edge(S)));
      }

      // update polygonal structure on the edge
      occ::handle<Poly_Polygon3D> aPolygon;
      if (M->NewPolygon(TopoDS::Edge(S), aPolygon))
      {
        if (rebuild)
        { // the copied edge already exists => update it
          B.UpdateEdge(TopoDS::Edge(result), aPolygon, S.Location());
        }
        else
        { // create new edge with bare polygon
          B.MakeEdge(TopoDS::Edge(result), aPolygon);
          result.Location(S.Location(), false);
        }
        rebuild = true;
      }
    }
    break;
    default:;
  }

  // rebuild sub-shapes and test new sub-shape ?

  bool newgeom = rebuild;
  theNewGeom   = rebuild;

  TopoDS_Iterator it;

  {
    int aShapeCount = 0;
    {
      for (it.Initialize(S, false); it.More(); it.Next())
      {
        ++aShapeCount;
      }
    }

    Message_ProgressScope aPS(theProgress, "Converting SubShapes", aShapeCount);
    //
    for (it.Initialize(S, false); it.More() && aPS.More(); it.Next())
    {
      // always call Rebuild
      bool isSubNewGeom = false;
      bool subrebuilt   = Rebuild(it.Value(), M, isSubNewGeom, aPS.Next());
      rebuild           = subrebuilt || rebuild;
      theNewGeom        = theNewGeom || isSubNewGeom;
    }
    if (!aPS.More())
    {
      // The processing was broken
      return false;
    }
  }
  if (theNewGeom)
  {
    myHasNewGeom.Add(S);
  }

  // make an empty copy
  if (rebuild && !newgeom)
  {
    result = S.EmptyCopied();
    result.Orientation(TopAbs_FORWARD);
  }

  // copy the sub-elements

  if (rebuild)
  {
    TopAbs_Orientation orient;
    for (it.Initialize(S, false); it.More(); it.Next())
    {
      orient = it.Value().Orientation();
      if (RevWires || myMap(it.Value()).Orientation() == TopAbs_REVERSED)
      {
        orient = TopAbs::Reverse(orient);
      }
      B.Add(result, myMap(it.Value()).Oriented(orient));
    }

    if (ts == TopAbs_FACE)
    {
      // pcurves
      occ::handle<Geom2d_Curve> curve2d; //,curve2d1;
      TopoDS_Face               face = TopoDS::Face(S);
      TopAbs_Orientation        fcor = face.Orientation();
      if (fcor != TopAbs_REVERSED)
      {
        fcor = TopAbs_FORWARD;
      }

      TopExp_Explorer ex(face.Oriented(fcor), TopAbs_EDGE);
      for (; ex.More(); ex.Next())
      {
        const TopoDS_Edge& edge = TopoDS::Edge(ex.Current());

#ifdef DEBUG_Modifier
        iE = MapE.Contains(edge) ? MapE.FindIndex(edge) : 0;
#endif
        if (theNewGeom
            && M->NewCurve2d(edge,
                             face,
                             TopoDS::Edge(myMap(ex.Current())),
                             TopoDS::Face(result),
                             curve2d,
                             tol))
        {
          // rem dub 16/09/97 : Make constant topology or not make at all.
          // Do not make if CopySurface = 1
          // Attention, TRUE sewing edges (ReallyClosed)
          // stay even if  CopySurface is true.

          // check that edge contains two pcurves on this surface:
          // either it is true seam on the current face, or belongs to two faces
          // built on that same surface (see OCC21772)
          // Note: this check could be made separate method in BRepTools
          bool isClosed = false;
          if (BRep_Tool::IsClosed(edge, face))
          {
            isClosed = (!newgeom || BRepTools::IsReallyClosed(edge, face));
            if (!isClosed)
            {
              TopLoc_Location aLoc;
              TopoDS_Shape    resface = (myMap.IsBound(face) ? myMap(face) : face);
              if (resface.IsNull())
              {
                resface = face;
              }
              occ::handle<Geom_Surface> aSurf = BRep_Tool::Surface(TopoDS::Face(resface), aLoc);
              // check other faces sharing the same surface
              TopExp_Explorer aExpF(myShape, TopAbs_FACE);
              for (; aExpF.More() && !isClosed; aExpF.Next())
              {
                TopoDS_Face anOther = TopoDS::Face(aExpF.Current());
                if (anOther.IsSame(face))
                {
                  continue;
                }
                TopoDS_Shape resface2 = (myMap.IsBound(anOther) ? myMap(anOther) : anOther);
                if (resface2.IsNull())
                {
                  resface2 = anOther;
                }
                TopLoc_Location           anOtherLoc;
                occ::handle<Geom_Surface> anOtherSurf =
                  BRep_Tool::Surface(TopoDS::Face(resface2), anOtherLoc);
                if (aSurf == anOtherSurf && aLoc.IsEqual(anOtherLoc))
                {
                  TopExp_Explorer aExpE(anOther, TopAbs_EDGE);
                  for (; aExpE.More() && !isClosed; aExpE.Next())
                  {
                    isClosed = edge.IsSame(aExpE.Current());
                  }
                }
              }
            }
          }
          if (isClosed)
          {
            TopoDS_Edge  CurE         = TopoDS::Edge(myMap(edge));
            TopoDS_Shape aLocalResult = result;
            aLocalResult.Orientation(TopAbs_FORWARD);
            TopoDS_Face               CurF = TopoDS::Face(aLocalResult);
            occ::handle<Geom2d_Curve> curve2d1, currcurv;
            double                    f, l;
            if ((!RevWires && fcor != edge.Orientation())
                || (RevWires && fcor == edge.Orientation()))
            {
              CurE.Orientation(TopAbs_FORWARD);
              curve2d1 = BRep_Tool::CurveOnSurface(CurE, CurF, f, l);
              if (curve2d1.IsNull())
              {
                curve2d1 = new Geom2d_Line(gp::OX2d());
              }
              B.UpdateEdge(CurE, curve2d1, curve2d, CurF, 0.);
            }
            else
            {
              CurE.Orientation(TopAbs_REVERSED);
              curve2d1 = BRep_Tool::CurveOnSurface(CurE, CurF, f, l);
              if (curve2d1.IsNull())
              {
                curve2d1 = new Geom2d_Line(gp::OX2d());
              }
              B.UpdateEdge(CurE, curve2d, curve2d1, CurF, 0.);
            }
            currcurv = BRep_Tool::CurveOnSurface(CurE, CurF, f, l);
            B.Range(CurE, f, l);
          }
          else
          {
            B.UpdateEdge(TopoDS::Edge(myMap(ex.Current())), curve2d, TopoDS::Face(result), 0.);
          }

          TopLoc_Location         theLoc;
          double                  theF, theL;
          occ::handle<Geom_Curve> C3D =
            BRep_Tool::Curve(TopoDS::Edge(myMap(ex.Current())), theLoc, theF, theL);
          if (C3D.IsNull())
          { // Update vertices
            double          param;
            TopExp_Explorer ex2(edge, TopAbs_VERTEX);
            while (ex2.More())
            {
              const TopoDS_Vertex& vertex = TopoDS::Vertex(ex2.Current());
              if (!M->NewParameter(vertex, edge, param, tol))
              {
                tol   = BRep_Tool::Tolerance(vertex);
                param = BRep_Tool::Parameter(vertex, edge);
              }

              TopAbs_Orientation vtxrelat = vertex.Orientation();
              if (edge.Orientation() == TopAbs_REVERSED)
              {
                // Update considers the edge as FORWARD, and the vertex relatively
                vtxrelat = TopAbs::Reverse(vtxrelat);
              }
              // if (myMap(edge).Orientation() == TopAbs_REVERSED) {
              //   vtxrelat= TopAbs::Reverse(vtxrelat);
              // }

              TopoDS_Vertex aLocalVertex = TopoDS::Vertex(myMap(vertex));
              aLocalVertex.Orientation(vtxrelat);
              // B.UpdateVertex(TopoDS::Vertex
              //(myMap(vertex).Oriented(vtxrelat)),
              B.UpdateVertex(aLocalVertex, param, TopoDS::Edge(myMap(edge)), tol);
              ex2.Next();
            }
          }
        }

        // Copy polygon on triangulation
        occ::handle<Poly_PolygonOnTriangulation> aPolyOnTria_1, aPolyOnTria_2;
        bool aNewPonT = M->NewPolygonOnTriangulation(edge, face, aPolyOnTria_1);
        if (BRepTools::IsReallyClosed(edge, face))
        {
          // Obtain triangulation on reversed edge
          TopoDS_Edge anEdgeRev = edge;
          anEdgeRev.Reverse();
          aNewPonT = M->NewPolygonOnTriangulation(anEdgeRev, face, aPolyOnTria_2) || aNewPonT;
          // It there is only one polygon on triangulation, store it to aPolyOnTria_1
          if (aPolyOnTria_1.IsNull() && !aPolyOnTria_2.IsNull())
          {
            aPolyOnTria_1 = aPolyOnTria_2;
            aPolyOnTria_2 = occ::handle<Poly_PolygonOnTriangulation>();
          }
        }
        if (aNewPonT)
        {
          TopLoc_Location                 aLocation;
          occ::handle<Poly_Triangulation> aNewFaceTria =
            BRep_Tool::Triangulation(TopoDS::Face(myMap(face)), aLocation);
          TopoDS_Edge aNewEdge = TopoDS::Edge(myMap(edge));
          if (aPolyOnTria_2.IsNull())
          {
            B.UpdateEdge(aNewEdge, aPolyOnTria_1, aNewFaceTria, aLocation);
          }
          else
          {
            if (edge.Orientation() == TopAbs_FORWARD)
            {
              B.UpdateEdge(aNewEdge, aPolyOnTria_1, aPolyOnTria_2, aNewFaceTria, aLocation);
            }
            else
            {
              B.UpdateEdge(aNewEdge, aPolyOnTria_2, aPolyOnTria_1, aNewFaceTria, aLocation);
            }
          }
        }
      }
    }

    //    else if (ts == TopAbs_EDGE) {
    else if (ts == TopAbs_EDGE && !No3DCurve)
    {
      // Vertices
      double             param;
      const TopoDS_Edge& edge = TopoDS::Edge(S);
      TopAbs_Orientation edor = edge.Orientation();
      if (edor != TopAbs_REVERSED)
      {
        edor = TopAbs_FORWARD;
      }
      TopExp_Explorer ex(edge.Oriented(edor), TopAbs_VERTEX);
      while (ex.More())
      {
        const TopoDS_Vertex& vertex = TopoDS::Vertex(ex.Current());

        if (!M->NewParameter(vertex, edge, param, tol))
        {
          tol   = BRep_Tool::Tolerance(vertex);
          param = BRep_Tool::Parameter(vertex, edge);
        }

        TopAbs_Orientation vtxrelat = vertex.Orientation();
        if (edor == TopAbs_REVERSED)
        {
          // Update considers the edge as FORWARD, and the vertex relatively
          vtxrelat = TopAbs::Reverse(vtxrelat);
        }

        // if (result.Orientation() == TopAbs_REVERSED) {
        //   vtxrelat= TopAbs::Reverse(vtxrelat);
        // }
        TopoDS_Vertex aLocalVertex = TopoDS::Vertex(myMap(vertex));
        aLocalVertex.Orientation(vtxrelat);
        // B.UpdateVertex(TopoDS::Vertex(myMap(vertex).Oriented(vtxrelat)),
        if (myMutableInput || !aLocalVertex.IsSame(vertex))
        {
          B.UpdateVertex(aLocalVertex, param, TopoDS::Edge(result), tol);
        }
        ex.Next();
      }
    }

    // update flags

    result.Orientable(S.Orientable());
    result.Closed(S.Closed());
    result.Infinite(S.Infinite());
  }
  else
  {
    result = S;
  }

  // Set flag of the shape.
  result.Orientation(ResOr);

  SetShapeFlags(S, result);

  return rebuild;
}

void BRepTools_Modifier::CreateNewVertices(
  const NCollection_IndexedDataMap<TopoDS_Shape,
                                   NCollection_List<TopoDS_Shape>,
                                   TopTools_ShapeMapHasher>& theMVE,
  const occ::handle<BRepTools_Modification>&                 M)
{
  double       aToler;
  BRep_Builder aBB;
  gp_Pnt       aPnt;
  for (int i = 1; i <= theMVE.Extent(); i++)
  {
    // fill MyMap only with vertices with NewPoint == true
    const TopoDS_Vertex& aV     = TopoDS::Vertex(theMVE.FindKey(i));
    bool                 IsNewP = M->NewPoint(aV, aPnt, aToler);
    if (IsNewP)
    {
      TopoDS_Vertex aNewV;
      aBB.MakeVertex(aNewV, aPnt, aToler);
      SetShapeFlags(aV, aNewV);
      myMap(aV) = aNewV;
      myHasNewGeom.Add(aV);
    }
    else if (myMutableInput)
    {
      myMap(aV) = aV.Oriented(TopAbs_FORWARD);
    }
  }
}

void BRepTools_Modifier::FillNewCurveInfo(
  const NCollection_IndexedDataMap<TopoDS_Shape,
                                   NCollection_List<TopoDS_Shape>,
                                   TopTools_ShapeMapHasher>& theMEF,
  const occ::handle<BRepTools_Modification>&                 M)
{
  occ::handle<Geom_Curve>          aCurve;
  TopLoc_Location                  aLocation;
  BRepTools_Modifier::NewCurveInfo aNCinfo;
  double                           aToler;
  for (int i = 1; i <= theMEF.Extent(); i++)
  {
    const TopoDS_Edge& anE      = TopoDS::Edge(theMEF.FindKey(i));
    bool               IsNewCur = M->NewCurve(anE, aCurve, aLocation, aToler);
    if (IsNewCur)
    {
      aNCinfo.myCurve = aCurve;
      aNCinfo.myLoc   = aLocation;
      aNCinfo.myToler = aToler;
      myNCInfo.Bind(anE, aNCinfo);
      myHasNewGeom.Add(anE);
    }
  }
}

void BRepTools_Modifier::FillNewSurfaceInfo(const occ::handle<BRepTools_Modification>& M)
{
  NCollection_IndexedMap<TopoDS_Shape, TopTools_ShapeMapHasher> aMF;
  TopExp::MapShapes(myShape, TopAbs_FACE, aMF);
  BRepTools_Modifier::NewSurfaceInfo aNSinfo;
  for (int i = 1; i <= aMF.Extent(); i++)
  {
    const TopoDS_Face&        aF = TopoDS::Face(aMF(i));
    bool                      RevFace;
    bool                      RevWires;
    occ::handle<Geom_Surface> aSurface;
    TopLoc_Location           aLocation;
    double                    aToler1;
    bool IsNewSur = M->NewSurface(aF, aSurface, aLocation, aToler1, RevWires, RevFace);
    if (IsNewSur)
    {
      aNSinfo.mySurface  = aSurface;
      aNSinfo.myLoc      = aLocation;
      aNSinfo.myToler    = aToler1;
      aNSinfo.myRevWires = RevWires;
      aNSinfo.myRevFace  = RevFace;
      myNSInfo.Bind(aF, aNSinfo);
      myHasNewGeom.Add(aF);
    }
    else
    {
      // check if subshapes will be modified
      bool            notRebuilded = true;
      TopExp_Explorer exE(aF, TopAbs_EDGE);
      while (exE.More() && notRebuilded)
      {
        const TopoDS_Edge& anEE = TopoDS::Edge(exE.Current());
        if (myNCInfo.IsBound(anEE))
        {
          notRebuilded = false;
          break;
        }
        TopExp_Explorer exV(anEE, TopAbs_VERTEX);
        while (exV.More() && notRebuilded)
        {
          const TopoDS_Vertex& aVV = TopoDS::Vertex(exV.Current());
          if (!myMap(aVV).IsNull())
          {
            notRebuilded = false;
            break;
          }
          exV.Next();
        }
        exE.Next();
      }
      if (notRebuilded)
      {
        // subshapes is not going to be modified
        myNonUpdFace.Add(aF);
      }
    }
  }
}

void BRepTools_Modifier::CreateOtherVertices(
  const NCollection_IndexedDataMap<TopoDS_Shape,
                                   NCollection_List<TopoDS_Shape>,
                                   TopTools_ShapeMapHasher>& theMVE,
  const NCollection_IndexedDataMap<TopoDS_Shape,
                                   NCollection_List<TopoDS_Shape>,
                                   TopTools_ShapeMapHasher>& theMEF,
  const occ::handle<BRepTools_Modification>&                 M)
{
  double aToler;
  // The following logic in some ways repeats the logic from the Rebuild() method.
  // If the face with its subshapes is not going to be modified
  //(i.e. NewSurface() for this face and NewCurve(), NewPoint() for its edges/vertices returns
  // false) then the calling of NewCurve2d() for this face with its edges is not performed.
  // Therefore, the updating of vertices will not present in such cases and
  // the EmptyCopied() operation for vertices from this face is not needed.

  for (int i = 1; i <= theMVE.Extent(); i++)
  {
    const TopoDS_Vertex& aV    = TopoDS::Vertex(theMVE.FindKey(i));
    TopoDS_Vertex        aNewV = TopoDS::Vertex(myMap(aV));
    if (aNewV.IsNull())
    {
      const NCollection_List<TopoDS_Shape>&    aLEdges   = theMVE(i);
      bool                                     toReplace = false;
      NCollection_List<TopoDS_Shape>::Iterator it(aLEdges);
      for (; it.More() && !toReplace; it.Next())
      {
        const TopoDS_Edge& anE = TopoDS::Edge(it.Value());
        if (myNCInfo.IsBound(anE) && !myNCInfo(anE).myCurve.IsNull())
        {
          toReplace = true;
        }

        if (!toReplace)
        {
          const NCollection_List<TopoDS_Shape>&    aLFaces = theMEF.FindFromKey(anE);
          NCollection_List<TopoDS_Shape>::Iterator it2(aLFaces);
          for (; it2.More(); it2.Next())
          {
            const TopoDS_Face& aF = TopoDS::Face(it2.Value());
            if (!myNonUpdFace.Contains(aF))
            {
              occ::handle<Geom2d_Curve> aCurve2d;
              // some NewCurve2d()s may use NewE arg internally, so the
              // null TShape as an arg may lead to the exceptions
              TopoDS_Edge aDummyE = TopoDS::Edge(anE.EmptyCopied());
              if (M->NewCurve2d(anE, aF, aDummyE, TopoDS_Face(), aCurve2d, aToler))
              {
                toReplace = true;
                break;
              }
            }
          }
        }
      }
      if (toReplace)
      {
        aNewV = TopoDS::Vertex(aV.EmptyCopied());
      }
      else
      {
        aNewV = aV;
      }
      aNewV.Orientation(TopAbs_FORWARD);
      myMap(aV) = aNewV;
    }
  }
}

static void SetShapeFlags(const TopoDS_Shape& theInSh, TopoDS_Shape& theOutSh)
{
  theOutSh.Modified(theInSh.Modified());
  theOutSh.Checked(theInSh.Checked());
  theOutSh.Orientable(theInSh.Orientable());
  theOutSh.Closed(theInSh.Closed());
  theOutSh.Infinite(theInSh.Infinite());
  theOutSh.Convex(theInSh.Convex());
}

bool BRepTools_Modifier::IsMutableInput() const
{
  return myMutableInput;
}

void BRepTools_Modifier::SetMutableInput(bool theMutableInput)
{
  myMutableInput = theMutableInput;
}
