// Created on: 1999-04-15
// Created by: Roman LYGIN
// Copyright (c) 1999-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.

//    gka 23.06.99 S4208: using tool SU_TransferParameter
//    pdn 13.07.99 synchronizing splitting values on 3d curve and pcurve
//    abv 14.07.99 dealing with edges without 3d curve
//    svv 10.01.00 porting on DEC

#include <Adaptor3d_CurveOnSurface.hxx>
#include <BRep_Builder.hxx>
#include <BRep_Tool.hxx>
#include <BRepLib_MakeFace.hxx>
#include <BRepLib_MakeWire.hxx>
#include <Geom2d_Curve.hxx>
#include <Geom2dAdaptor_Curve.hxx>
#include <Geom_Curve.hxx>
#include <Geom_Surface.hxx>
#include <GeomAdaptor_Surface.hxx>
#include <gp_Pnt.hxx>
#include <Precision.hxx>
#include <ShapeAnalysis_Curve.hxx>
#include <ShapeAnalysis_Edge.hxx>
#include <ShapeAnalysis_TransferParametersProj.hxx>
#include <ShapeBuild_Edge.hxx>
#include <ShapeBuild_ReShape.hxx>
#include <ShapeExtend.hxx>
#include <ShapeUpgrade.hxx>
#include <ShapeUpgrade_EdgeDivide.hxx>
#include <ShapeUpgrade_FixSmallCurves.hxx>
#include <ShapeUpgrade_SplitCurve2d.hxx>
#include <ShapeUpgrade_SplitCurve3d.hxx>
#include <ShapeUpgrade_WireDivide.hxx>
#include <Standard_Type.hxx>
#include <TColGeom2d_HArray1OfCurve.hxx>
#include <TColGeom_HArray1OfCurve.hxx>
#include <TColStd_Array1OfBoolean.hxx>
#include <TColStd_HSequenceOfReal.hxx>
#include <TColStd_SequenceOfReal.hxx>
#include <TopExp.hxx>
#include <TopExp_Explorer.hxx>
#include <TopLoc_Location.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Iterator.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Wire.hxx>
#include <TopTools_SequenceOfShape.hxx>

IMPLEMENT_STANDARD_RTTIEXT(ShapeUpgrade_WireDivide, ShapeUpgrade_Tool)

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

ShapeUpgrade_WireDivide::ShapeUpgrade_WireDivide()
    : ShapeUpgrade_Tool(),
      myStatus(0)
{
  //  if (ShapeUpgrade::Debug()) std::cout <<"ShapeUpgrade_WireDivide"<<std::endl;
  mySplitCurve3dTool  = new ShapeUpgrade_SplitCurve3d;
  mySplitCurve2dTool  = new ShapeUpgrade_SplitCurve2d;
  myTransferParamTool = new ShapeAnalysis_TransferParametersProj;
  myEdgeMode          = 2;
  myFixSmallCurveTool = new ShapeUpgrade_FixSmallCurves;
  myEdgeDivide        = new ShapeUpgrade_EdgeDivide;
}

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

void ShapeUpgrade_WireDivide::Init(const TopoDS_Wire& W, const TopoDS_Face& F)
{
  //  if (ShapeUpgrade::Debug()) std::cout <<"ShapeUpgrade_WireDivide::Init with Wire,
  //  Face"<<std::endl;
  myWire   = W;
  myFace   = F;
  myStatus = ShapeExtend::EncodeStatus(ShapeExtend_OK);
}

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

void ShapeUpgrade_WireDivide::Init(const TopoDS_Wire& W, const Handle(Geom_Surface)& S)
{
  //  if (ShapeUpgrade::Debug()) std::cout <<"ShapeUpgrade_WireDivide::Init with Wire, Surface
  //  "<<std::endl;
  myWire = W;
  BRepLib_MakeFace mkf(S, Precision::Confusion());
  myFace   = mkf.Face();
  myStatus = ShapeExtend::EncodeStatus(ShapeExtend_OK);
}

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

void ShapeUpgrade_WireDivide::Load(const TopoDS_Wire& W)
{
  myWire = W;
}

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

void ShapeUpgrade_WireDivide::Load(const TopoDS_Edge& E)
{
  BRepLib_MakeWire MakeWire(E);
  if (MakeWire.IsDone())
    Load(MakeWire.Wire());
}

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

void ShapeUpgrade_WireDivide::SetFace(const TopoDS_Face& F)
{
  myFace = F;
}

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

