// Created on: 1995-03-29
// Created by: Frederic MAUPAS
// Copyright (c) 1995-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 21.08.98 PRO7656
// gka 15.12.98 UKI60591 #1274560
// pdn 18.12.98 to keep pcurves
//: o0 abv 16.02.99: POLYLINE allowed as 3d curve of edge
//:    abv 07.04.99: S4136: improve tolerance management and dealing with pcurves
//    rln 02.06.99 removing #include <StepToTopoDS_DegeneratedTool.hxx>
//    smh 31.01.01 BUC60810 : IsNull protection

#include <BRep_Builder.hxx>
#include <BRep_TEdge.hxx>
#include <BRep_Tool.hxx>
#include <ElCLib.hxx>
#include <Geom2d_Curve.hxx>
#include <Geom_Curve.hxx>
#include <Geom_Plane.hxx>
#include <Geom_RectangularTrimmedSurface.hxx>
#include <Geom_Surface.hxx>
#include <gp_Pnt.hxx>
#include <Precision.hxx>
#include <ShapeAlgo.hxx>
#include <ShapeAlgo_AlgoContainer.hxx>
#include <ShapeAlgo_ToolContainer.hxx>
#include <ShapeAnalysis_Curve.hxx>
#include <ShapeAnalysis_Edge.hxx>
#include <Standard_ErrorHandler.hxx>
#include <ShapeBuild_Edge.hxx>
#include <ShapeExtend_WireData.hxx>
#include <ShapeFix_EdgeProjAux.hxx>
#include <StdFail_NotDone.hxx>
#include <StepData_Factors.hxx>
#include <StepData_StepModel.hxx>
#include <StepGeom_Pcurve.hxx>
#include <StepGeom_Surface.hxx>
#include <StepGeom_SurfaceCurve.hxx>
#include <StepRepr_DefinitionalRepresentation.hxx>
#include <StepShape_EdgeCurve.hxx>
#include <StepShape_EdgeLoop.hxx>
#include <StepShape_FaceBound.hxx>
#include <StepShape_OrientedEdge.hxx>
#include <StepShape_Vertex.hxx>
#include <StepToGeom.hxx>
#include <StepToTopoDS.hxx>
#include <StepToTopoDS_GeometricTool.hxx>
#include <StepToTopoDS_NMTool.hxx>
#include <StepToTopoDS_Tool.hxx>
#include <StepToTopoDS_TranslateEdge.hxx>
#include <StepToTopoDS_TranslateEdgeLoop.hxx>
#include <StepToTopoDS_TranslateVertex.hxx>
#include <TopAbs.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_Shape.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Wire.hxx>
#include <Transfer_TransientProcess.hxx>
#include <XSAlgo.hxx>
#include <XSAlgo_ShapeProcessor.hxx>

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

static void RemoveSinglePCurve(const TopoDS_Edge& aEdge, const TopoDS_Face& aFace)
{
  ShapeBuild_Edge().RemovePCurve(aEdge, aFace);
}

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

static void RemovePCurves(const TopoDS_Wire& aWire, const TopoDS_Face& aFace)
{
  TopExp_Explorer EdgeExp(aWire, TopAbs_EDGE);
  while (EdgeExp.More())
  {
    const TopoDS_Edge& myEdge = TopoDS::Edge(EdgeExp.Current());
    RemoveSinglePCurve(myEdge, aFace);
    EdgeExp.Next();
  }
}

// ============================================================================
// Method  : CheckPCurves
// Purpose : Checks the pcurves topological trimming parameter consistency
//           and deviation between 2D ans 3D
// ============================================================================

