// Created on: 1998-07-22
// Created by: Philippe MANGIN
// 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 <BRep_Builder.hxx>
#include <BRep_Tool.hxx>
#include <BRepAdaptor_CompCurve.hxx>
#include <BRepBuilderAPI_Copy.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include <BRepClass3d_SolidClassifier.hxx>
#include <BRepFill.hxx>
#include <BRepFill_ACRLaw.hxx>
#include <BRepFill_CompatibleWires.hxx>
#include <BRepFill_Edge3DLaw.hxx>
#include <BRepFill_EdgeOnSurfLaw.hxx>
#include <BRepFill_LocationLaw.hxx>
#include <BRepFill_NSections.hxx>
#include <BRepFill_PipeShell.hxx>
#include <BRepFill_Section.hxx>
#include <BRepFill_SectionLaw.hxx>
#include <BRepFill_SectionPlacement.hxx>
#include <BRepFill_ShapeLaw.hxx>
#include <BRepFill_Sweep.hxx>
#include <BRepGProp.hxx>
#include <BRepLib_MakeFace.hxx>
#include <GeomAdaptor_Surface.hxx>
#include <GeomFill_ConstantBiNormal.hxx>
#include <GeomFill_CorrectedFrenet.hxx>
#include <GeomFill_CurveAndTrihedron.hxx>
#include <GeomFill_DiscreteTrihedron.hxx>
#include <GeomFill_Fixed.hxx>
#include <GeomFill_Frenet.hxx>
#include <GeomFill_GuideTrihedronAC.hxx>
#include <GeomFill_GuideTrihedronPlan.hxx>
#include <GeomFill_LocationGuide.hxx>
#include <GeomFill_SectionLaw.hxx>
#include <GeomFill_TrihedronLaw.hxx>
#include <gp_Ax2.hxx>
#include <gp_Dir.hxx>
#include <gp_Trsf.hxx>
#include <gp_Vec.hxx>
#include <GProp_GProps.hxx>
#include <IntCurveSurface_HInter.hxx>
#include <IntCurveSurface_IntersectionPoint.hxx>
#include <Law_Interpol.hxx>
#include <Precision.hxx>
#include <Standard_ConstructionError.hxx>
#include <Standard_NotImplemented.hxx>
#include <Standard_Type.hxx>
#include <StdFail_NotDone.hxx>
#include <TColgp_HArray1OfPnt2d.hxx>
#include <TColStd_SequenceOfInteger.hxx>
#include <TopExp.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Iterator.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Shell.hxx>
#include <TopoDS_Solid.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Wire.hxx>
#include <TopTools_SequenceOfShape.hxx>

#include <stdio.h>
IMPLEMENT_STANDARD_RTTIEXT(BRepFill_PipeShell,Standard_Transient)

//Specification Guide
#ifdef DRAW
#include <Draw.hxx>
#include <DrawTrSurf.hxx>
#include <DBRep.hxx>
static Standard_Boolean Affich = 0;
#endif

#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
#include <TopTools_DataMapOfIntegerShape.hxx>


static Standard_Boolean BuildBoundaries(const BRepFill_Sweep&               theSweep,
					const Handle(BRepFill_SectionLaw)&  theSection,
					TopoDS_Shape&                       theBottom,
					TopoDS_Shape&                       theTop);

//=======================================================================
//function :  ComputeSection
//purpose  : Construct an intermediary section
//=======================================================================

static Standard_Boolean ComputeSection(const TopoDS_Wire& W1,
                                       const TopoDS_Wire& W2,
                                       const Standard_Real p1,
                                       const Standard_Real p2,
                                       TopoDS_Wire& Wres)
{
  TColStd_SequenceOfReal SR;
  TopTools_SequenceOfShape SSh;
  SR.Clear();
  SR.Append(0.);
  SR.Append(1.);
  SSh.Clear();
  SSh.Append(W1);
  SSh.Append(W2);
  BRepFill_CompatibleWires CW(SSh);
  CW.SetPercent(0.1);
  CW.Perform();
  if (!CW.IsDone()) throw StdFail_NotDone("Uncompatible wires");
  GeomFill_SequenceOfTrsf EmptyTrsfs;
  Handle(BRepFill_NSections) SL = new (BRepFill_NSections) (CW.Shape(),EmptyTrsfs,SR,0.,1.);
  Standard_Real US = p1/(p1+p2);
  SL->D0(US, Wres);
  return Standard_True;
}

				      

//=======================================================================
//function : PerformTransition
//purpose  : Modify a law of location depending on Transition
//=======================================================================

static void PerformTransition(const BRepFill_TransitionStyle Mode,
			      Handle(BRepFill_LocationLaw)& Loc,
			      const Standard_Real angmin)
{
 if (!Loc.IsNull()) {
   Loc->DeleteTransform();
   if (Mode == BRepFill_Modified) Loc->TransformInG0Law();
   else  Loc->TransformInCompatibleLaw(angmin);
 } 
}
//=======================================================================
//function :  PerformPlan
//purpose  : Construct a plane of filling if exists
//=======================================================================

static Standard_Boolean PerformPlan(TopoDS_Shape& S)
{
  Standard_Boolean isDegen = Standard_True;
  TopExp_Explorer explo(S, TopAbs_EDGE);
  for (; explo.More(); explo.Next())
    {
      const TopoDS_Edge& anEdge = TopoDS::Edge(explo.Current());
      if (!BRep_Tool::Degenerated(anEdge))
	isDegen = Standard_False;
    }
  if (isDegen)
    {
      S.Nullify();
      return Standard_True;
    }

  TopoDS_Wire W = TopoDS::Wire(S);
  Standard_Boolean Ok = Standard_False;
  if (!W.IsNull()) {
    BRepLib_MakeFace mkplan(W, Standard_True);
    if (mkplan.IsDone()) {
      S = mkplan.Face();
      Ok = Standard_True;
    }
  }
 return Ok;
}

//=============================================================================
//function :  IsSameOriented
//purpose  : Checks whether aFace is oriented to the same side as aShell or not
//=============================================================================

static Standard_Boolean IsSameOriented(const TopoDS_Shape& aFace,
				       const TopoDS_Shape& aShell)
{
  TopExp_Explorer Explo(aFace, TopAbs_EDGE);
  TopoDS_Shape anEdge = Explo.Current();
  TopAbs_Orientation Or1 = anEdge.Orientation();

  TopTools_IndexedDataMapOfShapeListOfShape EFmap;
  TopExp::MapShapesAndAncestors( aShell, TopAbs_EDGE, TopAbs_FACE, EFmap );

  const TopoDS_Shape& AdjacentFace = EFmap.FindFromKey(anEdge).First();
  TopoDS_Shape theEdge;
  for (Explo.Init(AdjacentFace, TopAbs_EDGE); Explo.More(); Explo.Next())
    {
      theEdge = Explo.Current();
      if (theEdge.IsSame(anEdge))
	break;
    }

  TopAbs_Orientation Or2 = theEdge.Orientation();
  if (Or1 == Or2)
    return Standard_False;
  return Standard_True;
}
//=======================================================================
//function : BRepFill_PipeShell
//purpose  : 
//=======================================================================
BRepFill_PipeShell::BRepFill_PipeShell(const TopoDS_Wire& Spine)
                      :  mySpine(Spine), 
                         myForceApproxC1(Standard_False),
                         myIsAutomaticLaw(Standard_False),
                         myTrihedron(GeomFill_IsCorrectedFrenet),
                         myTransition(BRepFill_Modified),
                         myStatus(GeomFill_PipeOk)
{
  myLocation.Nullify();
  mySection.Nullify();
  myLaw.Nullify();
  SetTolerance();

  myMaxDegree = 11;
  myMaxSegments = 100;

  // Attention to closed non-declared wire !
  if (!mySpine.Closed()) {
    TopoDS_Vertex Vf, Vl;
    TopExp::Vertices(mySpine, Vf, Vl);
    if (Vf.IsSame(Vl)) mySpine.Closed(Standard_True);
  }  
}