void ShapeUpgrade_WireDivide::SetSurface(const Handle(Geom_Surface)& S)
{
  BRepLib_MakeFace mkf(S, Precision::Confusion());
  myFace = mkf.Face();
}

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

void ShapeUpgrade_WireDivide::SetSurface(const Handle(Geom_Surface)& S, const TopLoc_Location& L)
{
  BRep_Builder B;
  B.MakeFace(myFace, S, L, Precision::Confusion());
}

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

static void CorrectSplitValues(const Handle(TColStd_HSequenceOfReal)& orig3d,
                               const Handle(TColStd_HSequenceOfReal)& orig2d,
                               Handle(TColStd_HSequenceOfReal)        new2d,
                               Handle(TColStd_HSequenceOfReal)        new3d)
{
  constexpr Standard_Real preci = Precision::PConfusion();
  Standard_Integer        len3d = orig3d->Length();
  Standard_Integer        len2d = orig2d->Length();
  TColStd_Array1OfBoolean fixNew2d(1, len3d);
  fixNew2d.Init(Standard_False);
  TColStd_Array1OfBoolean fixNew3d(1, len2d);
  fixNew3d.Init(Standard_False);
  Standard_Real Last3d = orig3d->Value(len3d);
  Standard_Real Last2d = orig2d->Value(len2d);

  Standard_Integer i; // svv #1
  for (i = 1; i <= len3d; i++)
  {
    Standard_Real    par   = new2d->Value(i);
    Standard_Integer index = 0;
    for (Standard_Integer j = 1; j <= len2d && !index; j++)
      if (Abs(par - orig2d->Value(j)) < preci)
        index = j;
    if (index && !fixNew3d(index))
    {
      Standard_Real newPar = orig2d->Value(index);
      new2d->SetValue(i, newPar);
      fixNew2d(i)            = Standard_True;
      Standard_Real newPar3d = orig3d->Value(i);
      new3d->SetValue(index, newPar3d);
      fixNew3d(index) = Standard_True;
    }
  }

  for (i = 1; i <= len2d; i++)
  {
    Standard_Real    par   = new3d->Value(i);
    Standard_Integer index = 0;
    for (Standard_Integer j = 1; j <= len3d && !index; j++)
      if (Abs(par - orig3d->Value(j)) < preci)
        index = j;
    if (index && !fixNew2d(index))
    {
      Standard_Real newPar = orig3d->Value(index);
      new3d->SetValue(i, newPar);
      fixNew3d(i)            = Standard_True;
      Standard_Real newPar2d = orig2d->Value(i);
      new2d->SetValue(index, newPar2d);
      fixNew2d(index) = Standard_True;
    }
  }

  Standard_Real dpreci = 2 * preci;
  for (i = 1; i < len3d; i++)
  {
    Standard_Real dist = new2d->Value(i + 1) - new2d->Value(i);
    if (dist < preci)
    {
      if (fixNew2d(i + 1))
      {
        // changing
        Standard_Real tmp = new2d->Value(i + 1);
        new2d->SetValue(i + 1, new2d->Value(i) + dpreci);
        new2d->SetValue(i, tmp);
        fixNew2d(i)     = Standard_True;
        fixNew2d(i + 1) = Standard_False;
      }
      else
        new2d->SetValue(i + 1, new2d->Value(i) + dpreci);
    }
  }
  if (new2d->Value(len3d) > Last3d)
  {
    Standard_Integer ind; // svv #1
    for (ind = len3d; ind > 1 && !fixNew2d(ind); ind--)
      ;
    Standard_Real lastFix = new2d->Value(ind);
    for (i = len3d; i >= ind; i--)
    {
      new2d->SetValue(i, lastFix);
      lastFix -= dpreci;
    }
  }

  for (i = 1; i < len2d; i++)
  {
    Standard_Real dist = new3d->Value(i + 1) - new3d->Value(i);
    if (dist < preci)
    {
      if (fixNew3d(i + 1))
      {
        // changing
        Standard_Real tmp = new3d->Value(i + 1);
        new3d->SetValue(i + 1, new3d->Value(i) + dpreci);
        new3d->SetValue(i, tmp);
        fixNew3d(i)     = Standard_True;
        fixNew3d(i + 1) = Standard_False;
      }
      else
        new3d->SetValue(i + 1, new3d->Value(i) + dpreci);
    }
  }
  if (new3d->Value(len2d) > Last2d)
  {
    Standard_Integer ind; // svv #1
    for (ind = len2d; ind > 1 && !fixNew3d(ind); ind--)
      ;
    Standard_Real lastFix = new3d->Value(ind);
    for (i = len2d; i >= ind; i--)
    {
      new3d->SetValue(i, lastFix);
      lastFix -= dpreci;
    }
  }
}

