// Created on: 1998-11-25
// Created by: Prestataire Xuan PHAM PHU
// Copyright (c) 1998-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 <BndLib_Add2dCurve.hxx>
#include <BRep_Builder.hxx>
#include <BRep_GCurve.hxx>
#include <BRep_TEdge.hxx>
#include <BRep_Tool.hxx>
#include <Geom2d_TrimmedCurve.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopOpeBRepTool.hxx>
#include <TopOpeBRepTool_2d.hxx>
#include <TopOpeBRepTool_CORRISO.hxx>
#include <TopOpeBRepTool_define.hxx>
#include <TopOpeBRepTool_EXPORT.hxx>
#include <TopOpeBRepTool_PURGE.hxx>
#include <TopOpeBRepTool_TOOL.hxx>
#include <TopTools_Array1OfShape.hxx>

#ifdef OCCT_DEBUG
extern Standard_Boolean                            TopOpeBRepTool_GettraceCORRISO();
Standard_EXPORT TopTools_IndexedMapOfShape         STATIC_PURGE_mapv;
Standard_EXPORT TopTools_IndexedMapOfOrientedShape STATIC_PURGE_mapeds;
extern void                                        FUN_tool_trace(const Standard_Integer Index);
extern void                                        FUN_tool_trace(const gp_Pnt2d p2d);
#endif

static void FUN_RaiseError()
{
#ifdef OCCT_DEBUG
  //  Standard_Boolean trc = TopOpeBRepTool_GettraceCORRISO();
  FUN_REINIT();
//  if (trc) std::cout <<"*********failure in CORRISO***********\n";
#endif
}

static void FUN_Raise()
{
#ifdef OCCT_DEBUG
//  std::cout <<"*********failure in CORRISO***********\n";
#endif
}

#ifdef DRAW
  #include <TopOpeBRepTool_DRAW.hxx>
#endif

#define M_FORWARD(sta) (sta == TopAbs_FORWARD)
#define M_REVERSED(sta) (sta == TopAbs_REVERSED)
#define M_INTERNAL(sta) (sta == TopAbs_INTERNAL)
#define M_EXTERNAL(sta) (sta == TopAbs_EXTERNAL)

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

TopOpeBRepTool_CORRISO::TopOpeBRepTool_CORRISO() {}

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

TopOpeBRepTool_CORRISO::TopOpeBRepTool_CORRISO(const TopoDS_Face& Fref)
{
  myFref = Fref;

  FUN_tool_closedS(myFref, myUclosed, myUper, myVclosed, myVper);

  const Handle(Geom_Surface)& SU = BRep_Tool::Surface(myFref);
  myGAS                          = GeomAdaptor_Surface(SU);
}

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

const TopoDS_Face& TopOpeBRepTool_CORRISO::Fref() const
{
  return myFref;
}

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

const GeomAdaptor_Surface& TopOpeBRepTool_CORRISO::GASref() const
{
  return myGAS;
}

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

Standard_Boolean TopOpeBRepTool_CORRISO::Refclosed(const Standard_Integer x,
                                                   Standard_Real&         xperiod) const
{
  if (x == 1)
  {
    xperiod = myUper;
    return myUclosed;
  }
  if (x == 2)
  {
    xperiod = myVper;
    return myVclosed;
  }
  return Standard_False;
}

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

Standard_Boolean TopOpeBRepTool_CORRISO::Init(const TopoDS_Shape& S)
{
#ifdef DRAW
  Standard_Integer ie   = 0;
  Standard_Boolean trc  = TopOpeBRepTool_GettraceCORRISO();
  Standard_Boolean INIT = Standard_True;
  if (INIT)
    FUN_REINIT();
#endif

  myERep2d.Clear();
  myEds.Clear();
  myVEds.Clear();

  if (S.IsNull())
    return Standard_False;
  myS = S;

  TopExp_Explorer ex(S, TopAbs_EDGE);
  for (; ex.More(); ex.Next())
  {
    const TopoDS_Edge& E = TopoDS::Edge(ex.Current());
#ifdef OCCT_DEBUG
    Standard_Integer iE = STATIC_PURGE_mapeds.Add(E);
    (void)iE; // avoid warning
  #ifdef DRAW
    if (trc)
    {
      TCollection_AsciiString aa = TCollection_AsciiString("e");
      FUN_tool_draw(aa, E, iE);
    }
  #endif
#endif

    // myEds :
    myEds.Append(E);

    // myERep2d :
    //    Standard_Real f,l,tol; Handle(Geom2d_Curve) PC = FC2D_CurveOnSurface(E,myFref,f,l,tol);
    Handle(Geom2d_Curve) PC;
    Standard_Real        f, l, tol;
    Standard_Boolean     hasold = FC2D_HasOldCurveOnSurface(E, myFref, PC);
    PC                          = FC2D_EditableCurveOnSurface(E, myFref, f, l, tol);
    if (!hasold)
      FC2D_AddNewCurveOnSurface(PC, E, myFref, f, l, tol);
    if (PC.IsNull())
      return Standard_False;
    TopOpeBRepTool_C2DF C2DF(PC, f, l, tol, myFref);
    myERep2d.Bind(E, C2DF);

    // myVEds :
    TopExp_Explorer exv(E, TopAbs_VERTEX);
    for (; exv.More(); exv.Next())
    {
      const TopoDS_Vertex& v = TopoDS::Vertex(exv.Current());
#ifdef OCCT_DEBUG
      Standard_Integer aniE = STATIC_PURGE_mapeds.Add(E);
      (void)aniE; // avoid warning
  #ifdef DRAW
      if (trc)
      {
        TCollection_AsciiString bb = TCollection_AsciiString("v");
        FUN_tool_draw(bb, v, iv);
      }
  #endif
#endif
      Standard_Boolean isb = myVEds.IsBound(v);
      if (isb)
        myVEds.ChangeFind(v).Append(E);
      else
      {
        TopTools_ListOfShape loe;
        loe.Append(E);
        myVEds.Bind(v, loe);
      }
    } // exv
  }
  return Standard_True;
}

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