//=======================================================================
//function : Set
//purpose  : Define a law of Frenet (Correct)
//=======================================================================
 void BRepFill_PipeShell::Set(const Standard_Boolean IsFrenet) 
{
  Handle(GeomFill_TrihedronLaw) TLaw;
  if (IsFrenet) {
    myTrihedron = GeomFill_IsFrenet;
    TLaw = new (GeomFill_Frenet) ();
  }
  else {
    myTrihedron = GeomFill_IsFrenet;
    TLaw = new (GeomFill_CorrectedFrenet) ();
  }
  Handle(GeomFill_CurveAndTrihedron) Loc = 
    new (GeomFill_CurveAndTrihedron) (TLaw);
  myLocation = new (BRepFill_Edge3DLaw) (mySpine, Loc);
  mySection.Nullify(); //It is required to relocalize sections.
}

//=======================================================================
//function : SetDiscrete
//purpose  : Define a law of Discrete Trihedron
//=======================================================================
 void BRepFill_PipeShell::SetDiscrete() 
{
  Handle(GeomFill_TrihedronLaw) TLaw;

  myTrihedron = GeomFill_IsDiscreteTrihedron;
  TLaw = new (GeomFill_DiscreteTrihedron) ();

  Handle(GeomFill_CurveAndTrihedron) Loc = 
    new (GeomFill_CurveAndTrihedron) (TLaw);
  myLocation = new (BRepFill_Edge3DLaw) (mySpine, Loc);
  mySection.Nullify(); //It is required to relocalize sections.
}

//=======================================================================
//function : Set
//purpose  : Define a law Constant
//=======================================================================
 void BRepFill_PipeShell::Set(const gp_Ax2& Axe) 
{
  myTrihedron = GeomFill_IsFixed;
  gp_Vec V1, V2;
  V1.SetXYZ(Axe.Direction().XYZ());
  V2.SetXYZ(Axe.XDirection().XYZ());
  Handle(GeomFill_Fixed) TLaw = new (GeomFill_Fixed) (V1, V2);
  Handle(GeomFill_CurveAndTrihedron) Loc = 
    new (GeomFill_CurveAndTrihedron) (TLaw);
  myLocation = new (BRepFill_Edge3DLaw) (mySpine, Loc); 
  mySection.Nullify(); //It is required to relocalize sections.
}

//=======================================================================
//function : Set
//purpose  : Construct a law of location of binormal fixed type
//=======================================================================
 void BRepFill_PipeShell::Set(const gp_Dir& BiNormal) 
{
  myTrihedron = GeomFill_IsConstantNormal;

  Handle(GeomFill_ConstantBiNormal) TLaw = 
    new (GeomFill_ConstantBiNormal) (BiNormal);
  Handle(GeomFill_CurveAndTrihedron) Loc = 
    new (GeomFill_CurveAndTrihedron) (TLaw);
  myLocation = new (BRepFill_Edge3DLaw) (mySpine, Loc); 
  mySection.Nullify(); //Il faut relocaliser les sections.
}

//=======================================================================
//function : Set
//purpose  : Construct a law of location of Darboux type
//=======================================================================
 Standard_Boolean BRepFill_PipeShell::Set(const TopoDS_Shape& SpineSupport) 
{
 Standard_Boolean B;

  // A special law of location is required
 Handle(BRepFill_EdgeOnSurfLaw) loc = 
   new (BRepFill_EdgeOnSurfLaw) (mySpine, SpineSupport);
 B = loc->HasResult();
 if (B) {
   myLocation = loc; 
   myTrihedron = GeomFill_IsDarboux;
   mySection.Nullify(); //It is required to relocalize the sections.
 }
 return B;
}

//=======================================================================
//function : Set
//purpose  : Defines a lawv with help of a guided contour
//=======================================================================
 void BRepFill_PipeShell::Set(const TopoDS_Wire& AuxiliarySpine,
			      const Standard_Boolean CurvilinearEquivalence,
			      const BRepFill_TypeOfContact KeepContact) 
{  
  // Reorganization of the guide (pb of orientation and origin)
  TopoDS_Wire TheGuide;
  TheGuide =  AuxiliarySpine;
  Standard_Boolean SpClose = mySpine.Closed(), 
                   GuideClose = AuxiliarySpine.Closed();

  if (KeepContact == BRepFill_ContactOnBorder)
    myIsAutomaticLaw = Standard_True;
  
  if (!SpClose && !GuideClose) {
    // Case open reorientation of the guide
    TopoDS_Wire sp = mySpine;
    TopTools_SequenceOfShape Seq;
    Seq.Append(sp);
    Seq.Append(TheGuide);
    BRepFill_CompatibleWires CW(Seq);
    CW.SetPercent(0.1);
    CW.Perform();
    if (!CW.IsDone()) throw StdFail_NotDone("Uncompatible wires");
    TheGuide = TopoDS::Wire(CW.Shape().Value(2));
  }
  else if (GuideClose) {
    // Case guide closed : Determination of the origin 
    // & reorientation of the guide
    gp_Vec Dir;
    gp_Pnt SpOr;
    if (!SpClose) {
      TopoDS_Vertex Vf, Vl;
      gp_Pnt P;
      TopExp::Vertices(mySpine, Vf, Vl);
      SpOr = BRep_Tool::Pnt(Vf);
      P = BRep_Tool::Pnt(Vl);
      gp_Vec V(P, SpOr);
      SpOr.BaryCenter(0.5, P, 0.5);
      Dir = V;
    }
    else {
      BRepAdaptor_CompCurve BC(mySpine);
      BC.D1(0,SpOr,Dir); 
    } 
    BRepFill::SearchOrigin(TheGuide, SpOr, Dir, 100*myTol3d);
  }

#ifdef DRAW
  if (Affich)
    DBRep::Set("theguide", TheGuide);
#endif
  // transform the guide in a single curve
  Handle(BRepAdaptor_CompCurve) Guide = new (BRepAdaptor_CompCurve) (TheGuide);

  if (CurvilinearEquivalence) { // trihedron by curvilinear reduced abscissa
    if (KeepContact == BRepFill_Contact ||
        KeepContact == BRepFill_ContactOnBorder) 
      myTrihedron = GeomFill_IsGuideACWithContact; // with rotation 
    else
      myTrihedron = GeomFill_IsGuideAC; // without rotation 
      
    Handle(GeomFill_GuideTrihedronAC) TLaw
      = new (GeomFill_GuideTrihedronAC) (Guide);
    Handle(GeomFill_LocationGuide) Loc = 
      new (GeomFill_LocationGuide) (TLaw);      
    myLocation = new (BRepFill_ACRLaw) (mySpine, Loc); 	
  }
  else {// trihedron by plane
    if (KeepContact == BRepFill_Contact ||
        KeepContact == BRepFill_ContactOnBorder) 
      myTrihedron = GeomFill_IsGuidePlanWithContact; // with rotation 
    else 
      myTrihedron = GeomFill_IsGuidePlan; // without rotation

    Handle(GeomFill_GuideTrihedronPlan) TLaw = 
      new (GeomFill_GuideTrihedronPlan) (Guide);    
    Handle(GeomFill_LocationGuide) Loc = 
      new (GeomFill_LocationGuide) (TLaw);
    myLocation = new (BRepFill_Edge3DLaw) (mySpine, Loc);  
  }    
  mySection.Nullify(); //It is required to relocalize the sections.
}