static void CheckPCurves(TopoDS_Wire&       aWire,
                         const TopoDS_Face& aFace,
                         const bool         isPlane,
                         const double       preci)
{
  if (isPlane)
  {
    RemovePCurves(aWire, aFace);
    return;
  }
  BRep_Builder              B;
  double                    w1, w2, cf, cl;
  occ::handle<Geom_Surface> mySurf = BRep_Tool::Surface(aFace);

  occ::handle<ShapeExtend_WireData> sbwd = new ShapeExtend_WireData(aWire);
  for (int i = 1; i <= sbwd->NbEdges(); i++)
  {
    const TopoDS_Edge& myEdge = sbwd->Edge(i);

    // First Check : 2D Parameters on Edge :
    // Case 1 : w1 == w2 illegal => Drop the PCurve
    // Case 2 : on bounded curve w1 < FirstParameter => w1 = FirstParameter
    //                           w2 > LastParameter  => w2 = LastParameter

    occ::handle<Geom2d_Curve> thePC;
    ShapeAnalysis_Edge        sae;
    if (!sae.PCurve(myEdge, aFace, thePC, w1, w2, false))
    {
      continue;
    }
    cf = thePC->FirstParameter();
    cl = thePC->LastParameter();

    if (w1 == w2)
    {
      RemoveSinglePCurve(myEdge, aFace);
#ifdef OCCT_DEBUG
      std::cout << "Removing pcuve w1=w2" << std::endl;
#endif
      continue;
    }

    // check parameters of range only for not periodic curves
    if (!thePC->IsPeriodic())
    {
      if (w1 < cf)
      {
        B.Range(myEdge, aFace, cf, w2);
        w1 = cf;
      }
      if (w2 > cl)
      {
        B.Range(myEdge, aFace, w1, cl);
        w2 = cl;
      }
    }

    if (w1 > w2 && mySurf->IsUPeriodic())
    {
      double u1, u2, v1, v2;
      mySurf->Bounds(u1, u2, v1, v2);
      ElCLib::AdjustPeriodic(u1,
                             u2,
                             std::min(std::abs(w2 - w1) / 2, Precision::PConfusion()),
                             w1,
                             w2);
      B.Range(myEdge, aFace, w1, w2);
    }

    // advanced check
    XSAlgo_ShapeProcessor::CheckPCurve(myEdge, aFace, preci, sbwd->IsSeam(i));
  }
}

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

StepToTopoDS_TranslateEdgeLoop::StepToTopoDS_TranslateEdgeLoop()
    : myError(StepToTopoDS_TranslateEdgeLoopOther)
{
  done = false;
}

// ============================================================================
// Method  : StepToTopoDS_TranslateEdgeLoop::StepToTopoDS_TranslateEdgeLoop
// Purpose : Constructor with a FaceSurface and a Tool
// ============================================================================

StepToTopoDS_TranslateEdgeLoop::StepToTopoDS_TranslateEdgeLoop(
  const occ::handle<StepShape_FaceBound>& FB,
  const TopoDS_Face&                      Face,
  const occ::handle<Geom_Surface>&        GeomSurf,
  const occ::handle<StepGeom_Surface>&    StepSurf,
  const bool                              sameSense,
  StepToTopoDS_Tool&                      T,
  StepToTopoDS_NMTool&                    NMTool,
  const StepData_Factors&                 theLocalFactors)
{
  Init(FB, Face, GeomSurf, StepSurf, sameSense, T, NMTool, theLocalFactors);
}

// ============================================================================
// Method  : Init
// Purpose : Init with a EdgeLoop and a Tool
// ============================================================================