const TopoDS_Shape& TopOpeBRepTool_CORRISO::S() const
{
  return myS;
}

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

const TopTools_ListOfShape& TopOpeBRepTool_CORRISO::Eds() const
{
  return myEds;
}

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

Standard_Boolean TopOpeBRepTool_CORRISO::UVRep(const TopoDS_Edge&   E,
                                               TopOpeBRepTool_C2DF& C2DF) const
{
  Standard_Boolean isb = myERep2d.IsBound(E);
  if (!isb)
    return Standard_False;

  C2DF = myERep2d.Find(E);
  return Standard_True;
}

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

Standard_Boolean TopOpeBRepTool_CORRISO::SetUVRep(const TopoDS_Edge&         E,
                                                  const TopOpeBRepTool_C2DF& C2DF)
{
  Standard_Boolean isb = myERep2d.IsBound(E);
  if (!isb)
    return Standard_False;

  myERep2d.ChangeFind(E) = C2DF;
  return Standard_True;
}

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

Standard_Boolean TopOpeBRepTool_CORRISO::Connexity(const TopoDS_Vertex&  V,
                                                   TopTools_ListOfShape& Eds) const
{
  Standard_Boolean isb = myVEds.IsBound(V);
  if (!isb)
    return Standard_False;

  Eds = myVEds.Find(V);
  return Standard_True;
}

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

Standard_Boolean TopOpeBRepTool_CORRISO::SetConnexity(const TopoDS_Vertex&        V,
                                                      const TopTools_ListOfShape& Eds)
{
  Standard_Boolean isb = myVEds.IsBound(V);
  if (!isb)
    return Standard_False;

  myVEds.ChangeFind(V) = Eds;
  return Standard_True;
}

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

Standard_Boolean TopOpeBRepTool_CORRISO::UVClosed() const
{
#ifdef OCCT_DEBUG
  Standard_Boolean trc = TopOpeBRepTool_GettraceCORRISO();
  if (trc)
    std::cout << "** UVClosed" << std::endl;
#endif
  TopTools_DataMapOfOrientedShapeInteger lfyE;
  Standard_Integer                       nfybounds   = 3;
  Standard_Boolean                       stopatfirst = Standard_True;
  Standard_Boolean foundfaulty = EdgesWithFaultyUV(myEds, nfybounds, lfyE, stopatfirst);
  return !foundfaulty;
}

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

Standard_Real TopOpeBRepTool_CORRISO::Tol(const Standard_Integer I, const Standard_Real tol3d) const
{
  Standard_Real tol = (I == 1) ? myGAS.UResolution(tol3d) : myGAS.VResolution(tol3d);
  return tol;
}

// static Standard_Real FUN_getx(const TopoDS_Edge& E,
static Standard_Real FUN_getx(const TopoDS_Edge&,
                              const TopOpeBRepTool_C2DF& c2df,
                              const Standard_Boolean     uiso,
                              const Standard_Real        par)
{ // prequesitory : E is uviso
  gp_Pnt2d      uv = TopOpeBRepTool_TOOL::UVF(par, c2df);
  Standard_Real x  = (uiso) ? uv.Y() : uv.X();
  return x;
}

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