//=======================================================================
//function : SetMaxDegree
//purpose  : 
//=======================================================================
void BRepFill_PipeShell::SetMaxDegree(const Standard_Integer NewMaxDegree)
{
  myMaxDegree = NewMaxDegree;
}

//=======================================================================
//function : SetMaxSegments
//purpose  : 
//=======================================================================
void BRepFill_PipeShell::SetMaxSegments(const Standard_Integer NewMaxSegments)
{
  myMaxSegments = NewMaxSegments;
}

//=======================================================================
//function : SetForceApproxC1
//purpose  : Set the flag that indicates attempt to approximate
//           a C1-continuous surface if a swept surface proved
//           to be C0.
//=======================================================================
void BRepFill_PipeShell::SetForceApproxC1(const Standard_Boolean ForceApproxC1)
{
  myForceApproxC1 = ForceApproxC1;
}

//=======================================================================
//function : Add
//purpose  : Add a Section
//=======================================================================
 void BRepFill_PipeShell::Add(const TopoDS_Shape& Profile,
			      const Standard_Boolean WithContact,
			      const Standard_Boolean WithCorrection) 
{ 
  TopoDS_Vertex V;
  V.Nullify();
  Add(Profile, V, WithContact, WithCorrection);
  ResetLoc();
}

//=======================================================================
//function : Add
//purpose  : Add a Section
//=======================================================================
 void BRepFill_PipeShell::Add(const TopoDS_Shape& Profile,
			      const TopoDS_Vertex& Location,
			      const Standard_Boolean WithContact,
			      const Standard_Boolean WithCorrection) 
{
 DeleteProfile(Profile); // No duplication
 if (myIsAutomaticLaw)
 {
   mySeq.Clear();
   BRepFill_Section S (Profile, Location, WithContact, WithCorrection);
   S.Set(Standard_True);
   mySeq.Append(S);
   mySection.Nullify();
   ResetLoc();

   Handle(GeomFill_LocationGuide) Loc = Handle(GeomFill_LocationGuide)::DownCast(myLocation->Law(1));
   Handle(TColgp_HArray1OfPnt2d) ParAndRad;
   Loc->ComputeAutomaticLaw(ParAndRad);
   
   //Compuite initial width of section (this will be 1.)
   GProp_GProps GlobalProps;
   BRepGProp::LinearProperties(Profile, GlobalProps);
   gp_Pnt BaryCenter = GlobalProps.CentreOfMass();

   TopoDS_Face ProfileFace = BRepLib_MakeFace(TopoDS::Wire(Profile), Standard_True); //only plane
   Handle(Geom_Surface) thePlane = BRep_Tool::Surface(ProfileFace);
   Handle(GeomAdaptor_Surface) GAHplane = new GeomAdaptor_Surface(thePlane);
   IntCurveSurface_HInter Intersector;
   Handle(Adaptor3d_Curve) aHCurve [2];
   aHCurve[0] = Loc->GetCurve();
   aHCurve[1] = Loc->Guide();
   gp_Pnt PointsOnSpines [2];
   Standard_Integer i, j;

   for (i = 0; i < 2; i++)
   {
     Intersector.Perform(aHCurve[i], GAHplane);
     Standard_Real MinDist = RealLast();
     for (j = 1; j <= Intersector.NbPoints(); j++)
     {
       gp_Pnt aPint = Intersector.Point(j).Pnt();
       Standard_Real aDist = BaryCenter.Distance(aPint);
       if (aDist < MinDist)
       {
         MinDist = aDist;
         PointsOnSpines[i] = aPint;
       }
     }
   }

   //Correct <ParAndRad> according to <InitialWidth>
   Standard_Real InitialWidth = PointsOnSpines[0].Distance(PointsOnSpines[1]);
   Standard_Integer NbParRad = ParAndRad->Upper();
   for (i = 1; i <= NbParRad; i++)
   {
     gp_Pnt2d aParRad = ParAndRad->Value(i);
     aParRad.SetY( aParRad.Y() / InitialWidth );
     ParAndRad->SetValue(i, aParRad);
   }
  
   myLaw = new Law_Interpol();
   
   Standard_Boolean IsPeriodic =
     (Abs(ParAndRad->Value(1).Y() - ParAndRad->Value(NbParRad).Y()) < Precision::Confusion());

   (Handle(Law_Interpol)::DownCast(myLaw))->Set(ParAndRad->Array1(), IsPeriodic);
 }
 else
 {
   BRepFill_Section S (Profile, Location, WithContact, WithCorrection);
   mySeq.Append(S);
   mySection.Nullify();
   ResetLoc();
 }
}

//=======================================================================
//function : SetLaw
//purpose  : Section + law of homothety
//=======================================================================
 void BRepFill_PipeShell::SetLaw(const TopoDS_Shape& Profile,
				 const Handle(Law_Function)& L,
				 const Standard_Boolean WithContact,
				 const Standard_Boolean WithCorrection) 
{
 TopoDS_Vertex V;
 V.Nullify();
 SetLaw( Profile, L, V, WithContact, WithCorrection);
 ResetLoc();
}

//=======================================================================
//function : SetLaw
//purpose  :  Section + Law of homothety
//=======================================================================
 void BRepFill_PipeShell::SetLaw(const TopoDS_Shape& Profile,
				 const Handle(Law_Function)& L,
				 const TopoDS_Vertex& Location,
				 const Standard_Boolean WithContact,
				 const Standard_Boolean WithCorrection) 
{
  mySeq.Clear();
  BRepFill_Section S (Profile, Location, WithContact, WithCorrection);
  S.Set(Standard_True);
  mySeq.Append(S);
  myLaw = L;
  mySection.Nullify();
  ResetLoc();
}