void StepToTopoDS_TranslateEdgeLoop::Init(const occ::handle<StepShape_FaceBound>& FaceBound,
                                          const TopoDS_Face&                      Face,
                                          const occ::handle<Geom_Surface>&        GeomSurf,
                                          const occ::handle<StepGeom_Surface>&    StepSurf,
                                          const bool                              sameSense,
                                          StepToTopoDS_Tool&                      aTool,
                                          StepToTopoDS_NMTool&                    NMTool,
                                          const StepData_Factors&                 theLocalFactors)
{
  done                               = true;
  occ::handle<StepShape_EdgeLoop> EL = occ::down_cast<StepShape_EdgeLoop>(FaceBound->Bound());

  if (aTool.IsBound(EL))
  {
    myResult = TopoDS::Wire(aTool.Find(EL));
    myError  = StepToTopoDS_TranslateEdgeLoopDone;
    done     = true;
    return;
  }

  BRep_Builder                           B;
  occ::handle<Transfer_TransientProcess> TP  = aTool.TransientProcess();
  occ::handle<StepData_StepModel> aStepModel = occ::down_cast<StepData_StepModel>(TP->Model());
  int                             modepcurve = aStepModel->InternalParameters.ReadSurfaceCurveMode;
  //  0,1 : suivre le code,  2 : ne prendre que pcurve,  3 : ne prendre que C3D

  double        preci = Precision();
  TopoDS_Wire   W;
  TopoDS_Edge   E;
  TopoDS_Vertex V;

  bool isSeam, isLikeSeam;

  occ::handle<StepShape_OrientedEdge>              OrEdge1, OrEdge2;
  occ::handle<StepGeom_Curve>                      StepCurve, StepCurve1, StepCurve2;
  occ::handle<StepRepr_DefinitionalRepresentation> DRI, Dri1, Dri2;

  occ::handle<Geom2d_Curve> C2d, C2d1, C2d2, WhichC2d1, WhichC2d2;
  TopoDS_Edge               suspectE; //: f1, degEdge;

  int j, NbEdge = EL->NbEdgeList();
  if (NbEdge == 0)
  {
    TP->AddWarning(EL, "Wire not done. EdgeLoop does not contain edges.");
    done = false;
    return;
  }
  // PTV 16.09.2000
  // default value set as true (if not correct see logic of algorithm).
  bool                      hasPcurve = true;
  bool                      isPlane   = GeomSurf->IsKind(STANDARD_TYPE(Geom_Plane));
  occ::handle<Geom_Surface> ConvSurf  = GeomSurf;
  if (GeomSurf->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface)))
  {
    occ::handle<Geom_RectangularTrimmedSurface> theRTS =
      occ::down_cast<Geom_RectangularTrimmedSurface>(GeomSurf);
    ConvSurf = theRTS->BasisSurface();
  }

  aTool.ComputePCurve(false);

  // What is the Wire Orientation
  bool ForwardWire = FaceBound->Orientation();

  // --- Initialize target Wire ---

  B.MakeWire(W);

  // -----------------------------------------------
  // Preparation : Make Vertices + Curves3d
  //  Hence, a closed curve limited by distinct vertices
  //  will give a unique vertex (if same coords)
  //
  // In addition : detect shared PCurve (cf SYRKO)
  //  This case may not be processed, PCurves has to be recomputed from scratch
  // -----------------------------------------------
  //  int theSame = 1; //gka 15.12.98

  for (j = 1; j <= NbEdge; j++)
  {
    OrEdge1 = EL->EdgeListValue(j);

    if (OrEdge1.IsNull() || OrEdge1->EdgeElement().IsNull())
    {
      TP->AddWarning(
        OrEdge1,
        "Incorrect Oriented_Edge is not translated. Oriented_Edge definition is not correct");
      continue;
    }

    // see bug #29979: oriented edge contains another oriented edge
    if (OrEdge1->EdgeElement()->IsKind(STANDARD_TYPE(StepShape_OrientedEdge)))
    {
      OrEdge1 = occ::down_cast<StepShape_OrientedEdge>(OrEdge1->EdgeElement());
    }

    occ::handle<StepShape_EdgeCurve> EC =
      occ::down_cast<StepShape_EdgeCurve>(OrEdge1->EdgeElement());
    if (EC.IsNull())
    {
      TP->AddWarning(OrEdge1, "Edge does not contain EDGE_CURVE, skipped");
      continue;
    }

    occ::handle<StepGeom_Curve> C = EC->EdgeGeometry();
    if (!C.IsNull())
    {
      if (C->IsKind(STANDARD_TYPE(StepGeom_SurfaceCurve)))
      {
        occ::handle<StepGeom_SurfaceCurve> Sc = occ::down_cast<StepGeom_SurfaceCurve>(C);
        C                                     = Sc->Curve3d();
      }
    }
    occ::handle<Geom_Curve> C1;
    if (!C.IsNull())
    {
      try
      {
        OCC_CATCH_SIGNALS
        C1 = occ::down_cast<Geom_Curve>(TP->FindTransient(C));
        if (C1.IsNull())
        {
          C1 = StepToGeom::MakeCurve(C, theLocalFactors);
          if (!C1.IsNull())
          {
            TP->BindTransient(C, C1);
          }
          else
          {
            TP->AddWarning(C, "Could not convert a curve. Curve definition is incorrect");
          }
        }
      }
      catch (Standard_Failure const& anException)
      {
#ifdef OCCT_DEBUG
        std::cout << "Warning: StepToTopoDS_TranslateEdgeLoop: Exception: ";
        anException.Print(std::cout);
        std::cout << std::endl;
#endif
        (void)anException;
        TP->AddFail(C, "Exception was raised. Curve geometry definition is incorrect");
      }
    }

    occ::handle<StepShape_Vertex> Vstart, Vend;
    if (EC->SameSense())
    {
      Vstart = EC->EdgeStart();
      Vend   = EC->EdgeEnd();
    }
    else
    {
      Vend   = EC->EdgeStart();
      Vstart = EC->EdgeEnd();
    }

    bool                         istV = aTool.IsBound(Vstart);
    bool                         iseV = aTool.IsBound(Vend);
    TopoDS_Vertex                V1, V2;
    StepToTopoDS_TranslateVertex myTranVertex1(Vstart, aTool, NMTool, theLocalFactors);
    StepToTopoDS_TranslateVertex myTranVertex2(Vend, aTool, NMTool, theLocalFactors);

    if (myTranVertex1.IsDone())
    {
      V1 = TopoDS::Vertex(myTranVertex1.Value());
    }
    if (myTranVertex2.IsDone())
    {
      V2        = TopoDS::Vertex(myTranVertex2.Value());
      gp_Pnt p1 = BRep_Tool::Pnt(V1);
      gp_Pnt p2 = BRep_Tool::Pnt(V2);
      if (p1.Distance(p2) <= Precision::Confusion())
      { //: S4136: preci) {
        bool Fixed = true;
        if (!iseV)
        {
          aTool.Bind(Vend, V1); // gka 21.08.1998 bug PRO7656
        }
        else if (!istV)
        {
          aTool.Bind(Vstart, V2);
        }
        else
        {
          aTool.Bind(Vend, V1);
        }
        if (!C1.IsNull() && !C1->IsClosed() && Fixed)
        {
          TP->AddWarning(EL->EdgeListValue(j), "Vertex of same coordinates, set confused");
        }
      }
    }
  }

  //: f6 abv 29 Apr 98: BUC50070 #3815: make sure that each two edges are
  // connected by the same vertex; else check that vertices confuse
  // and make it be one vertex
  // NOTE: this is done only for the case if at least one of edges
  // was not yet translated; else nothing will help
  for (j = 1; j <= NbEdge; j++)
  {
    OrEdge1 = EL->EdgeListValue(j);
    OrEdge2 = EL->EdgeListValue(j < NbEdge ? j + 1 : 1);
    if (OrEdge1.IsNull() || OrEdge2.IsNull())
    {
      continue;
    }

    occ::handle<StepShape_EdgeCurve> EC1 =
      occ::down_cast<StepShape_EdgeCurve>(OrEdge1->EdgeElement());
    occ::handle<StepShape_EdgeCurve> EC2 =
      occ::down_cast<StepShape_EdgeCurve>(OrEdge2->EdgeElement());
    if (EC1.IsNull() || EC2.IsNull()) // see #29979
    {
      continue;
    }

    occ::handle<StepShape_Vertex> Vs1, Vs2, Vs11, Vs22;
    Vs1 = (OrEdge1->Orientation() ? EC1->EdgeEnd() : EC1->EdgeStart());
    Vs2 = (OrEdge2->Orientation() ? EC2->EdgeStart() : EC2->EdgeEnd());

    Vs11 = (OrEdge1->Orientation() ? EC1->EdgeStart() : EC1->EdgeEnd());
    Vs22 = (OrEdge2->Orientation() ? EC2->EdgeEnd() : EC2->EdgeStart());

    if ((Vs1 == Vs2) || (Vs1 == Vs22) || (Vs2 == Vs11) || (Vs22 == Vs11))
    {
      continue;
    }

    StepToTopoDS_TranslateVertex myTranVertex1(Vs1, aTool, NMTool, theLocalFactors);
    StepToTopoDS_TranslateVertex myTranVertex2(Vs2, aTool, NMTool, theLocalFactors);

    TopoDS_Vertex V1, V2;
    if (myTranVertex1.IsDone())
    {
      V1 = TopoDS::Vertex(myTranVertex1.Value());
    }
    if (myTranVertex2.IsDone())
    {
      V2 = TopoDS::Vertex(myTranVertex2.Value());
    }
    if (V1.IsNull() || V2.IsNull())
    {
      continue; // not treated
    }
    if (V1.IsSame(V2))
    {
      continue; // OK
    }

    gp_Pnt p1       = BRep_Tool::Pnt(V1);
    gp_Pnt p2       = BRep_Tool::Pnt(V2);
    bool   locFixed = true;
    if (p1.Distance(p2) <= preci)
    {
      if (!aTool.IsBound(EC1))
      {
        aTool.Bind(Vs1, V2);
      }
      else if (!aTool.IsBound(EC2))
      {
        aTool.Bind(Vs2, V1);
      }
      else
      {
        locFixed = false;
      }
    }
    else
    {
      locFixed = false;
    }
    if (locFixed)
    {
      TP->AddWarning(EL, "Adjacent edges do not have common vertex; set confused");
    }
    else
    {
      TP->AddWarning(EL, "Adjacent edges are not connected");
    }
  }

  // -----------------------------------------------
  // Iteration on each Oriented Edge of the EdgeLoop
  // -----------------------------------------------

  for (j = 1; j <= NbEdge; j++)
  {

    bool ThereIsLikeSeam = false;

#ifdef OCCT_DEBUG
    std::cout << "      Processing Edge :" << j << std::endl;
#endif

    OrEdge1 = EL->EdgeListValue(j);
    if (OrEdge1.IsNull() || OrEdge1->EdgeElement().IsNull())
    {
      continue;
    }

    // see bug #29979: oriented edge contains another oriented edge
    if (OrEdge1->EdgeElement()->IsKind(STANDARD_TYPE(StepShape_OrientedEdge)))
    {
      OrEdge1 = occ::down_cast<StepShape_OrientedEdge>(OrEdge1->EdgeElement());
    }

    occ::handle<StepShape_EdgeCurve> EC =
      occ::down_cast<StepShape_EdgeCurve>(OrEdge1->EdgeElement());
    if (EC.IsNull())
    {
      continue;
    }

    // ----------------
    // Map the StepEdge
    // ----------------

    StepToTopoDS_TranslateEdge myTranEdge;

    myTranEdge.SetPrecision(preci);
    myTranEdge.SetMaxTol(MaxTol());
    myTranEdge.Init(OrEdge1, aTool, NMTool, theLocalFactors);

    if (myTranEdge.IsDone())
    {

      E = TopoDS::Edge(myTranEdge.Value());
      if (E.IsNull())
      {
        continue; // NULL, on saute
      }

      occ::handle<StepGeom_Curve> C = EC->EdgeGeometry();

      if (OrEdge1->Orientation() && EC->SameSense())
      {
        E.Orientation(TopAbs_FORWARD);
      }
      else if (!OrEdge1->Orientation() && !EC->SameSense())
      {
        E.Orientation(TopAbs_FORWARD);
      }
      else
      {
        E.Orientation(TopAbs_REVERSED);
      }

      isSeam = isLikeSeam = false;

      // ------------------------------------------
      // Map the StepEdge parametric representation
      // ------------------------------------------

      // --------------------------------------------
      // CASE 1 : The Edge Geometry is of Pcurve Type
      // --------------------------------------------
      if (C.IsNull())
      {
        aTool.ComputePCurve(true);
        hasPcurve = false;
      }
      else if (C->IsKind(STANDARD_TYPE(StepGeom_Pcurve)))
      {
        occ::handle<StepGeom_Pcurve> StepPCurve = occ::down_cast<StepGeom_Pcurve>(C);
        C2d = myTranEdge.MakePCurve(StepPCurve, ConvSurf, theLocalFactors);
        // -- Statistics --
        aTool.AddContinuity(C2d);
      }

      // -----------------------------------------
      // CASE 2 : The curve is a SurfaceCurve i.e.
      //           - a 3D Curve (mandatory)
      //           - 2 PCurveOrSurface
      //   If modepcurve = 3, PCurve are ignored here
      // -----------------------------------------

      else if (modepcurve == 3)
      {
        aTool.ComputePCurve(true);
        hasPcurve = false;
      }
      else if (C->IsKind(STANDARD_TYPE(StepGeom_SurfaceCurve)))
      {
        // recouvre les cas SeamCurve et IntersectionCurve

        occ::handle<StepGeom_SurfaceCurve> SurfCurve = occ::down_cast<StepGeom_SurfaceCurve>(C);

        occ::handle<StepGeom_Pcurve> StepPCurve, StepPCurve1, StepPCurve2;
        int lastpcurve = StepToTopoDS_GeometricTool::PCurve(SurfCurve, StepSurf, StepPCurve, 0);
        hasPcurve      = !StepPCurve.IsNull();

        // De toute facon, on recalcule

        if (isPlane)
        {
          hasPcurve = false;
        }

        // -------------------------------------------
        // ---        Special Mapping Cases :      ---
        // ---   the SurfaceCurve is a SeamCurve   ---
        // ---        or is like a seam curve      ---
        // ---         (see CATIA cylinder)        ---
        // -------------------------------------------
        isLikeSeam = StepToTopoDS_GeometricTool::IsLikeSeam(SurfCurve, StepSurf, EC, EL);

        isSeam = StepToTopoDS_GeometricTool::IsSeamCurve(SurfCurve, StepSurf, EC, EL);

        if (isSeam || isLikeSeam)
        {
          // isLikeSeam = Two faces on the same Surface
          StepPCurve1 = SurfCurve->AssociatedGeometryValue(1).Pcurve();
          StepPCurve2 = SurfCurve->AssociatedGeometryValue(2).Pcurve();
          // clang-format off
          if (StepPCurve1.IsNull() || StepPCurve2.IsNull()) { hasPcurve = false; //smh : BUC60810
            // clang-format on
          }
          else
          {
            C2d1      = myTranEdge.MakePCurve(StepPCurve1, ConvSurf, theLocalFactors);
            C2d2      = myTranEdge.MakePCurve(StepPCurve2, ConvSurf, theLocalFactors);
            hasPcurve = (!C2d1.IsNull() && !C2d2.IsNull());
          }

          if (isLikeSeam)
          {
            suspectE        = E;
            ThereIsLikeSeam = true;
            hasPcurve       = true;
          }
        }
        else if (hasPcurve)
        {
          //  GeometricTool : Pcurve a retourne StepPCurve
          while (lastpcurve > 0)
          {
            C2d1 = myTranEdge.MakePCurve(StepPCurve, ConvSurf, theLocalFactors);
            if (C2d1.IsNull())
            {
              TP->AddWarning(
                EC,
                "Incorrect pcurve is not translated. Pcurve definition is not correct");
              hasPcurve = false;
              break;
            }
            else
            {
              C2d = C2d1;
            }
            lastpcurve =
              StepToTopoDS_GeometricTool::PCurve(SurfCurve, StepSurf, StepPCurve, lastpcurve);
            // -- Statistics --
            aTool.AddContinuity(C2d);
          }
        }
        if (!hasPcurve)
        {
          // The edge geometry has no 2D representation
          aTool.ComputePCurve(true);
        }
      }

      // ----------------------------------------------------------
      // CASE 3 : The EdgeCurve Geometry is not a Pcurve
      //          nor a SurfaceCurve (i.e. it is a single 3D curve)
      // ----------------------------------------------------------

      else
      {
        aTool.ComputePCurve(true);
        hasPcurve = false;
      }

      // ----------------------------------
      // update the edge with the pcurve(s)
      // ----------------------------------

      if (hasPcurve && (isSeam || ThereIsLikeSeam))
      {

        // -----------------------------------------------------------
        // The Edge is a Seam Edge : The pcurve which is FORWARD has
        //                           to be identified
        // -----------------------------------------------------------

        if ((!C2d1.IsNull()) && (!C2d2.IsNull()))
        {
          TopAbs_Orientation CumulO, EdgeO, WireO, FaceO;
          EdgeO = E.Orientation();
          if (ForwardWire)
          {
            WireO = TopAbs_FORWARD;
          }
          else
          {
            WireO = TopAbs_REVERSED;
          }
          if (sameSense)
          {
            FaceO = TopAbs_FORWARD;
          }
          else
          {
            FaceO = TopAbs_REVERSED;
          }

          CumulO = TopAbs::Compose(EdgeO, WireO);
          CumulO = TopAbs::Compose(CumulO, FaceO);

          bool ForwardEdge = (CumulO == TopAbs_FORWARD);

          int forwardPC = ShapeAnalysis_Curve().SelectForwardSeam(C2d1, C2d2);
          if (forwardPC == 0)
          {
            TP->AddFail(EC, " Seam curve not mapped");
            done    = false;
            myError = StepToTopoDS_TranslateEdgeLoopOther;
            continue;
          }
          else if (!ForwardEdge)
          {
            forwardPC = 3 - forwardPC; // inverser 1-2
          }

          if (forwardPC == 1)
          {
            if (isSeam)
            {
              // When the edge is a Seam, it is better to find the topological
              // trimming right now.
              // Remarque : pour bien faire, il faudrait, si necessaire, recalculer
              //            les trois courbes de maniere a ce qu`elles soient
              //            immediatement Same Range et Same Parameter.
              B.UpdateEdge(E, C2d1, C2d2, Face, 0.);
              //: S4136	      FindParameter(C2d1, C2d2, E, Face, preci);
            }
            else
            {
              B.UpdateEdge(E, C2d1, Face, 0.); // preci
            }
          }
          else
          {
            if (isSeam)
            {
              // When the edge is a Seam, it is better to find the topological
              // trimming right now.
              B.UpdateEdge(E, C2d2, C2d1, Face, 0.);
              //: S4136	      FindParameter(C2d1, C2d2, E, Face, preci);
            }
            else
            {
              B.UpdateEdge(E, C2d2, Face, 0.);
            }
          }
        }
        else
        {
          TP->AddFail(EC, " Seam curve not mapped");
          done    = false;
          myError = StepToTopoDS_TranslateEdgeLoopOther;
          continue;
        }
      }
      else
      {

        // ---------------------------
        // The Edge is a "normal" edge
        // ---------------------------

        if (hasPcurve)
        {
          if (!C2d.IsNull() && !isLikeSeam)
          {
            B.UpdateEdge(E, C2d, Face, 0.);
          }
          else
          {
            TP->AddFail(EC, " Edge: Trimming of 2D curve failed");
            done    = false;
            myError = StepToTopoDS_TranslateEdgeLoopOther;
            continue;
          }
        }
      }

      if (E.IsNull())
      {
        TP->AddFail(EC, " an Edge not mapped");
        done    = false;
        myError = StepToTopoDS_TranslateEdgeLoopOther;
      }
    }
    else
    { // The Edge is Not mapped => switch to next wire ?
      TP->AddFail(EC, " an Edge not mapped");
      done    = false;
      myError = StepToTopoDS_TranslateEdgeLoopOther;
    }

    if (done)
    {
      B.Add(W, E); // on le fait ici. Sauf si erreur rencontree ... !
    }
    else
    {
      occ::handle<StepShape_Vertex> Vs1, Vs2;
      Vs1 = EC->EdgeStart();
      Vs2 = EC->EdgeEnd();
      if (!Vs1.IsNull() && !Vs2.IsNull() && Vs1 == Vs2)
      {
        done = true;
        TP->AddFail(EL, " Edge with equal vertices failed, scipped");
      }
    }
  }

  // The EdgeLoop is binded in the Wire

  if (!done)
  {
    TP->AddFail(EL, "At least one edge failed : wire not done");
    return;
  }
  W.Closed(BRep_Tool::IsClosed(W));
  aTool.Bind(EL, W);

  // ----------------------------------------------
  // Computes the 2D parameter of Vertices on Edges
  // ----------------------------------------------
  // pdn compute parameter of Vertices using projecting
  if (!aTool.ComputePCurve())
  {
    for (TopoDS_Iterator EdgeIt(W); EdgeIt.More(); EdgeIt.Next())
    {
      TopoDS_Edge                       edge = TopoDS::Edge(EdgeIt.Value());
      occ::handle<ShapeFix_EdgeProjAux> myEdgePro =
        ShapeAlgo::AlgoContainer()->ToolContainer()->EdgeProjAux();
      myEdgePro->Init(Face, edge);
      myEdgePro->Compute(preci);
      if (myEdgePro->IsFirstDone() && myEdgePro->IsLastDone())
      {
        if (std::abs(myEdgePro->FirstParam() - myEdgePro->LastParam()) < Precision::PConfusion())
        {
          continue;
        }
        B.Range(edge, Face, myEdgePro->FirstParam(), myEdgePro->LastParam());
      }
      else
      {
        RemoveSinglePCurve(edge, Face);
#ifdef OCCT_DEBUG
        std::cout << "Removing after prj" << std::endl;
#endif
      }
    }
  }

  myResult = W;
  myError  = StepToTopoDS_TranslateEdgeLoopDone;
  done     = true;
  //  Check des PCurves SYSTEMATIQUE, s il n y en a que quelques unes
  CheckPCurves(W, Face, isPlane, preci);
}

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

const TopoDS_Shape& StepToTopoDS_TranslateEdgeLoop::Value() const
{
  StdFail_NotDone_Raise_if(!done, "StepToTopoDS_TranslateEdgeLoop::Value() - no result");
  return myResult;
}

// ============================================================================
// Method  : Error
// Purpose : Return the TranslateEdgeLoop error
// ============================================================================

StepToTopoDS_TranslateEdgeLoopError StepToTopoDS_TranslateEdgeLoop::Error() const
{
  return myError;
}