Standard_Boolean TopOpeBRepTool_CORRISO::PurgeFyClosingE(const TopTools_ListOfShape& ClEds,
                                                         TopTools_ListOfShape&       fyClEds) const
{
  fyClEds.Clear();
#ifdef OCCT_DEBUG
  Standard_Boolean trc = TopOpeBRepTool_GettraceCORRISO();
  if (trc)
    std::cout << "* PurgeFyClosingE" << std::endl;
#endif
  //  Standard_Real xperiod = myUclosed ? myUper : myVper;
  Standard_Real tttol  = 1.e-8;
  Standard_Real tttolS = BRep_Tool::Tolerance(myFref);
  Standard_Real tolu = Tol(1, tttolS), tolv = Tol(2, tttolS);
  Standard_Real tttuvF = Max(tolu, tolv);

  TopTools_IndexedMapOfOrientedShape mapcl;
  TopTools_ListIteratorOfListOfShape itce(ClEds);
  for (; itce.More(); itce.Next())
    mapcl.Add(itce.Value());

  //* one closing edge should be removed
  itce.Initialize(ClEds);
  TopTools_DataMapOfOrientedShapeInteger fyceds;
  Standard_Boolean                       found = EdgesWithFaultyUV(ClEds, 3, fyceds);
  if (!found)
    return Standard_False;

  if (fyceds.Extent() == 1)
  { // ivf == 3 : cto016G*
    TopTools_DataMapOfOrientedShapeInteger fyeds;

    EdgesWithFaultyUV(myEds, 3, fyeds);
    Standard_Integer nfy = fyeds.Extent();

    TopTools_DataMapIteratorOfDataMapOfOrientedShapeInteger itm(fyceds);
    const TopoDS_Edge&                                      cE = TopoDS::Edge(itm.Key());

    TopAbs_Orientation OocE   = TopAbs::Complement(cE.Orientation());
    Standard_Boolean   isoncE = mapcl.Contains(cE.Oriented(OocE));
    if (isoncE)
    {
      TopTools_Array1OfShape vcE(1, 2);
      TopOpeBRepTool_TOOL::Vertices(cE, vcE);
      TopAbs_Orientation  ocE     = cE.Orientation();
      Standard_Real       tttolcE = BRep_Tool::Tolerance(cE);
      Standard_Real       tttuvcE = Max(Tol(1, tttolcE), Tol(2, tttolcE));
      TopOpeBRepTool_C2DF cE2d;
      Standard_Boolean    isb = UVRep(cE, cE2d);
      if (!isb)
        return Standard_False; // NYIRAISE

      // isonOcE2d :
      // OcE (closing edge with complemented orientation):
      TopAbs_Orientation oOcE        = TopAbs::Complement(ocE);
      TopoDS_Shape       alocalShape = cE.Oriented(oOcE);
      TopoDS_Edge        OcE         = TopoDS::Edge(alocalShape);
      //      TopoDS_Edge OcE = TopoDS::Edge(cE.Oriented(oOcE));
      TopTools_Array1OfShape vOcE(1, 2);
      TopOpeBRepTool_TOOL::Vertices(OcE, vOcE);
      Standard_Real       tttolOcE = BRep_Tool::Tolerance(OcE);
      Standard_Real       tttuvOcE = Max(Tol(1, tttolOcE), Tol(2, tttolOcE));
      TopOpeBRepTool_C2DF OcE2d;
      Standard_Boolean    isOb = UVRep(OcE, OcE2d);
      if (!isOb)
        return Standard_False; // NYIRAISE

      Standard_Real parvce1 = TopOpeBRepTool_TOOL::ParE(1, cE);
      gp_Pnt2d      UVvce1  = TopOpeBRepTool_TOOL::UVF(parvce1, cE2d);

      Standard_Real parvOcE2 = TopOpeBRepTool_TOOL::ParE(2, OcE);
      gp_Pnt2d      UVvOcE2  = TopOpeBRepTool_TOOL::UVF(parvOcE2, OcE2d);
      Standard_Real tol      = Max(tttuvcE, tttuvOcE);
      isoncE                 = (UVvce1.Distance(UVvOcE2) < tol);
      if (isoncE && (nfy != 1))
      { // cto009L2
        return Standard_False;
      }
    }

    Standard_Integer ivf = itm.Value();
    if (ivf == 3)
    {
      fyClEds.Append(cE);
      return Standard_True;
    }
  }
  else if (fyceds.Extent() > 1)
  { // ivf == 1,2 : cto016E*
    // if {edges of fyceds} describe a closing edge with its first and last
    // uvbounds non connexed -> we do remove these edges
    Standard_Boolean hasinit = Standard_False;
    Standard_Boolean isou = Standard_False, isov = Standard_False;
    gp_Pnt2d         o2d;
    gp_Dir2d         d2d;
    Standard_Real    xinf = 1.e7, xsup = -1.e7; // faulty inf and sup bounds
    Standard_Boolean infdef = Standard_False, supdef = Standard_False;
    TopTools_DataMapIteratorOfDataMapOfOrientedShapeInteger itm(fyceds);
    for (; itm.More(); itm.Next())
    {
      const TopoDS_Edge&  cE = TopoDS::Edge(itm.Key());
      TopOpeBRepTool_C2DF c2df;
      Standard_Boolean    isb = UVRep(cE, c2df);
      if (!isb)
        return Standard_False; // NYIRAISE

      Standard_Integer ivf = itm.Value();
      Standard_Boolean isoux, isovx;
      gp_Pnt2d         o2dx;
      gp_Dir2d         d2dx;
      Standard_Boolean uvisox = TopOpeBRepTool_TOOL::UVISO(c2df, isoux, isovx, d2dx, o2dx);
      if (!uvisox)
        return Standard_False;

      if (hasinit)
      {
        Standard_Boolean onsamline = (isou && isoux) || (isov && isovx);
        if (!onsamline)
          return Standard_False;
      }
      if (!hasinit)
      {
        isou    = isoux;
        isov    = isovx;
        o2d     = o2dx;
        d2d     = d2dx;
        hasinit = Standard_True;
      }
      else
      {
        Standard_Boolean onsamline = Standard_False;
        if (isou && isoux)
        {
          Standard_Real du = o2d.X() - o2dx.X();
          onsamline        = (Abs(du) < tolu);
        }
        if (isov && isovx)
        {
          Standard_Real dv = o2d.Y() - o2dx.Y();
          onsamline        = (Abs(dv) < tolv);
        }
        if (!onsamline)
          return Standard_False;
      }
      for (Standard_Integer i = 1; i <= 2; i++)
      {
        Standard_Real    pari     = TopOpeBRepTool_TOOL::ParE(i, cE);
        Standard_Real    xi       = FUN_getx(cE, c2df, isou, pari);
        Standard_Boolean vifaulty = (ivf == i || ivf == 3);
        Standard_Boolean inff     = (xi < xinf);
        Standard_Boolean supl     = (xi > xsup);
        //	if (inff) xinf = (ivf == i || ivf == 3) ? xi : 1.e7;
        //	if (supl) xsup = (ivf == i || ivf == 3) ? xi : -1.e7;
        if (inff)
        {
          xinf   = xi;
          infdef = vifaulty;
        }
        if (supl)
        {
          xsup   = xi;
          supdef = vifaulty;
        }
      }
      fyClEds.Append(cE);
    } // itm
    Standard_Boolean toremove = infdef && supdef; // ie infx,supx are not "uv-connexed"
    if (!toremove)
      fyClEds.Clear();
  }
  if (!fyClEds.IsEmpty())
    return Standard_True; // keeping only one closing edge

  //* the 2 closing edges have they 2drep "confunded"
  itce.Initialize(ClEds);
  for (; itce.More(); itce.Next())
  {
    // cE :
    const TopoDS_Edge&     cE = TopoDS::Edge(itce.Value());
    TopTools_Array1OfShape vcE(1, 2);
    TopOpeBRepTool_TOOL::Vertices(cE, vcE);
    TopAbs_Orientation ocE = cE.Orientation();

    Standard_Real       tttolcE = BRep_Tool::Tolerance(cE);
    Standard_Real       tttuvcE = Max(Tol(1, tttolcE), Tol(2, tttolcE));
    TopOpeBRepTool_C2DF cE2d;
    Standard_Boolean    isb = UVRep(cE, cE2d);
    if (!isb)
      return Standard_False; // NYIRAISE
#ifdef OCCT_DEBUG
    Standard_Integer icE = STATIC_PURGE_mapeds.Add(cE);
    if (trc)
      std::cout << "? e" << icE << " :" << std::endl;
#endif

    // isonOcE2d :
    Standard_Boolean isonOcE2d = Standard_False;
    {
      // OcE (closing edge with complemented orientation):
      TopAbs_Orientation oOcE        = TopAbs::Complement(ocE);
      TopoDS_Shape       aLocalShape = cE.Oriented(oOcE);
      TopoDS_Edge        OcE         = TopoDS::Edge(aLocalShape);
      //      TopoDS_Edge OcE = TopoDS::Edge(cE.Oriented(oOcE));
      TopTools_Array1OfShape vOcE(1, 2);
      TopOpeBRepTool_TOOL::Vertices(OcE, vOcE);
      Standard_Boolean hasOcE = mapcl.Contains(OcE);
      if (!hasOcE)
        continue; // closing edge appears twice
      Standard_Real       tttolOcE = BRep_Tool::Tolerance(OcE);
      Standard_Real       tttuvOcE = Max(Tol(1, tttolOcE), Tol(2, tttolOcE));
      TopOpeBRepTool_C2DF OcE2d;
      Standard_Boolean    isOb = UVRep(OcE, OcE2d);
      if (!isOb)
        return Standard_False; // NYIRAISE

      Standard_Real parvce1 = TopOpeBRepTool_TOOL::ParE(1, cE);
      gp_Pnt2d      UVvce1  = TopOpeBRepTool_TOOL::UVF(parvce1, cE2d);

      Standard_Real parvOcE2 = TopOpeBRepTool_TOOL::ParE(2, OcE);
      gp_Pnt2d      UVvOcE2  = TopOpeBRepTool_TOOL::UVF(parvOcE2, OcE2d);
      Standard_Real tol      = Max(tttuvcE, tttuvOcE);
      isonOcE2d              = (UVvce1.Distance(UVvOcE2) < tol);
    }
    if (!isonOcE2d)
    {
#ifdef OCCT_DEBUG
      if (trc)
        std::cout << "-> valid edge" << std::endl;
#endif
      continue;
    }

    Standard_Integer nvcEok = 0;
    for (Standard_Integer ivce = 1; ivce <= 2; ivce++)
    {
      // <vce> (boundary of <cE>):
      const TopoDS_Vertex& vce = TopoDS::Vertex(vcE(ivce));
      TopTools_ListOfShape loe;
      isb = Connexity(vce, loe);

      if (!isb)
        return Standard_False; // NYIRAISE

      Standard_Real parvce = TopOpeBRepTool_TOOL::ParE(ivce, cE);
      gp_Pnt2d      UVvce  = TopOpeBRepTool_TOOL::UVF(parvce, cE2d);
#ifdef OCCT_DEBUG
      // recall in one wire, there are 2 vertices for one non-degenerated closing edge
      Standard_Integer ivmapv = STATIC_PURGE_mapv.Add(vce);
      if (trc)
      {
        std::cout << " connexity for v(" << ivce << ")=v" << ivmapv;
        FUN_tool_trace(UVvce);
      }
  #ifdef DRAW
      if (trc)
      {
        TCollection_AsciiString bb("uv_");
        bb += TCollection_AsciiString(ivmapv);
        FUN_tool_draw(bb, UVvce);
      }
  #endif
#endif
      Standard_Real tttolvce = BRep_Tool::Tolerance(vce);
      Standard_Real tttuvvce = Max(Tol(1, tttolvce), Tol(2, tttolvce));

      Standard_Boolean vceok = Standard_False;
      for (TopTools_ListIteratorOfListOfShape ite(loe); ite.More(); ite.Next())
      {
        const TopoDS_Edge& E = TopoDS::Edge(ite.Value());

#ifdef OCCT_DEBUG
        Standard_Integer iE = STATIC_PURGE_mapeds.Add(E);
        if (trc)
        {
          std::cout << "    : on e" << iE << std::endl;
        }
#endif
        //	if (E.IsSame(cE)) continue;
        if (mapcl.Contains(E))
          continue; // do NOT check connexity on closing edges
                    // xpu090399 cto016E1

        TopOpeBRepTool_C2DF E2d;
        Standard_Boolean    isB2 = UVRep(E, E2d);
        if (!isB2)
          return Standard_False; // NYIRAISE

        Standard_Real tttolE = BRep_Tool::Tolerance(E);
        Standard_Real tttuvE = Max(Tol(1, tttolE), Tol(2, tttolE));

        TopTools_Array1OfShape vE(1, 2);
        TopOpeBRepTool_TOOL::Vertices(E, vE);
        for (Standard_Integer ive = 1; ive <= 2; ive++)
        {

          const TopoDS_Vertex& ve    = TopoDS::Vertex(vE(ive));
          Standard_Boolean     samev = ve.IsSame(vce);
          if (!samev)
            continue;
          Standard_Real parve = TopOpeBRepTool_TOOL::ParE(ive, E);
          gp_Pnt2d      UVve  = TopOpeBRepTool_TOOL::UVF(parve, E2d);
#ifdef OCCT_DEBUG
          if (trc)
          {
            std::cout << "    ve(" << ive << ")";
            FUN_tool_trace(UVve);
          }
#endif
          if (ive == ivce)
            continue; // vertex FORWARD connexed to REVERSED one
          Standard_Real tttolve = BRep_Tool::Tolerance(ve);
          Standard_Real tttuvve = Max(Tol(1, tttolve), Tol(2, tttolve));

          tttol = Max(tttol, Max(tttuvF, Max(tttuvE, Max(tttuvcE, Max(tttuvve, tttuvvce)))));
          //	  Standard_Real dd = myUclosed ? (UVve.X()-UVvce.X()) : (UVve.Y()-UVvce.Y());
          //	  Standard_Boolean xok = (Abs(dd)<tttol) || (Abs(Abs(dd)-xperiod)<tttol);
          //	  if (xok) {
          Standard_Real    dd     = UVve.Distance(UVvce);
          Standard_Boolean sameuv = (dd < tttol);
          if (myUclosed)
          {
            Standard_Real xperiod = myUper;
            dd                    = (UVve.X() - UVvce.X());
            sameuv                = sameuv || (Abs(Abs(dd) - xperiod) < tttol);
          }
          if (myVclosed)
          {
            Standard_Real xperiod = myVper;
            dd                    = (UVve.Y() - UVvce.Y());
            sameuv                = sameuv || (Abs(Abs(dd) - xperiod) < tttol);
          }
          if (sameuv)
          {
            vceok = Standard_True;
#ifdef OCCT_DEBUG
            if (trc)
            {
              std::cout << " connexity->ok" << std::endl;
            }
#endif
          }
          break;
        } // ive=1..2
        if (vceok)
          break;
      } // ite(loe)

#ifdef OCCT_DEBUG
      if (trc && !vceok)
      {
        std::cout << " connexity->KO" << std::endl;
      }
#endif
      if (vceok)
        nvcEok++;
    } // ivce=1..2

    Standard_Boolean isfycE = (nvcEok == 0); // each bound is not connexed to any non-closed edge

#ifdef OCCT_DEBUG
    if (trc)
    {
      if (isfycE)
        std::cout << "-> faulty edge" << std::endl;
      else
        std::cout << "-> valid edge" << std::endl;
    }
#endif
    if (isfycE)
      fyClEds.Append(cE);
  } // itce
  return (!fyClEds.IsEmpty());
}