//=======================================================================
//function : Delete
//purpose  : Delete a section
//=======================================================================
 void BRepFill_PipeShell::DeleteProfile(const TopoDS_Shape&  Profile)
{
  Standard_Boolean Trouve=Standard_False;
  Standard_Integer ii;
  for (ii=1; ii<=mySeq.Length() && !Trouve; ii++) {
    const TopoDS_Shape& aSection = mySeq.Value(ii).OriginalShape();
    if (Profile.IsSame(aSection))
    {
      Trouve = Standard_True;
      mySeq.Remove(ii);
    }
  }

  if (Trouve) mySection.Nullify();
  ResetLoc();
}


//=======================================================================
//function : IsReady
//purpose  : 
//=======================================================================
 Standard_Boolean BRepFill_PipeShell::IsReady() const
{
 return (mySeq.Length() != 0);
}
//=======================================================================
//function : GetStatus
//purpose  : 
//=======================================================================
 GeomFill_PipeError BRepFill_PipeShell::GetStatus() const
{
 return myStatus;
}


//=======================================================================
//function : SetTolerance
//purpose  :
//=======================================================================
 void BRepFill_PipeShell::SetTolerance(const Standard_Real Tol3d  ,
				       const Standard_Real BoundTol, 
				       const Standard_Real TolAngular)
{
 myTol3d = Tol3d;
 myBoundTol =  BoundTol;
 myTolAngular = TolAngular;
}

//=======================================================================
//function : SetTransition
//purpose  : Defines the mode of processing of corners
//=======================================================================
 void BRepFill_PipeShell::SetTransition(const BRepFill_TransitionStyle Mode,
					const Standard_Real Angmin,
					const Standard_Real Angmax)
{
 if (myTransition != Mode) 
   mySection.Nullify(); //It is required to relocalize the sections.
 myTransition = Mode;
 angmin =  Angmin;
 angmax = Angmax;
}  

//=======================================================================
//function : Simulate
//purpose  : Calculate N Sections
//=======================================================================
 void BRepFill_PipeShell::Simulate(const Standard_Integer N, 
				   TopTools_ListOfShape& List)
{
  // Preparation
  Prepare();
  List.Clear();

  Standard_Real First, Last, Length, Delta, U, 
                US, DeltaS,FirstS;
  Standard_Integer ii, NbL = myLocation->NbLaw();
  Standard_Boolean Finis=Standard_False;
  TopoDS_Shape W;
  
  // Calculate the parameters of digitalization
  mySection->Law(1)->GetDomain(FirstS, Last);
  DeltaS = Last - FirstS; 
  myLocation->CurvilinearBounds(NbL,First, Length);
  Delta = Length;
  if (N>1) Delta /= (N-1);

  myLocation->CurvilinearBounds(1,First, Last); // Initiation of Last
  for (U=0.0, ii=1; !Finis ; U+=Delta) {
    if (U >= Length) {
      U = Length;
      Finis = Standard_True;
    }
    else {
      if (ii <  NbL) myLocation->CurvilinearBounds(NbL,First, Last);
      if (U > Last) U = (Last+First)/2; // The edge is not skipped
      if (U> First) ii++;
    }
    US = FirstS + (U/Length)*DeltaS;
    // Calcul d'une section
    mySection->D0(US, W);
    myLocation->D0(U, W);
    List.Append(W);
  } 
}

//=======================================================================
//function : Build
//purpose  : Construct the Shell and the history
//=======================================================================
 Standard_Boolean BRepFill_PipeShell::Build()
{
  Standard_Boolean Ok;
  Standard_Real FirstS, LastS;
  // 1) Preparation
  Prepare();

  if (myStatus != GeomFill_PipeOk) {
    BRep_Builder B;
    TopoDS_Shell Sh;
    B.MakeShell(Sh); 
    myShape = Sh; // Nullify
    return Standard_False; 
  }

  // 2) Calculate myFirst and myLast
  mySection->Law(1)->GetDomain(FirstS, LastS);
  mySection->D0(FirstS, myFirst);
  myLocation->D0(0, myFirst);
  if (mySection->IsVClosed() && myLocation->IsClosed()) {
      if (myLocation->IsG1(0)>=0) 
	myLast = myFirst;
      else {
	myFirst.Nullify();
	myLast.Nullify();
      }
  }
  else {
    Standard_Real Length;
    myLocation->CurvilinearBounds(myLocation->NbLaw(), 
				  FirstS, Length);
    mySection->D0(LastS,   myLast);
    myLocation->D0(Length, myLast);
    // eap 5 Jun 2002 occ332, myLast and myFirst must not share one TShape,
    // tolerances of shapes built on them may be quite different
    if (myFirst.IsPartner( myLast )) {
      BRepBuilderAPI_Copy copy(myLast);
      if (copy.IsDone()) 
        myLast = copy.Shape();
    }
    // eap 5 Jun 2002 occ332, end modif
  }
#ifdef DRAW
  if (Affich) {
    DBRep::Set("PipeFirst", myFirst);
    DBRep::Set("PipeLast",  myLast);
  }
#endif

  // 3) Construction
  BRepFill_Sweep MkSw(mySection, myLocation, Standard_True);
  MkSw.SetTolerance(myTol3d, myBoundTol, 1.e-5, myTolAngular);
  MkSw.SetAngularControl(angmin, angmax);
  MkSw.SetForceApproxC1(myForceApproxC1);
  MkSw.SetBounds(TopoDS::Wire(myFirst),
                 TopoDS::Wire(myLast));

  GeomAbs_Shape theContinuity = GeomAbs_C2;
  if (myTrihedron == GeomFill_IsDiscreteTrihedron)
    theContinuity = GeomAbs_C0;
  TopTools_MapOfShape Dummy;
  BRepFill_DataMapOfShapeHArray2OfShape Dummy2;
  BRepFill_DataMapOfShapeHArray2OfShape Dummy3;
  MkSw.Build(Dummy, Dummy2, Dummy3, myTransition, theContinuity,
             GeomFill_Location, myMaxDegree, myMaxSegments);

  myStatus = myLocation->GetStatus();
  Ok =  (MkSw.IsDone() && (myStatus == GeomFill_PipeOk));

  if (Ok) {
    myShape = MkSw.Shape();
    myErrorOnSurf = MkSw.ErrorOnSurface();

    TopoDS_Shape aBottomWire = myFirst;
    TopoDS_Shape aTopWire    = myLast;

    if(BuildBoundaries(MkSw, mySection, aBottomWire, aTopWire)) {
      myFirst = aBottomWire;
      myLast = aTopWire;
    }
    
    if (mySection->IsUClosed())
      {
	TopExp_Explorer explo;
	Standard_Boolean DegenFirst = Standard_True, DegenLast = Standard_True;

	for (explo.Init(myFirst, TopAbs_EDGE); explo.More(); explo.Next())
	  {
	    const TopoDS_Edge& anEdge = TopoDS::Edge(explo.Current());
	    DegenFirst = DegenFirst && BRep_Tool::Degenerated(anEdge);
	  }

	for (explo.Init(myLast, TopAbs_EDGE); explo.More(); explo.Next())
	  {
	    const TopoDS_Edge& anEdge = TopoDS::Edge(explo.Current());
	    DegenLast = DegenLast && BRep_Tool::Degenerated(anEdge);
	  }

	if (DegenFirst && DegenLast)
	  myShape.Closed(Standard_True);
      }

    BuildHistory(MkSw);
  }
  else {
    BRep_Builder B;
    TopoDS_Shell Sh;
    B.MakeShell(Sh); 
    myShape = Sh; // Nullify
    if (myStatus == GeomFill_PipeOk) myStatus = GeomFill_PipeNotOk;
  }
  return Ok;
}