void ShapeUpgrade_WireDivide::Perform()
{

  myStatus = ShapeExtend::EncodeStatus(ShapeExtend_OK);

  //  if (ShapeUpgrade::Debug()) std::cout << "ShapeUpgrade_WireDivide::Perform" << std::endl;

  BRep_Builder       B;
  ShapeAnalysis_Edge sae;

  TopoDS_Wire newWire;
  B.MakeWire(newWire);
  TopLoc_Location      Loc;
  Handle(Geom_Surface) Surf;
  if (!myFace.IsNull())
    Surf = BRep_Tool::Surface(myFace, Loc);

  Standard_Boolean isSplit3d = Standard_True;
  switch (myEdgeMode)
  {
    case 0:
      if (!myFace.IsNull())
        isSplit3d = Standard_False;
      break;
    case 1:
      if (myFace.IsNull())
        isSplit3d = Standard_False;
      break;
    default:
      break;
  }
  myEdgeDivide->SetFace(myFace);
  if (isSplit3d)
    myEdgeDivide->SetSplitCurve3dTool(GetSplitCurve3dTool());
  myEdgeDivide->SetSplitCurve2dTool(GetSplitCurve2dTool());
  for (TopoDS_Iterator ItW(myWire, Standard_False); ItW.More(); ItW.Next())
  {
    // for each Edge:
    TopoDS_Shape sh = Context()->Apply(ItW.Value(), TopAbs_SHAPE);
    for (TopExp_Explorer exp(sh, TopAbs_EDGE); exp.More(); exp.Next())
    {
      TopoDS_Edge E = TopoDS::Edge(exp.Current());
      //      if (ShapeUpgrade::Debug()) std::cout << ".. Edge " << (void*) &(*E.TShape()) <<
      //      std::endl;

      // skip degenerated edges (and also INTERNAL/EXTERNAL, to avoid failures)
      if (E.Orientation() == TopAbs_INTERNAL || E.Orientation() == TopAbs_EXTERNAL)
      {
        B.Add(newWire, E);
        continue;
      }

      if (!myEdgeDivide->Compute(E))
      {
        B.Add(newWire, E);
        continue;
      }
      // first iteration: getting split knots
      // on 3D curve: preliminary

      Handle(ShapeAnalysis_TransferParameters) theTransferParamTool = GetTransferParamTool();
      theTransferParamTool->SetMaxTolerance(MaxTolerance());
      theTransferParamTool->Init(E, myFace);
      Standard_Boolean wasSR = theTransferParamTool->IsSameRange();

      // on pcurve(s): all knots
      // assume that if seam-edge, its pcurve1 and pcurve2 has the same split knots !!!
      Handle(TColStd_HSequenceOfReal) theKnots3d = myEdgeDivide->Knots3d();
      Handle(TColStd_HSequenceOfReal) theKnots2d = myEdgeDivide->Knots2d();

      // second iteration: transfer parameters and build segments
      Handle(TColStd_HSequenceOfReal) SplitValues2d;
      Handle(TColStd_HSequenceOfReal) SplitValues3d;
      if (myEdgeDivide->HasCurve2d() && myEdgeDivide->HasCurve3d())
      {
        SplitValues2d = theTransferParamTool->Perform(theKnots3d, Standard_True);
        SplitValues3d = theTransferParamTool->Perform(theKnots2d, Standard_False);
        CorrectSplitValues(theKnots3d, theKnots2d, SplitValues2d, SplitValues3d);
      }
      Handle(ShapeUpgrade_SplitCurve3d) theSplit3dTool = myEdgeDivide->GetSplitCurve3dTool();
      Handle(ShapeUpgrade_SplitCurve2d) theSplit2dTool = myEdgeDivide->GetSplitCurve2dTool();

      if (myEdgeDivide->HasCurve2d())
      {
        if (!theKnots3d.IsNull())
        {
          SplitValues2d->Remove(1);
          SplitValues2d->Remove(SplitValues2d->Length());
          theSplit2dTool->SetSplitValues(SplitValues2d);
        }
        theSplit2dTool->Build(Standard_True);
      }
      if (myEdgeDivide->HasCurve3d())
      {
        if (!theKnots2d.IsNull())
        {
          SplitValues3d->Remove(1);
          SplitValues3d->Remove(SplitValues3d->Length());
          theSplit3dTool->SetSplitValues(SplitValues3d);
        }
        theSplit3dTool->Build(Standard_True);
      }
      // get 2d and 3d split values which should be the same
      if (myEdgeDivide->HasCurve2d())
        theKnots2d = theSplit2dTool->SplitValues();
      if (myEdgeDivide->HasCurve3d())
        theKnots3d = theSplit3dTool->SplitValues();

      Standard_Boolean isSeam = Standard_False;
      if (!myFace.IsNull())
        isSeam = BRep_Tool::IsClosed(E, myFace);
      Handle(TColGeom2d_HArray1OfCurve) theSegments2d;
      if (myEdgeDivide->HasCurve2d())
        theSegments2d = theSplit2dTool->GetCurves();
      Handle(TColGeom2d_HArray1OfCurve) theSegments2dR;
      if (isSeam)
      {
        Handle(Geom2d_Curve) c2;
        Standard_Real        f2, l2;
        // smh#8
        TopoDS_Shape tmpE = E.Reversed();
        TopoDS_Edge  erev = TopoDS::Edge(tmpE);
        if (sae.PCurve(erev, myFace, c2, f2, l2, Standard_False))
        {
          theSplit2dTool->Init(c2, f2, l2);
          if (!theKnots2d.IsNull())
            theSplit2dTool->SetSplitValues(theKnots2d);
          theSplit2dTool->Perform(Standard_True);
          Handle(TColStd_HSequenceOfReal) revKnots2d = theSplit2dTool->SplitValues();
          if (revKnots2d->Length() != theKnots2d->Length())
          {
            isSeam = Standard_False;
#ifdef OCCT_DEBUG
            std::cout
              << "Error: ShapeUpgrade_WireDivide: seam has different splitting values on pcurvesd"
              << std::endl;
#endif
          }
          else
            theSegments2dR = theSplit2dTool->GetCurves();
        }
        else
          isSeam = Standard_False;
      }

      // Exploring theEdge
      TopoDS_Vertex    V1o       = TopExp::FirstVertex(E, Standard_False);
      TopoDS_Vertex    V2o       = TopExp::LastVertex(E, Standard_False);
      Standard_Boolean isForward = (E.Orientation() == TopAbs_FORWARD);
      Standard_Real    TolEdge   = BRep_Tool::Tolerance(E);
      Standard_Boolean isDeg     = BRep_Tool::Degenerated(E);

      // Copy vertices to protect original shape against SameParamseter
      // smh#8
      TopoDS_Shape  emptyCopiedV1 = V1o.EmptyCopied();
      TopoDS_Vertex V1            = TopoDS::Vertex(emptyCopiedV1);
      Context()->Replace(V1o, V1);
      TopoDS_Vertex V2;
      if (V1o.IsSame(V2o))
      {
        // smh#8
        TopoDS_Shape tmpV = V1.Oriented(V2o.Orientation());
        V2                = TopoDS::Vertex(tmpV);
      }
      else
      {
        // smh#8
        TopoDS_Shape emptyCopied = V2o.EmptyCopied();
        V2                       = TopoDS::Vertex(emptyCopied);
        Context()->Replace(V2o, V2);
      }

      // collect NM vertices

      Standard_Real            af = 0., al = 0.;
      Handle(Geom_Curve)       c3d;
      Adaptor3d_CurveOnSurface AdCS;
      if (myEdgeDivide->HasCurve3d())
        sae.Curve3d(E, c3d, af, al, Standard_False);
      else if (myEdgeDivide->HasCurve2d() && !Surf.IsNull())
      {
        Handle(Geom2d_Curve) c2d;
        sae.PCurve(E, myFace, c2d, af, al, Standard_False);
        Handle(Adaptor3d_Surface) AdS  = new GeomAdaptor_Surface(Surf);
        Handle(Adaptor2d_Curve2d) AC2d = new Geom2dAdaptor_Curve(c2d, af, al);
        AdCS.Load(AC2d);
        AdCS.Load(AdS);
      }
      TopTools_SequenceOfShape aSeqNMVertices;
      TColStd_SequenceOfReal   aSeqParNM;
      TopoDS_Iterator          aItv(E, Standard_False);
      ShapeAnalysis_Curve      sac;
      for (; aItv.More(); aItv.Next())
      {
        if (aItv.Value().Orientation() == TopAbs_INTERNAL
            || aItv.Value().Orientation() == TopAbs_EXTERNAL)
        {
          TopoDS_Vertex aVold = TopoDS::Vertex(aItv.Value());
          aSeqNMVertices.Append(aVold);
          gp_Pnt        aP = BRep_Tool::Pnt(TopoDS::Vertex(aVold));
          Standard_Real ppar;
          gp_Pnt        pproj;
          if (!c3d.IsNull())
            sac.Project(c3d, aP, Precision(), pproj, ppar, af, al, Standard_False);
          else
            sac.Project(AdCS, aP, Precision(), pproj, ppar);
          aSeqParNM.Append(ppar);
        }
      }

      // creating new edge(s)
      Handle(TColGeom_HArray1OfCurve) theSegments3d;
      if (myEdgeDivide->HasCurve3d())
        theSegments3d = theSplit3dTool->GetCurves();

      Standard_Integer nbc = 0;
      if (!theSegments3d.IsNull())
      {
        nbc = theSegments3d->Length();
        if (!theSegments2d.IsNull())
        {
          Standard_Integer nbc2d = theSegments2d->Length();
          if (nbc != nbc2d)
          {
#ifdef OCCT_DEBUG
            std::cout << "Error: Number of intervals are not equal for 2d 3d. Ignored."
                      << std::endl;
#endif
            nbc = Min(nbc, nbc2d);
          }
        }
      }
      else if (!theSegments2d.IsNull()) // if theSegments have different length ???
        nbc = theSegments2d->Length();

      if (nbc <= 1 && !theSplit3dTool->Status(ShapeExtend_DONE)
          && !theSplit2dTool->Status(ShapeExtend_DONE))
      {
        B.Add(newWire, E);
        continue;
      }

      myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE1);

      TopoDS_Wire resWire;
      B.MakeWire(resWire);
      //      TopoDS_Vertex firstVertex, lastVertex;
      Standard_Integer numE  = 0;
      gp_Pnt           pntV1 = BRep_Tool::Pnt(V1);
      // gp_Pnt pntV2 = BRep_Tool::Pnt(V2); // pntV2 not used - see below (skl)
      // Standard_Real V2Tol = LimitTolerance( BRep_Tool::Tolerance(V2) ); // V2Tol not used - see
      // below (skl)

      // clang-format off
      Handle(ShapeUpgrade_FixSmallCurves) FixSmallCurveTool = GetFixSmallCurveTool(); //gka Precision
      // clang-format on
      FixSmallCurveTool->SetMinTolerance(MinTolerance());
      FixSmallCurveTool->Init(E, myFace);
      FixSmallCurveTool->SetSplitCurve3dTool(theSplit3dTool);
      FixSmallCurveTool->SetSplitCurve2dTool(theSplit2dTool);
      FixSmallCurveTool->SetPrecision(MinTolerance());
      Standard_Integer Savnum = 0;
      Standard_Real    SavParf;
      Standard_Integer Small = 0;
      for (Standard_Integer icurv = 1; icurv <= nbc; icurv++)
      {

        Handle(Geom_Curve) theNewCurve3d;
        if (!theSegments3d.IsNull())
          theNewCurve3d = theSegments3d->Value(icurv);

        Handle(Geom2d_Curve) theNewPCurve1;
        if (!theSegments2d.IsNull())
          theNewPCurve1 = theSegments2d->Value(icurv);
        Handle(Geom2d_Curve) revPCurve;
        if (isSeam)
          revPCurve = theSegments2dR->Value(icurv);
        // construction of the intermediate Vertex
        TopoDS_Vertex V;
        if (icurv <= nbc && nbc != 1 && !isDeg)
        {
          Standard_Real par, parf /*,SavParl*/;
          // Standard_Real SaveParf; // SaveParf not used - see below (skl)
          gp_Pnt P, P1, PM;
          // if edge has 3d curve, take point from it
          if (!theNewCurve3d.IsNull())
          {
            if (theNewCurve3d->IsKind(STANDARD_TYPE(Geom_BoundedCurve)))
            {
              par  = theNewCurve3d->LastParameter();
              parf = theNewCurve3d->FirstParameter();
            }
            else
            {
              par  = theKnots3d->Value(icurv + 1);
              parf = theKnots3d->Value(icurv);
            }
            P  = theNewCurve3d->Value(par);
            P1 = theNewCurve3d->Value(parf);
            PM = theNewCurve3d->Value((parf + par) / 2);
          }
          // else use pcurve and surface (suppose that both exist)
          else
          {
            if (Surf.IsNull())
              Surf = BRep_Tool::Surface(myFace, Loc);
            if (theNewPCurve1->IsKind(STANDARD_TYPE(Geom2d_BoundedCurve)))
            {
              par  = theNewPCurve1->LastParameter();
              parf = theNewPCurve1->FirstParameter();
            }
            else
            {
              par  = theKnots2d->Value(icurv + 1);
              parf = theKnots2d->Value(icurv);
            }
            gp_Pnt2d p2d  = theNewPCurve1->Value(par);
            gp_Pnt2d p2df = theNewPCurve1->Value(parf);
            gp_Pnt2d p2dM = theNewPCurve1->Value((parf + par) / 2);
            P             = Surf->Value(p2d.X(), p2d.Y());
            P1            = Surf->Value(p2df.X(), p2df.Y());
            PM            = Surf->Value(p2dM.X(), p2dM.Y());
            P.Transform(Loc.Transformation());
            P1.Transform(Loc.Transformation());
            PM.Transform(Loc.Transformation());
          }
          if (P.Distance(pntV1) < MinTolerance() && P.Distance(PM) < MinTolerance()
              && !myFace.IsNull())
          {
            if (!Small)
            {
              SavParf = parf;
              Savnum  = icurv;
            }
            // SavParl = par;
            Small++;
            if (icurv == nbc)
            {
              TopoDS_Vertex VVV = V1;
              VVV.Orientation(V2.Orientation());
              Context()->Replace(V2, VVV);
            }
            continue;
          }
          if (Small)
          {
            if (P.Distance(P1) > MinTolerance() || P.Distance(PM) > MinTolerance())
            {
              // FixSmallCurveTool->Perform(prevEdge,theNewCurve3d,theNewPCurve1,revPCurve,SavParf,SavParl);
              gp_Pnt pmid = 0.5 * (pntV1.XYZ() + P1.XYZ());
              B.UpdateVertex(V1, pmid, 0);
            }
            else
            {
              Handle(Geom_Curve)   atmpCurve;
              Handle(Geom2d_Curve) atmpCurve2d1, atmprepcurve;
              if (FixSmallCurveTool->Approx(atmpCurve, atmpCurve2d1, atmprepcurve, SavParf, par))
              { // BRepTools
                theNewCurve3d = atmpCurve;
                theNewPCurve1 = atmpCurve2d1;
                revPCurve     = atmprepcurve;
              }
              else
              {
                gp_Pnt pmid = 0.5 * (pntV1.XYZ() + P1.XYZ());
                B.UpdateVertex(V1, pmid, 0);
              }
            }
            Small = 0;
          }
          // pdn
          /* if(P.Distance (pntV1) < V1Tol)
             V = V1;
           else if (P.Distance (pntV2) < V2Tol) {
             V = V2;
             V1Tol = V2Tol;
             pntV1 = pntV2;
           }
           else {*/
          if (icurv != nbc)
          {
            B.MakeVertex(V, P, TolEdge); // tolerance of the edge
            pntV1 = P;
          }
          else
            V = V2;
          // else  V2;
          // }
          //	  if (ShapeUpgrade::Debug()) std::cout <<"... New intermediate Vertex ("
          //	    <<P.X()<<","<<P.Y()<<","<<P.Z()<<") :"<<(void*) &(*V.TShape())
          //	      <<" with Tolerance "<<TolEdge <<std::endl;
        }
        // else V = V2;

        TopoDS_Edge     newEdge;
        ShapeBuild_Edge sbe;
        if (isForward)
        {
          V1.Orientation(TopAbs_FORWARD);
          V.Orientation(TopAbs_REVERSED);
          newEdge = sbe.CopyReplaceVertices(E, V1, V);
        }
        else
        {
          V1.Orientation(TopAbs_REVERSED);
          V.Orientation(TopAbs_FORWARD);
          newEdge = sbe.CopyReplaceVertices(E, V, V1);
        }
        sbe.CopyPCurves(newEdge, E);
        if (!theNewCurve3d.IsNull())
          B.UpdateEdge(newEdge, theNewCurve3d, 0.);
        else if (isDeg)
          B.Degenerated(newEdge, Standard_True);
        // if(isSeam) {
        //  Handle(Geom2d_Curve) revPCurve = theSegments2dR->Value(icurv);
        // if(newEdge.Orientation()==TopAbs_FORWARD)
        // B.UpdateEdge ( newEdge, theNewPCurve1, revPCurve, myFace, 0. );
        // else
        // B.UpdateEdge ( newEdge, revPCurve, theNewPCurve1, myFace, 0. );
        //}
        // else if ( ! myFace.IsNull() )
        // B.UpdateEdge ( newEdge, theNewPCurve1, myFace, 0. );

        Standard_Real f3d = 0., l3d = 0.;
        if (!Savnum)
          Savnum = icurv;
        Standard_Boolean srNew;
        if (!theNewCurve3d.IsNull())
        {
          if (theNewCurve3d->IsKind(STANDARD_TYPE(Geom_BoundedCurve)))
          {
            f3d   = theNewCurve3d->FirstParameter();
            l3d   = theNewCurve3d->LastParameter();
            srNew = ((f3d == theKnots3d->Value(Savnum)) && (l3d == theKnots3d->Value(icurv + 1)));
          }
          else
          {
            f3d   = theKnots3d->Value(Savnum);
            l3d   = theKnots3d->Value(icurv + 1);
            srNew = Standard_True;
          }
        }
        else
          srNew = Standard_True;

        Standard_Real f2d = 0, l2d = 0;
        if (!theNewPCurve1.IsNull())
        {
          if (theNewPCurve1->IsKind(STANDARD_TYPE(Geom2d_BoundedCurve)))
          {
            f2d = theNewPCurve1->FirstParameter();
            l2d = theNewPCurve1->LastParameter();
            srNew &= ((f2d == theKnots2d->Value(Savnum)) && (l2d == theKnots2d->Value(icurv + 1)));
          }
          else
          {
            f2d = theKnots2d->Value(Savnum);
            l2d = theKnots2d->Value(icurv + 1);
          }
        }
        // if(!Savnum) Savnum = icurv;
        if (!theNewCurve3d.IsNull())
          theTransferParamTool->TransferRange(newEdge,
                                              theKnots3d->Value(Savnum),
                                              theKnots3d->Value(icurv + 1),
                                              Standard_False);
        else
          theTransferParamTool->TransferRange(newEdge,
                                              theKnots2d->Value(Savnum),
                                              theKnots2d->Value(icurv + 1),
                                              Standard_True);
        /*
        Standard_Real alpha = (theKnots3d->Value (icurv) - f)/(l - f);
        Standard_Real beta  = (theKnots3d->Value (icurv + 1) - f)/(l - f);
        sbe.CopyRanges(newEdge,E, alpha, beta);*/
        Savnum = 0;
        Handle(Geom2d_Curve) c2dTmp;
        Standard_Real        setF, setL;
        if (!myFace.IsNull() && sae.PCurve(newEdge, myFace, c2dTmp, setF, setL, Standard_False))
          srNew &= ((setF == f2d) && (setL == l2d));

        if (isSeam)
        {
          // Handle(Geom2d_Curve  revPCurve = theSegments2dR->Value(icurv);
          if (newEdge.Orientation() == TopAbs_FORWARD)
            B.UpdateEdge(newEdge, theNewPCurve1, revPCurve, myFace, 0.);
          else
            B.UpdateEdge(newEdge, revPCurve, theNewPCurve1, myFace, 0.);
        }
        else if (!myFace.IsNull())
        {
          B.UpdateEdge(newEdge, theNewPCurve1, myFace, 0.);
        }

        if (!theNewCurve3d.IsNull())
          sbe.SetRange3d(newEdge, f3d, l3d);
        if (!theNewPCurve1.IsNull())
        {
          B.Range(newEdge, myFace, f2d, l2d);
        }
        if ((!wasSR || !srNew) && !BRep_Tool::Degenerated(newEdge))
        {
          B.SameRange(newEdge, Standard_False);
        }

        // addition NM vertices to new edges
        Standard_Real afpar = (myEdgeDivide->HasCurve3d() ? f3d : f2d);
        Standard_Real alpar = (myEdgeDivide->HasCurve3d() ? l3d : l2d);
        for (Standard_Integer n = 1; n <= aSeqParNM.Length(); ++n)
        {
          Standard_Real apar  = aSeqParNM.Value(n);
          TopoDS_Vertex aVold = TopoDS::Vertex(aSeqNMVertices.Value(n));
          TopoDS_Vertex aNMVer =
            ShapeAnalysis_TransferParametersProj::CopyNMVertex(aVold, newEdge, E);
          Context()->Replace(aVold, aNMVer);
          if (fabs(apar - afpar) <= Precision::PConfusion())
          {
            Context()->Replace(aNMVer, V1);
          }
          else if (fabs(apar - alpar) <= Precision::PConfusion())
          {
            Context()->Replace(aNMVer, V);
          }
          else if (apar > afpar && apar < alpar)
          {
            B.Add(newEdge, aNMVer);
          }
          else
          {
            continue;
          }

          aSeqNMVertices.Remove(n);
          aSeqParNM.Remove(n);
          n--;
        }

        //	if (ShapeUpgrade::Debug()) std::cout <<"... New Edge "
        //	  <<(void*) &(*newEdge.TShape())<<" on vertices "
        //	    <<(void*) &(*V1.TShape())<<", " <<(void*) &(*V.TShape())
        //	      <<" with Tolerance "<<TolEdge <<std::endl;
        B.Add(resWire, newEdge);
        B.Add(newWire, newEdge);
        numE++;
        V1 = V;
      }
      if (numE)
      {
        resWire.Closed(BRep_Tool::IsClosed(resWire));
        Context()->Replace(E, resWire);
      }
      else
        Context()->Remove(E);
    }
  }
  if (Status(ShapeExtend_DONE))
  {
    // smh#8
    newWire.Closed(BRep_Tool::IsClosed(newWire));
    TopoDS_Shape tmpW = Context()->Apply(newWire).Oriented(myWire.Orientation());
    myWire            = TopoDS::Wire(tmpW);
  }
}

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