#define SPLITEDGE (0)
#define INCREASE (1)
#define DECREASE (-1)

static Standard_Integer FUN_tool_recadre(const Standard_Real minx,
                                         const Standard_Real maxx,
                                         const Standard_Real xfirst,
                                         const Standard_Real xlast,
                                         const Standard_Real tolx,
                                         Standard_Boolean&   maxsup)
{
  Standard_Integer recadre = 10;                                              // INIT
  Standard_Boolean maxinf  = (maxx < xfirst + tolx);                          // permissive
  Standard_Boolean mininf  = (minx < xfirst - tolx);                          //
  maxsup                   = (maxx > xlast + tolx);                           //
  Standard_Boolean minsup  = (minx > xlast - tolx);                           // permissive
  Standard_Boolean maxok   = (xfirst - tolx < maxx) && (maxx < xlast + tolx); // permissive
  Standard_Boolean minok   = (xfirst - tolx < minx) && (minx < xlast + tolx); // permissive

  if (maxinf)
    recadre = INCREASE;
  else if (minsup)
    recadre = DECREASE;
  else if (mininf && maxok)
    recadre = SPLITEDGE;
  else if (minok && maxsup)
    recadre = SPLITEDGE;
  return recadre;
}

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

Standard_Integer TopOpeBRepTool_CORRISO::EdgeOUTofBoundsUV(const TopoDS_Edge&     E,
                                                           const Standard_Boolean onU,
                                                           const Standard_Real    tolx,
                                                           Standard_Real&         parspE) const
{
  Standard_Integer recadre = 10;
  parspE                   = -1.e7; // INIT
  Standard_Integer isb     = myERep2d.IsBound(E);
  if (!isb)
    return Standard_False;

  const TopOpeBRepTool_C2DF&  C2DF = myERep2d.Find(E);
  Standard_Real               f, l, tol;
  const Handle(Geom2d_Curve)& PC = C2DF.PC(f, l, tol);

  Standard_Real xfirst = onU ? myGAS.FirstUParameter() : myGAS.FirstVParameter();
  // clang-format off
  Standard_Real xlast   = onU ? myGAS.LastUParameter() : myGAS.LastVParameter(); // xlast=xfirst+xperiod
  // clang-format on
  Standard_Real xperiod = onU ? myUper : myVper;

  Standard_Boolean isou, isov;
  gp_Pnt2d         o2d;
  gp_Dir2d         d2d;
  Standard_Boolean iso = TopOpeBRepTool_TOOL::UVISO(PC, isou, isov, d2d, o2d);

  if (iso)
  { // 2drep(E,myFref) is an ISO
    // -------------------------
    Standard_Boolean inX = (onU && isou) || ((!onU) && isov);
    if (inX)
    {
      // faulty u-iso : upar out of ubound
      Standard_Real    xpar     = onU ? o2d.X() : o2d.Y();
      Standard_Boolean toosmall = (xpar < xfirst - tolx);
      Standard_Boolean tobig    = (xpar > xfirst + xperiod + tolx);

      if (toosmall)
        recadre = INCREASE;
      if (tobig)
        recadre = DECREASE;
      return recadre;
    } // inX
    Standard_Boolean inY = (onU && isov) || ((!onU) && isou); // inY = !inX
    if (inY)
    {
      // faulty u-iso : vpar describes (minv,maxv) out of vbounds
      // PC describes [minx,maxx] in x-space
      // recadre = INCREASE : maxx < 0.
      //           DECREASE : minx > 2PI
      //           SPLITEDGE : minx<0.<maxx || minx<2PI<maxx
      Standard_Real    d2ddir  = onU ? d2d.Y() : d2d.X();
      Standard_Boolean reverse = (d2ddir < 0.);
      Standard_Real    xfactor = reverse ? -1. : 1.;
      Standard_Real    max     = reverse ? f : l;
      Standard_Real    min     = reverse ? l : f;
      gp_Pnt2d         maxuv   = PC->Value(max);
      gp_Pnt2d         minuv   = PC->Value(min);

      Standard_Real maxx = onU ? maxuv.X() : maxuv.Y();
      Standard_Real minx = onU ? minuv.X() : minuv.Y();

      Standard_Boolean maxsup;
      recadre = FUN_tool_recadre(minx, maxx, xfirst, xlast, tolx, maxsup);
      if (recadre == SPLITEDGE)
      {
        Standard_Real xbound = maxsup ? xperiod : 0.;
        parspE               = max - xfactor * (maxx - xbound);
      }
      return recadre;
    } // inY
  } // iso
  else
  { // 2drep(E, myFref) is NOT an iso
    // ------------------------------
    Bnd_Box2d           Bn2d;
    Geom2dAdaptor_Curve GC2d(PC, f, l);
    Standard_Real       tolE   = BRep_Tool::Tolerance(E);
    Standard_Real       toladd = Max(tolE, tol);
    BndLib_Add2dCurve::Add(GC2d, toladd, Bn2d);
    Standard_Real umin, vmin, umax, vmax;
    Bn2d.Get(umin, vmin, umax, vmax);
    Standard_Real    xmin = onU ? umin : vmin;
    Standard_Real    xmax = onU ? umax : vmax;
    Standard_Boolean maxsup;
    recadre = FUN_tool_recadre(xmin, xmax, xfirst, xlast, tolx, maxsup);
    if (recadre == SPLITEDGE)
    {
      // ================================================================
      // NYIxpu271198 : intersection PC avec xiso (x= maxsup? xperiod: 0.)
      // ================================================================
      return 10;
    }
    return recadre;
  }
  return recadre;
}

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