//=======================================================================
//function : MakeSolid
//purpose  : 
//=======================================================================
 Standard_Boolean BRepFill_PipeShell::MakeSolid() 
{ 
  if (myShape.IsNull()) 
    throw StdFail_NotDone("PipeShell is not built");
  Standard_Boolean B = myShape.Closed();
  BRep_Builder BS;

  if (!B)
    {
      if(!myFirst.IsNull() && !myLast.IsNull()) {
	B = (myFirst.Closed() && myLast.Closed());
      }
      if (B) {
	// It is necessary to block the extremities 
	B =  PerformPlan(myFirst);
	if (B) {
	  B =  PerformPlan(myLast);
	  if (B) {
	    if (!myFirst.IsNull() && !IsSameOriented( myFirst, myShape ))
	      myFirst.Reverse();
	    if (!myLast.IsNull() && !IsSameOriented( myLast, myShape ))
	      myLast.Reverse();

	    if (!myFirst.IsNull())
	      BS.Add(myShape, TopoDS::Face(myFirst));
	    if (!myLast.IsNull())
	      BS.Add(myShape, TopoDS::Face(myLast));

	    myShape.Closed(Standard_True);
	  }
	}
      }
    }

  if (B) {
   TopoDS_Solid solid;
   BS.MakeSolid(solid);
   BS.Add(solid,TopoDS::Shell(myShape));
   BRepClass3d_SolidClassifier SC(solid);
   SC.PerformInfinitePoint(Precision::Confusion());
   if ( SC.State() == TopAbs_IN) {
     BS.MakeSolid(solid);
     myShape.Reverse();
     BS.Add(solid,TopoDS::Shell(myShape));
   }
   myShape = solid;   
   myShape.Closed(Standard_True);
 }
  return B;
}

//=======================================================================
//function : Shape
//purpose  : Return the result
//=======================================================================
const TopoDS_Shape& BRepFill_PipeShell::Shape() const
{
  return myShape;
}

//=======================================================================
//function : ErrorOnSurface
//purpose  : 
//=======================================================================

Standard_Real BRepFill_PipeShell::ErrorOnSurface() const 
{
  return myErrorOnSurf;
}

//=======================================================================
//function : FirstShape
//purpose  : Return the start section 
//=======================================================================
const TopoDS_Shape& BRepFill_PipeShell::FirstShape() const
{
  return myFirst;
}

//=======================================================================
//function : LastShape
//purpose  : Return the end section 
//=======================================================================
const TopoDS_Shape& BRepFill_PipeShell::LastShape() const
{
 return myLast;
}

//=======================================================================
//function : Generated
//purpose  : 
//=======================================================================
void BRepFill_PipeShell::Generated(const TopoDS_Shape&   theShape,
				   TopTools_ListOfShape& theList) 
{
  theList.Clear();

  if(myGenMap.IsBound(theShape)) {
    theList = myGenMap.Find(theShape);
  }
}