const TopoDS_Wire& ShapeUpgrade_WireDivide::Wire() const
{
  return myWire;
}

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

Standard_Boolean ShapeUpgrade_WireDivide::Status(const ShapeExtend_Status status) const
{
  return ShapeExtend::DecodeStatus(myStatus, status);
}

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

void ShapeUpgrade_WireDivide::SetSplitCurve3dTool(
  const Handle(ShapeUpgrade_SplitCurve3d)& splitCurve3dTool)
{
  mySplitCurve3dTool = splitCurve3dTool;
}

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

void ShapeUpgrade_WireDivide::SetSplitCurve2dTool(
  const Handle(ShapeUpgrade_SplitCurve2d)& splitCurve2dTool)
{
  mySplitCurve2dTool = splitCurve2dTool;
}

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

Handle(ShapeUpgrade_SplitCurve3d) ShapeUpgrade_WireDivide::GetSplitCurve3dTool() const
{
  return mySplitCurve3dTool;
}

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

Handle(ShapeUpgrade_SplitCurve2d) ShapeUpgrade_WireDivide::GetSplitCurve2dTool() const
{
  return mySplitCurve2dTool;
}

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

void ShapeUpgrade_WireDivide::SetEdgeDivideTool(
  const Handle(ShapeUpgrade_EdgeDivide)& edgeDivideTool)
{
  myEdgeDivide = edgeDivideTool;
}

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

Handle(ShapeUpgrade_EdgeDivide) ShapeUpgrade_WireDivide::GetEdgeDivideTool() const
{
  return myEdgeDivide;
}

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

void ShapeUpgrade_WireDivide::SetTransferParamTool(
  const Handle(ShapeAnalysis_TransferParameters)& TransferParam)
{
  myTransferParamTool = TransferParam;
}

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

Handle(ShapeAnalysis_TransferParameters) ShapeUpgrade_WireDivide::GetTransferParamTool()
{
  return myTransferParamTool;
}

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

void ShapeUpgrade_WireDivide::SetEdgeMode(const Standard_Integer EdgeMode)
{
  myEdgeMode = EdgeMode;
}

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

void ShapeUpgrade_WireDivide::SetFixSmallCurveTool(
  const Handle(ShapeUpgrade_FixSmallCurves)& FixSmallCurvesTool)
{
  myFixSmallCurveTool = FixSmallCurvesTool;
}

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

Handle(ShapeUpgrade_FixSmallCurves) ShapeUpgrade_WireDivide::GetFixSmallCurveTool() const
{
  return myFixSmallCurveTool;
}