Standard_Boolean TopOpeBRepTool_CORRISO::EdgesOUTofBoundsUV(
  const TopTools_ListOfShape&             EdsToCheck,
  const Standard_Boolean                  onU,
  const Standard_Real                     tolx,
  TopTools_DataMapOfOrientedShapeInteger& FyEds) const
{
  FyEds.Clear();
  TopTools_ListIteratorOfListOfShape it(EdsToCheck);
  for (; it.More(); it.Next())
  {
    const TopoDS_Edge& E       = TopoDS::Edge(it.Value());
    Standard_Real      sspar   = -1.e7;
    Standard_Integer   recadre = EdgeOUTofBoundsUV(E, onU, tolx, sspar);
    if (recadre == SPLITEDGE)
      FUN_Raise();
    if (recadre == INCREASE)
      FyEds.Bind(E, 1);
    if (recadre == DECREASE)
      FyEds.Bind(E, -1);
  }
  return (!FyEds.IsEmpty());
}

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

Standard_Boolean TopOpeBRepTool_CORRISO::EdgeWithFaultyUV(const TopoDS_Edge& E,
                                                          Standard_Integer&  Ivfaulty) const
{
#ifdef OCCT_DEBUG
  Standard_Boolean trc = TopOpeBRepTool_GettraceCORRISO();
  Standard_Integer iE  = STATIC_PURGE_mapeds.Add(E);
  if (trc)
    std::cout << "? e" << iE << " :" << std::endl;
#endif
  Ivfaulty             = 0;
  Standard_Real tttol  = 1.e-8;
  Standard_Real tttolF = BRep_Tool::Tolerance(TopoDS::Face(myFref));
  Standard_Real tttuvF = Max(Tol(1, tttolF), Tol(2, tttolF));
  Standard_Real tttolE = BRep_Tool::Tolerance(E);
  Standard_Real tttuvE = Max(Tol(1, tttolE), Tol(2, tttolE));

  TopAbs_Orientation oE = E.Orientation();
  if (M_INTERNAL(oE) || M_EXTERNAL(oE))
    return Standard_False;

  TopTools_Array1OfShape vEs(1, 2);
  TopOpeBRepTool_TOOL::Vertices(E, vEs);
  Standard_Boolean closed = vEs(1).IsSame(vEs(2));
  if (closed)
  {
#ifdef OCCT_DEBUG
    if (trc)
    {
      std::cout << "closed -> valid edge" << std::endl;
    }
#endif
    return Standard_False; // closed edge is assumed valid
  }

  Standard_Integer nfyv = 0;
  for (Standard_Integer ivE = 1; ivE <= 2; ivE++)
  {

    // <vE> (boundary of <E>):
    const TopoDS_Vertex& vE    = TopoDS::Vertex(vEs(ivE));
    Standard_Real        parvE = TopOpeBRepTool_TOOL::ParE(ivE, E);
    TopOpeBRepTool_C2DF  C2DF;
    Standard_Boolean     isb = UVRep(E, C2DF);
    if (!isb)
      return Standard_False; // NYIRAISE
    gp_Pnt2d UVvE = TopOpeBRepTool_TOOL::UVF(parvE, C2DF);
#ifdef OCCT_DEBUG
    // recall in one wire, there are 2 vertices for one non-degenerated closing edge
    Standard_Integer ivmapv = STATIC_PURGE_mapv.Add(vE);
    if (trc)
    {
      std::cout << " connexity for v(" << ivE << ")=v" << ivmapv;
      FUN_tool_trace(UVvE);
    }
  #ifdef DRAW
    if (trc)
    {
      TCollection_AsciiString bb("uv_");
      bb += TCollection_AsciiString(ivmapv);
      FUN_tool_draw(bb, UVvE);
    }
  #endif
#endif

    Standard_Real tttolvE = BRep_Tool::Tolerance(vE);
    Standard_Real tttuvvE = Max(Tol(1, tttolvE), Tol(2, tttolvE));

    Standard_Boolean isbound = myVEds.IsBound(vE);
    if (!isbound)
    {
      FUN_RaiseError();
      return Standard_False;
    }

    // <vEok> :
    Standard_Boolean            vEok = Standard_False;
    const TopTools_ListOfShape& loe  = myVEds.Find(vE);

    for (TopTools_ListIteratorOfListOfShape ite(loe); ite.More(); ite.Next())
    {
      const TopoDS_Edge& e  = TopoDS::Edge(ite.Value());
      TopAbs_Orientation oe = e.Orientation();

#ifdef OCCT_DEBUG
      Standard_Integer ie = STATIC_PURGE_mapeds.Add(e);
      if (trc)
      {
        std::cout << "    : on e" << ie << std::endl;
      }
#endif

      if (e.IsSame(E))
        continue;
      if (M_INTERNAL(oe) || M_EXTERNAL(oe))
        continue;

      Standard_Boolean isBound = myERep2d.IsBound(e);
      if (!isBound)
      {
        FUN_RaiseError();
        return Standard_False;
      }
      const TopOpeBRepTool_C2DF& aC2DF = myERep2d.Find(e);

      TopTools_Array1OfShape ves(1, 2);
      TopOpeBRepTool_TOOL::Vertices(e, ves);
      for (Standard_Integer ive = 1; ive <= 2; ive++)
      {
        const TopoDS_Vertex& ve    = TopoDS::Vertex(ves(ive));
        Standard_Boolean     samev = ve.IsSame(vE);
        if (!samev)
          continue;

        Standard_Real pare = TopOpeBRepTool_TOOL::ParE(ive, e);
        gp_Pnt2d      UVve = TopOpeBRepTool_TOOL::UVF(pare, aC2DF);
#ifdef OCCT_DEBUG
        if (trc)
        {
          std::cout << "    ve(" << ive << ")";
          FUN_tool_trace(UVve);
        }
#endif
        if (ive == ivE)
          continue;

        Standard_Real tttolve = BRep_Tool::Tolerance(ve);
        Standard_Real tttuvve = Max(Tol(1, tttolve), Tol(2, tttolve));

        tttol = Max(tttol, Max(tttuvF, Max(tttuvE, Max(tttuvE, Max(tttuvve, tttuvvE)))));
        Standard_Boolean isequal = UVvE.IsEqual(UVve, tttol);
        if (isequal)
        {
          vEok = Standard_True;
#ifdef OCCT_DEBUG
          if (trc)
          {
            std::cout << " connexity->ok" << std::endl;
          }
#endif
          break;
        }
      } // ive
      if (vEok)
        break;
    } // ite(loe)

    if (!vEok)
    {
      nfyv++;
      Ivfaulty = ivE;
    }
#ifdef OCCT_DEBUG
    if (trc && !vEok)
    {
      std::cout << " connexity->KO" << std::endl;
    }
#endif

  } // ivE = 1..2
  if (nfyv == 2)
    Ivfaulty = 3;
#ifdef OCCT_DEBUG
  if (trc)
  {
    if (Ivfaulty == 0)
      std::cout << "-> valid edge" << std::endl;
    else
      std::cout << "-> faulty edge" << std::endl;
  }
#endif
  return (Ivfaulty != 0);
}

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