//=======================================================================
//function : Prepare
//purpose  : - Check that everything is ready
//           - Construct the law of section
//           - Construct the law of location if required
//           - Calculate First & Last
//=======================================================================
 void BRepFill_PipeShell::Prepare() 
{
  WSeq.Clear();
  myEdgeNewEdges.Clear();
  
  TopoDS_Wire theSect;
  if (!IsReady()) throw StdFail_NotDone("PipeShell");
  if (!myLocation.IsNull() && !mySection.IsNull()) return; // It is ready
 
  //Check set of section for right configuration of punctual sections
  Standard_Integer i;
  TopoDS_Iterator iter;
  for (i = 2; i <= mySeq.Length()-1; i++)
    {
      Standard_Boolean wdeg = Standard_True;
      for (iter.Initialize(mySeq(i).Wire()); iter.More(); iter.Next())
	{
	  const TopoDS_Edge& anEdge = TopoDS::Edge(iter.Value());
	  wdeg = wdeg && (BRep_Tool::Degenerated(anEdge));
	}
      if (wdeg)
	throw Standard_Failure("Wrong usage of punctual sections");
    }
  if (mySeq.Length() <= 2)
    {
      Standard_Boolean wdeg = Standard_True;
      for (i = 1; i <= mySeq.Length(); i++)
	for (iter.Initialize(mySeq(i).Wire()); iter.More(); iter.Next())
	  {
	    const TopoDS_Edge& anEdge = TopoDS::Edge(iter.Value());
	    wdeg = wdeg && (BRep_Tool::Degenerated(anEdge));
	  }
      if (wdeg)
	throw Standard_Failure("Wrong usage of punctual sections");
    }

  // Construction of the law of location  
  if(myLocation.IsNull()) 
    {
      switch(myTrihedron)
	{
	case GeomFill_IsCorrectedFrenet :
	  {
	    Handle(GeomFill_TrihedronLaw) TLaw = 
	      new (GeomFill_CorrectedFrenet) ();
	    Handle(GeomFill_CurveAndTrihedron) Loc = 
	      new (GeomFill_CurveAndTrihedron) (TLaw);
	    myLocation = new (BRepFill_Edge3DLaw) (mySpine, Loc);
	    break;
	  } 
	  default :
	    { // Not planned!
	      throw Standard_ConstructionError("PipeShell");
	    }
	}
    }  
  
  //Transformation of the law (Transition Management)
  PerformTransition(myTransition, myLocation, angmin);

  
 // Construction of the section law
  if (mySeq.Length() == 1) {
    Standard_Real p1;
    gp_Trsf aTrsf;
    Place(mySeq(1), theSect, aTrsf, p1);
    TopoDS_Wire aLocalShape = theSect;
    if (mySeq(1).IsLaw())
      mySection = new BRepFill_ShapeLaw(aLocalShape, myLaw);
//      mySection = new (BRepFill_ShapeLaw) (TopoDS_Wire(theSect), myLaw);
    else 
      mySection = new BRepFill_ShapeLaw(aLocalShape);
// mySection = new (BRepFill_ShapeLaw) (TopoDS::Wire(theSect));
    
    WSeq.Append(theSect);
    //Simple case of single section
    myIndOfSec.Append(1);
    TopoDS_Iterator itw(theSect);
    for (; itw.More(); itw.Next())
    {
      const TopoDS_Shape& anEdge = itw.Value();
      TopTools_ListOfShape Elist;
      Elist.Append(anEdge);
      myEdgeNewEdges.Bind(anEdge, Elist);
    }
    ///////////////////////////////
  }   
  else 
  {
    TColStd_SequenceOfReal Param;
    TColStd_SequenceOfInteger IndSec;
    GeomFill_SequenceOfTrsf Transformations;
    Standard_Integer NbL = myLocation->NbLaw();
    gp_Trsf aTrsf;
    Standard_Real V1, V2, param;
    myLocation->CurvilinearBounds(NbL, V1, V2);
    V1 = 0.;
    Standard_Integer ideb = 0, ifin = 0;
    Standard_Integer iseq;
    for (iseq = 1; iseq <= mySeq.Length(); iseq++) {
      IndSec.Append(iseq);
      Place(mySeq(iseq), theSect, aTrsf, param);
      Param.Append(param);
      WSeq.Append(theSect);
      Transformations.Append(aTrsf);
      if (param==V1) ideb = iseq;
      if (param==V2) ifin = iseq;
    }
    
    
    // looping sections ?
    if (myLocation->IsClosed()) {
      if (ideb>0) {
        // place the initial section at the final position 
        Param.Append(V2);
        WSeq.Append(WSeq(ideb));
      }
      else if (ifin>0) {
        // place the final section at the initial position
        Param.Append(V1);
        WSeq.Append(WSeq(ifin));
      }
      else {
        // it is necessary to find a medium section to impose by V1 and by V2
        Standard_Real pmin = RealLast(), pmax = RealFirst();
        TopoDS_Wire Wmin, Wmax;
        for (iseq = 1; iseq <= WSeq.Length(); iseq++) {
          if (Param.Value(iseq)<pmin) {
            pmin = Param.Value(iseq);
            Wmin = TopoDS::Wire(WSeq.Value(iseq));
          }
          if (Param.Value(iseq)>pmax) {
            pmax = Param.Value(iseq);
            Wmax = TopoDS::Wire(WSeq.Value(iseq));
          }
        }
        // medium section between Wmin and Wmax
        TopoDS_Wire Wres;
        Standard_Real dmin = Abs(pmin-V1);
        Standard_Real dmax = Abs(pmax-V2);
        if (ComputeSection(Wmin,Wmax,dmin,dmax,Wres)) {
          // impose section Wres at the beginning and the end
          Param.Append(V1);
          WSeq.Append(Wres);
          IndSec.Append(WSeq.Length());
          Param.Append(V2);
          WSeq.Append(Wres);
          IndSec.Append(WSeq.Length());
        }
      }
    }
    
    // parse sections by increasing parameter
    Standard_Boolean play_again = Standard_True;
    while (play_again) {
      play_again = Standard_False;
      for (iseq=1;iseq<=WSeq.Length();iseq++) {
        for (Standard_Integer jseq=iseq+1;jseq<=WSeq.Length();jseq++) {
          if (Param.Value(iseq) > Param.Value(jseq)) {
            Param.Exchange(iseq,jseq);
            WSeq.Exchange(iseq,jseq);
            IndSec.Exchange(iseq,jseq);
            play_again = Standard_True;
          }
        }
      }
    }
    //Fill the array of real indices of sections
    for (Standard_Integer ii = 1; ii <= mySeq.Length(); ii++)
      for (Standard_Integer jj = 1; jj <= IndSec.Length(); jj++)
        if (IndSec(jj) == ii)
        {
          myIndOfSec.Append(jj);
          break;
        }
    
#ifdef DRAW
    if ( Affich) {
      char*  name = new char[100];
      Standard_Integer NBSECT = 0;
      for (Standard_Integer i=1;i<=WSeq.Length();i++) {
        NBSECT++;
        sprintf(name,"WSeq_%d",NBSECT);
        DBRep::Set(name,TopoDS::Wire(WSeq.Value(i)));
      }
    }
#endif
    
    
    
    //  Calculate work sections 
    TopTools_SequenceOfShape WorkingSections;
    WorkingSections.Clear();
    TopTools_DataMapOfShapeListOfShape WorkingMap;
    BRepFill_CompatibleWires Georges(WSeq);
    Georges.SetPercent(0.1);
    Georges.Perform(Standard_False);
    if (Georges.IsDone()) {
      WorkingSections = Georges.Shape();
      WorkingMap = Georges.Generated();
      //For each sub-edge of each section
      //we save its splits
      for (Standard_Integer ii = 1; ii <= WSeq.Length(); ii++)
      {
        TopExp_Explorer Explo(WSeq(ii), TopAbs_EDGE);
        for (; Explo.More(); Explo.Next())
        {
          const TopoDS_Edge& anEdge = TopoDS::Edge(Explo.Current());
          const TopTools_ListOfShape& aNewEdges = Georges.GeneratedShapes(anEdge);
          myEdgeNewEdges.Bind(anEdge, aNewEdges);
        }
      }
    }
    else {
      throw Standard_ConstructionError("PipeShell : uncompatible wires");
    }
    mySection = new (BRepFill_NSections) (WorkingSections,Transformations,Param,V1,V2);
    
  }// else

  //  modify the law of location if contact
  if ( (myTrihedron == GeomFill_IsGuidePlanWithContact)
      || (myTrihedron == GeomFill_IsGuideACWithContact) )  {
    Standard_Real fs, f, l, Delta, Length;
    Handle(GeomFill_LocationGuide) Loc;
    Handle(GeomFill_SectionLaw) Sec = mySection->ConcatenedLaw();
    myLocation->CurvilinearBounds(myLocation->NbLaw(), f, Length);
    Sec->GetDomain(fs,l);
    Delta = (l-fs)/Length;

    Standard_Real angle, old_angle = 0;
    for (Standard_Integer ipath=1; ipath<=myLocation->NbLaw(); ipath++) {
      myLocation->CurvilinearBounds(ipath, f, l);
      Loc = Handle(GeomFill_LocationGuide)::DownCast(myLocation->Law(ipath));
      Loc->Set(Sec, Standard_True, fs + f*Delta, fs + l*Delta,
	       old_angle, angle); // force the rotation	
      old_angle = angle;
    }      
  }

  myStatus = myLocation->GetStatus();
  if (!mySection->IsDone())
    myStatus = GeomFill_PipeNotOk;
}

//=======================================================================
//function : Place
//purpose  : Implement a Section in the local refernce frame
//           and return its parameter on the trajectory
//=======================================================================
void BRepFill_PipeShell::Place(const BRepFill_Section& Sec,
			       TopoDS_Wire& W,
                               gp_Trsf& aTrsf,
			       Standard_Real& param)
{
  BRepFill_SectionPlacement Place(myLocation, 
				  Sec.Wire(),
				  Sec.Vertex(),
				  Sec.WithContact(),
				  Sec.WithCorrection());
  const TopoDS_Wire& TmpWire =  Sec.Wire();
  aTrsf = Place.Transformation();
  //Transform the copy
  W = TopoDS::Wire(BRepBuilderAPI_Transform(TmpWire, aTrsf, Standard_True));
  ////////////////////////////////////
  param = Place.AbscissaOnPath();
}