Standard_Boolean TopOpeBRepTool_CORRISO::EdgesWithFaultyUV(
  const TopTools_ListOfShape&             EdsToCheck,
  const Standard_Integer                  nfybounds,
  TopTools_DataMapOfOrientedShapeInteger& FyEds,
  const Standard_Boolean                  stopatfirst) const
{
  FyEds.Clear();
#ifdef OCCT_DEBUG
  Standard_Integer ifault = 0;
  Standard_Boolean trc    = TopOpeBRepTool_GettraceCORRISO();
  if (trc)
    std::cout << std::endl << "* EdgesWithFaultyUV" << std::endl;
#endif

  // fF's checking :
  // -----------------
  // An edge is valid if the first and last vertices are valid:
  // vertex <vEchk> is valid if there is an edge with bound <ve> such that :
  //   <vEchk> and <ve> share same UV geometry
  //   <vEchk> and <ve> are of opposite orientation.
  TopTools_ListIteratorOfListOfShape itchk(EdsToCheck);
  for (; itchk.More(); itchk.Next())
  {

    const TopoDS_Edge& Echk     = TopoDS::Edge(itchk.Value());
    Standard_Integer   Ivfaulty = 0;
    Standard_Boolean   faulty   = EdgeWithFaultyUV(Echk, Ivfaulty);
    if (!faulty)
      continue;
    Standard_Integer nfyv = (Ivfaulty == 3) ? 2 : 1;

#ifdef OCCT_DEBUG
    ifault++;
    if (trc)
      std::cout << "e" << STATIC_PURGE_mapeds.FindIndex(Echk) << " has ifyv=" << Ivfaulty
                << std::endl;
  #ifdef DRAW
    if (trc)
    {
      TCollection_AsciiString aa("fault");
      FUN_tool_draw(aa, Echk, ifault);
    }
  #endif
#endif

    Standard_Boolean found = Standard_False;
    if (nfybounds == 1)
      found = (nfyv == nfybounds);
    else if (nfybounds == 2)
      found = (nfyv == nfybounds);
    else if (nfybounds == 3)
      found = (nfyv > 0);

    if (found)
    {
      FyEds.Bind(Echk, Ivfaulty);
      if (stopatfirst)
        return Standard_True;
    }
  } // itchk
  Standard_Integer n = FyEds.Extent(); // DEB
  return (n != 0);
}

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