//=======================================================================
//function : ResetLoc
//purpose  : Remove references to the sections in the laws of location
//=======================================================================
 void BRepFill_PipeShell::ResetLoc() 
{
  if ( (myTrihedron == GeomFill_IsGuidePlanWithContact)
      || (myTrihedron == GeomFill_IsGuideACWithContact) ) {
    Handle(GeomFill_LocationGuide) Loc;
    for (Standard_Integer isec=1; isec<=myLocation->NbLaw(); isec++) { 
      Loc = Handle(GeomFill_LocationGuide)::DownCast(myLocation->Law(isec));
      Loc->EraseRotation();// remove the rotation	
    }    
  }
}

//=======================================================================
//function : BuildHistory
//purpose  : Builds history for edges and vertices of sections,
//           for edges and vertices of spine
//=======================================================================
void BRepFill_PipeShell::BuildHistory(const BRepFill_Sweep& theSweep) 
{
  //Filling of <myGenMap>
  const Handle(TopTools_HArray2OfShape)& anUEdges = theSweep.InterFaces();
  BRep_Builder BB;
  
  TopTools_DataMapOfIntegerShape IndWireMap;
  
  Standard_Integer indw, inde;
  TopoDS_Iterator itw;
  for (indw = 1; indw <= mySeq.Length(); indw++)
  {
    const TopoDS_Shape& Section = mySeq(indw).OriginalShape();
    TopoDS_Wire aSection;
    Standard_Boolean IsPunctual = mySeq(indw).IsPunctual();
    if (IsPunctual)
    {
      //for punctual sections (first or last)
      //we take all the wires generated along the path
      
      TopTools_ListOfShape* Elist = myGenMap.Bound(Section, TopTools_ListOfShape());
      for (Standard_Integer i = 1; i <= anUEdges->UpperRow(); i++)
        for (Standard_Integer j = 1; j <= anUEdges->UpperCol(); j++)
          Elist->Append(anUEdges->Value(i,j));

      continue;
    }
    else
      aSection = TopoDS::Wire(Section);
    //Take the real index of section on the path
    Standard_Integer IndOfW = myIndOfSec(indw);
    const TopoDS_Wire& theWire = TopoDS::Wire(WSeq(IndOfW));
    BRepTools_WireExplorer wexp_sec(aSection);
    for (inde = 1; wexp_sec.More(); wexp_sec.Next())
    {
      const TopoDS_Edge& anOriginalEdge = TopoDS::Edge(wexp_sec.Current());
      TopoDS_Edge anEdge = TopoDS::Edge(mySeq(indw).ModifiedShape(anOriginalEdge));
      if (BRep_Tool::Degenerated(anEdge))
        continue;

      TopoDS_Shell aShell;
      BB.MakeShell(aShell);
      TopoDS_Vertex aVertex [2];
      TopExp::Vertices(anOriginalEdge, aVertex[0], aVertex[1]);
      Standard_Integer SignOfAnEdge =
        (anOriginalEdge.Orientation() == TopAbs_FORWARD)? 1 : -1;
      
      //For each non-degenerated inde-th edge of <aSection>
      //we find inde-th edge in <theWire>
      TopoDS_Edge theEdge;
      BRepTools_WireExplorer wexp(theWire);
      for (Standard_Integer i = 1; wexp.More(); wexp.Next())
      {
        theEdge = TopoDS::Edge(wexp.Current());
        if (BRep_Tool::Degenerated(anEdge))
          continue;
        if (i == inde)
          break;
        i++;
      }

      //Take the list of splits for <theEdge>
      const TopTools_ListOfShape& NewEdges = myEdgeNewEdges(theEdge);
      Standard_Integer SignOfANewEdge = 0, SignOfIndex = 0;
      TopTools_ListIteratorOfListOfShape iter(NewEdges);
      for (; iter.More(); iter.Next())
      {
        const TopoDS_Edge& aNewEdge = TopoDS::Edge(iter.Value());
        SignOfANewEdge = (aNewEdge.Orientation() == TopAbs_FORWARD)? 1 : -1;
        Standard_Integer anIndE = mySection->IndexOfEdge(aNewEdge);
        SignOfIndex = (anIndE > 0)? 1 : -1;
        anIndE = Abs(anIndE);
        //For an edge generated shape is a "tape" -
        //a shell usually containing this edge and
        //passing from beginning of path to its end
        TopoDS_Shape aTape = theSweep.Tape(anIndE);
        TopoDS_Iterator itsh(aTape);
        for (; itsh.More(); itsh.Next())
          BB.Add(aShell, itsh.Value());
      }

      //Processing of vertices of <anEdge>
      //We should choose right index in <anUEdges>
      //for each vertex of edge 
      Standard_Integer ToReverse = SignOfAnEdge * SignOfANewEdge * SignOfIndex;
      Standard_Integer UIndex [2];
      UIndex[0] = Abs(mySection->IndexOfEdge(NewEdges.First()));
      UIndex[1] = Abs(mySection->IndexOfEdge(NewEdges.Last())) + ToReverse;
      if (ToReverse == -1)
      {
        UIndex[0]++;
        UIndex[1]++;
      }
      if (mySection->IsUClosed())
      {
        if (UIndex[0] > mySection->NbLaw())
          UIndex[0] = 1;
        if (UIndex[1] > mySection->NbLaw())
          UIndex[1] = 1;
      }
      //if (SignOfAnEdge * SignOfANewEdge == -1)
      if (SignOfAnEdge   == -1 ||
          SignOfANewEdge == -1)
      { Standard_Integer Tmp = UIndex[0]; UIndex[0] = UIndex[1]; UIndex[1] = Tmp; }

      TopTools_IndexedDataMapOfShapeListOfShape VEmap;
      TopExp::MapShapesAndAncestors(aShell, TopAbs_VERTEX, TopAbs_EDGE, VEmap);
      for (Standard_Integer kk = 0; kk < 2; kk++)
      {
        if (myGenMap.IsBound(aVertex[kk]))
          continue;
        if (IndWireMap.IsBound(UIndex[kk]))
        {
          TopTools_ListOfShape* Elist = myGenMap.Bound(aVertex[kk], TopTools_ListOfShape());
          
          for (itw.Initialize( IndWireMap(UIndex[kk]) ); itw.More(); itw.Next())
            Elist->Append(itw.Value());
          
          continue;
        }
        
        //Collect u-edges
        TopTools_SequenceOfShape SeqEdges;
        Standard_Integer jj;
        for (jj = 1; jj <= anUEdges->UpperCol(); jj++)
          SeqEdges.Append(anUEdges->Value(UIndex[kk], jj));

        //Assemble the wire ("rail" along the path)
        //checking for possible holes
        //(they appear with option "Round Corner")
        //and filling them
        //Missed edges are taken from <aShell>
        TopoDS_Wire aWire;
        BB.MakeWire(aWire);
        const TopoDS_Edge& FirstEdge = TopoDS::Edge(SeqEdges(1));
        if (FirstEdge.IsNull())
          continue;
        BB.Add(aWire, FirstEdge);
        TopoDS_Vertex FirstVertex, CurVertex;
        TopExp::Vertices(FirstEdge, FirstVertex, CurVertex);
        TopoDS_Edge CurEdge;
        for (jj = 2; jj <= SeqEdges.Length(); jj++)
        {
          CurEdge = TopoDS::Edge(SeqEdges(jj));
          TopoDS_Vertex Vfirst, Vlast;
          TopExp::Vertices(CurEdge, Vfirst, Vlast);
          if (CurVertex.IsSame(Vfirst))
            CurVertex = Vlast;
          else //a hole
          {
            const TopTools_ListOfShape& Elist = VEmap.FindFromKey(Vfirst);
            TopTools_ListIteratorOfListOfShape itl(Elist);
            for (; itl.More(); itl.Next())
            {
              const TopoDS_Edge& Candidate = TopoDS::Edge(itl.Value());
              if (Candidate.IsSame(CurEdge))
                continue;
              TopoDS_Vertex V1, V2;
              TopExp::Vertices(Candidate, V1, V2);
              if (V1.IsSame(CurVertex) || V2.IsSame(CurVertex))
              {
                BB.Add(aWire, Candidate);
                break;
              }
            }
          }
          CurVertex = Vlast;
          BB.Add(aWire, CurEdge);
        } //for (jj = 2; jj <= SeqEdges.Length(); jj++)
        //case of closed wire
        if (myLocation->IsClosed() &&
            !CurVertex.IsSame(FirstVertex))
        {
          const TopTools_ListOfShape& Elist = VEmap.FindFromKey(CurVertex);
          TopTools_ListIteratorOfListOfShape itl(Elist);
          for (; itl.More(); itl.Next())
          {
            const TopoDS_Edge& Candidate = TopoDS::Edge(itl.Value());
            if (Candidate.IsSame(CurEdge))
              continue;
            TopoDS_Vertex V1, V2;
            TopExp::Vertices(Candidate, V1, V2);
            if (V1.IsSame(FirstVertex) || V2.IsSame(FirstVertex))
            {
              BB.Add(aWire, Candidate);
              break;
            }
          }
        }

        TopTools_ListOfShape* Elist = myGenMap.Bound(aVertex[kk], TopTools_ListOfShape());

        for (itw.Initialize(aWire); itw.More(); itw.Next())
          Elist->Append(itw.Value());
        
        //Save already built wire with its index
        IndWireMap.Bind(UIndex[kk], aWire);
      } //for (Standard_Integer kk = 0; kk < 2; kk++)
      ////////////////////////////////////
      
      TopTools_ListOfShape* Flist = myGenMap.Bound(anOriginalEdge, TopTools_ListOfShape());
      TopoDS_Iterator itsh(aShell);
      for (; itsh.More(); itsh.Next())
        Flist->Append(itsh.Value());
      ////////////////////////

      inde++;
    }
  }

  //For subshapes of spine
  const Handle(TopTools_HArray2OfShape)& aFaces  = theSweep.SubShape();
  const Handle(TopTools_HArray2OfShape)& aVEdges = theSweep.Sections();
  
  BRepTools_WireExplorer wexp(mySpine);
  inde = 0;
  Standard_Boolean ToExit = Standard_False;
  for (;;)
  {
    if (!wexp.More())
      ToExit = Standard_True;
    
    inde++;

    if (!ToExit)
    {
      const TopoDS_Edge& anEdgeOfSpine = wexp.Current();
      
      TopTools_ListOfShape* Flist = myGenMap.Bound(anEdgeOfSpine, TopTools_ListOfShape());
      
      for (Standard_Integer i = 1; i <= aFaces->UpperRow(); i++)
      {
        const TopoDS_Shape& aFace = aFaces->Value(i, inde);
        if (aFace.ShapeType() == TopAbs_FACE)
          Flist->Append(aFace);
      }
    }
    
    const TopoDS_Vertex& aVertexOfSpine = wexp.CurrentVertex();
    TopTools_ListOfShape* ListVshapes = myGenMap.Bound(aVertexOfSpine, TopTools_ListOfShape());
    for (Standard_Integer i = 1; i <= aVEdges->UpperRow(); i++)
    {
      const TopoDS_Shape& aVshape = aVEdges->Value(i, inde);
      if (aVshape.IsNull())
      {
        continue;
      }
      if (aVshape.ShapeType() == TopAbs_EDGE ||
          aVshape.ShapeType() == TopAbs_FACE)
        ListVshapes->Append(aVshape);
      else
      {
        TopoDS_Iterator itvshape(aVshape);
        for (; itvshape.More(); itvshape.Next())
        {
          const TopoDS_Shape& aSubshape = itvshape.Value();
          if (aSubshape.ShapeType() == TopAbs_EDGE ||
              aSubshape.ShapeType() == TopAbs_FACE)
            ListVshapes->Append(aSubshape);
          else
          {
            //it is wire
            for (itw.Initialize(aSubshape); itw.More(); itw.Next())
              ListVshapes->Append(itw.Value());
          }
        }
      }
    }

    if (ToExit)
      break;

    if (wexp.More())
      wexp.Next();
  }
}


// ---------------------------------------------------------------------------------
// static function: BuildBoundaries
// purpose:
// ---------------------------------------------------------------------------------
Standard_Boolean BuildBoundaries(const BRepFill_Sweep&               theSweep,
				 const Handle(BRepFill_SectionLaw)&  theSection,
				 TopoDS_Shape&                       theBottom,
				 TopoDS_Shape&                       theTop) {
  
  TopoDS_Wire aBottomWire;
  TopoDS_Wire aTopWire;
  BRep_Builder aB;
  aB.MakeWire(aBottomWire);
  aB.MakeWire(aTopWire);
  Standard_Boolean bfoundbottom = Standard_False;
  Standard_Boolean bfoundtop = Standard_False;
  Handle(TopTools_HArray2OfShape) aVEdges = theSweep.Sections();
  Standard_Integer i = 0;
  Standard_Boolean bAllSame = Standard_True;

  for(i = 1; i <= theSection->NbLaw(); i++) {
    const TopoDS_Shape& aBottomEdge = aVEdges->Value(i, aVEdges->LowerCol());

    if(!aBottomEdge.IsNull() && (aBottomEdge.ShapeType() == TopAbs_EDGE)) {
      aB.Add(aBottomWire, aBottomEdge);
      bfoundbottom = Standard_True;
    }
    const TopoDS_Shape& aTopEdge = aVEdges->Value(i, aVEdges->UpperCol());

    if(!aTopEdge.IsNull() && (aTopEdge.ShapeType() == TopAbs_EDGE)) {
      aB.Add(aTopWire, aTopEdge);
      bfoundtop = Standard_True;
    }

    if(!aBottomEdge.IsNull() && !aTopEdge.IsNull() && !aBottomEdge.IsSame(aTopEdge))
      bAllSame = Standard_False;
  }

  if(theSection->IsUClosed()) {
    aBottomWire.Closed(Standard_True);
    aTopWire.Closed(Standard_True);
  }

  if(bfoundbottom) {
    theBottom = aBottomWire;
  }

  if(bfoundtop) {
    theTop  = aTopWire;
  }

  if(bAllSame && bfoundbottom && bfoundtop)
    theTop = theBottom;

  return bfoundbottom || bfoundtop;
}