Standard_Boolean TopOpeBRepTool_CORRISO::EdgeWithFaultyUV(const TopTools_ListOfShape& EdsToCheck,
                                                          const Standard_Integer      nfybounds,
                                                          TopoDS_Shape&               fyE,
                                                          Standard_Integer&           Ifaulty) const
{
  TopTools_DataMapOfOrientedShapeInteger FyEds;
  Standard_Boolean found = EdgesWithFaultyUV(EdsToCheck, nfybounds, FyEds, Standard_True);
  if (!found)
    return Standard_False;

  TopTools_DataMapIteratorOfDataMapOfOrientedShapeInteger itm(FyEds);
  fyE     = itm.Key();
  Ifaulty = itm.Value();
  return Standard_True;
}

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

Standard_Boolean TopOpeBRepTool_CORRISO::TrslUV(const Standard_Boolean                        onU,
                                                const TopTools_DataMapOfOrientedShapeInteger& FyEds)
{
  gp_Vec2d tt2d;
  if (onU)
  {
    Standard_Real uper;
    Refclosed(1, uper);
    if (!uper)
      return Standard_False;
    tt2d = gp_Vec2d(uper, 0.);
  }
  else
  {
    Standard_Real vper;
    Refclosed(2, vper);
    if (!vper)
      return Standard_False;
    tt2d = gp_Vec2d(0., vper);
  }
  TopTools_DataMapIteratorOfDataMapOfOrientedShapeInteger itm(FyEds);
  for (; itm.More(); itm.Next())
  {
    const TopoDS_Edge&  E = TopoDS::Edge(itm.Key());
    TopOpeBRepTool_C2DF C2DF;
    Standard_Boolean    isb = UVRep(E, C2DF);
    if (!isb)
      return Standard_False;

    Standard_Integer itt = itm.Value();
    if (itt == SPLITEDGE)
      return Standard_False;
    else if (itt == INCREASE)
      TopOpeBRepTool_TOOL::TrslUV(tt2d, C2DF);
    else if (itt == DECREASE)
      TopOpeBRepTool_TOOL::TrslUV(tt2d.Multiplied(-1.), C2DF);
    else
      return Standard_False;
    SetUVRep(E, C2DF);
  }
  return Standard_True;
}

// modif in BRep_Builder031298
/*static void FUN_tool_correctdgE(const TopoDS_Edge& E)
{
  const Handle(BRep_TEdge)& TE = *((Handle(BRep_TEdge)*) &E.TShape());
  BRep_ListOfCurveRepresentation& lcr = TE->ChangeCurves();
  BRep_ListIteratorOfListOfCurveRepresentation itcr(lcr);
  while (itcr.More()) {
    Handle(BRep_GCurve) GC = Handle(BRep_GCurve)::DownCast(itcr.Value());
    if (!GC.IsNull()) {
      if (GC->IsCurve3D()) lcr.Remove(itcr);
      else itcr.Next();
    }
  }
}*/

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

Standard_Boolean TopOpeBRepTool_CORRISO::GetnewS(TopoDS_Face& newS) const
{
  newS.Nullify();
  if (myS.ShapeType() != TopAbs_FACE)
    return Standard_False;

  newS = TopoDS::Face(myS);
  BRep_Builder BB;

  TopTools_ListIteratorOfListOfShape it(myEds);
  for (; it.More(); it.Next())
  {
    TopoDS_Edge         E    = TopoDS::Edge(it.Value());
    TopAbs_Orientation  oriE = E.Orientation();
    TopOpeBRepTool_C2DF C2DF;
    Standard_Boolean    isb = UVRep(E, C2DF);
    if (!isb)
      return Standard_False;

    Standard_Real               f, l, tol;
    const Handle(Geom2d_Curve)& PC = C2DF.PC(f, l, tol);
    Handle(Geom2d_TrimmedCurve) cu = new Geom2d_TrimmedCurve(PC, f, l);

    TopoDS_Shape aLocalShape = E.Oriented(TopAbs::Complement(oriE));
    TopoDS_Edge  Err         = TopoDS::Edge(aLocalShape);
    //    TopoDS_Edge Err = TopoDS::Edge(E.Oriented(TopAbs::Complement(oriE)));
    TopOpeBRepTool_C2DF C2DFrr;
    Standard_Boolean    isclo = UVRep(Err, C2DFrr);

    //    Standard_Boolean isdgE = BRep_Tool::Degenerated(E);
    // !BUC60380 : degenerated edge has a 3d curve !!, remove it
    //    if (isdgE) {FUN_tool_correctdgE(E);}

    if (isclo)
    {
      Standard_Real               frr, lrr, tolrr;
      const Handle(Geom2d_Curve)& PCrr = C2DFrr.PC(frr, lrr, tolrr);
      Handle(Geom2d_TrimmedCurve) curr = new Geom2d_TrimmedCurve(PCrr, frr, lrr);
      if (M_FORWARD(oriE))
        BB.UpdateEdge(E, cu, curr, newS, tol);
    }
    else
      BB.UpdateEdge(E, cu, newS, tol);
  }
  return Standard_True;
}

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

// Standard_Boolean TopOpeBRepTool_CORRISO::AddNewConnexity(const TopoDS_Vertex& V,
Standard_Boolean TopOpeBRepTool_CORRISO::AddNewConnexity(const TopoDS_Vertex&, const TopoDS_Edge& E)
{
  // <myERep2d> :
  Standard_Boolean isb = myERep2d.IsBound(E);
  if (!isb)
  {
    Handle(Geom2d_Curve) PC;
    Standard_Real        f, l, tol;
    Standard_Boolean     hasold = FC2D_HasOldCurveOnSurface(E, myFref, PC);
    PC                          = FC2D_EditableCurveOnSurface(E, myFref, f, l, tol);
    if (!hasold)
      FC2D_AddNewCurveOnSurface(PC, E, myFref, f, l, tol);
    if (PC.IsNull())
      return Standard_False;
    TopOpeBRepTool_C2DF C2DF(PC, f, l, tol, myFref);
    myERep2d.Bind(E, C2DF);
  }

  // <myEds> :
  if (!isb)
    myEds.Append(E);

  // <myVEds> :
  TopExp_Explorer exv(E, TopAbs_VERTEX);
  for (; exv.More(); exv.Next())
  {
    const TopoDS_Vertex& v    = TopoDS::Vertex(exv.Current());
    Standard_Boolean     isbb = myVEds.IsBound(v);
    if (isbb)
      myVEds.ChangeFind(v).Append(E);
    else
    {
      TopTools_ListOfShape loe;
      loe.Append(E);
      myVEds.Bind(v, loe);
    }
  } // exv
  return Standard_True;
}

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

// Standard_Boolean TopOpeBRepTool_CORRISO::RemoveOldConnexity(const TopoDS_Vertex& V,
Standard_Boolean TopOpeBRepTool_CORRISO::RemoveOldConnexity(const TopoDS_Vertex&,
                                                            const TopoDS_Edge& E)
{
  // <myERep2d> :
  Standard_Boolean isb = myERep2d.IsBound(E);
  if (isb)
    myERep2d.UnBind(E);

  // <myEds> :
  if (isb)
  {
    TopTools_ListIteratorOfListOfShape it(myEds);
    while (it.More())
    {
      if (it.Value().IsEqual(E))
      {
        myEds.Remove(it);
        break;
      }
      else
        it.Next();
    }
  }

  // <myVEds> :
  Standard_Boolean done = Standard_False;
  TopExp_Explorer  exv(E, TopAbs_VERTEX);
  for (; exv.More(); exv.Next())
  {
    const TopoDS_Vertex& v        = TopoDS::Vertex(exv.Current());
    Standard_Boolean     isBoundV = myVEds.IsBound(v);
    if (!isBoundV)
      return Standard_False;
    TopTools_ListOfShape&              loe = myVEds.ChangeFind(v);
    TopTools_ListIteratorOfListOfShape ite(loe);
    while (ite.More())
    {
      if (ite.Value().IsEqual(E))
      {
        done = Standard_True;
        loe.Remove(ite);
        break;
      }
      else
        ite.Next();
    }
  } // exv
  return done;
}
