// Created on: 1993-10-15
// Created by: Remi LEQUETTE
// Copyright (c) 1993-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.

#define TRC 0 
#define MODIF 1 


#include <BRep_Builder.hxx>
#include <BRep_Tool.hxx>
#include <BRepAlgo_BooleanOperation.hxx>
#include <BRepBuilderAPI_Sewing.hxx>
#include <BRepCheck.hxx>
#include <BRepCheck_Edge.hxx>
#include <BRepCheck_Shell.hxx>
#include <BRepClass3d_SolidClassifier.hxx>
#include <BRepLib.hxx>
#include <BRepTools_Substitution.hxx>
#include <TopExp.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Shape.hxx>
#include <TopOpeBRep_DSFiller.hxx>
#include <TopOpeBRepBuild_HBuilder.hxx>
#include <TopOpeBRepBuild_Tools.hxx>
#include <TopOpeBRepDS_BuildTool.hxx>
#include <TopOpeBRepDS_HDataStructure.hxx>
#include <TopOpeBRepTool_GeomTool.hxx>
#include <TopOpeBRepTool_OutCurveType.hxx>
#include <TopTools_IndexedMapOfShape.hxx>
#include <TopTools_ListIteratorOfListOfShape.hxx>
#include <TopTools_ListOfShape.hxx>
#include <TopTools_MapOfShape.hxx>

//  sewing
#ifdef OCCT_DEBUG
extern Standard_Boolean TopOpeBRepTool_GetcontextNOSEW();
#endif

#define Opecom(st1,st2) (((st1)==TopAbs_IN) && ((st2)==TopAbs_IN))
#define Opefus(st1,st2) (((st1)==TopAbs_OUT) && ((st2)==TopAbs_OUT))
#define Opecut(st1,st2) (((st1)==TopAbs_OUT) && ((st2)==TopAbs_IN))

// -------------------------------------------------------------------
static void Sub_Classify(TopExp_Explorer& Ex,
			 const TopAbs_State St1,
			 TopTools_ListOfShape& Solids2,
			 BRep_Builder& BB,
			 TopTools_ListIteratorOfListOfShape& LIter,
			 TopoDS_Shape& myShape); 


#ifdef OCCT_DEBUG
Standard_IMPORT Standard_Integer TopOpeBRepTool_BOOOPE_CHECK_DEB;
#endif

//modified by NIZHNY-MZV  Wed Apr 19 17:19:11 2000
//see comments at the top of file TopOpeBRepBuild_Builder1.cxx
//about using of this global variable
extern Standard_Boolean GLOBAL_USE_NEW_BUILDER;
//
//modified by NIZNHY-PKV Sun Dec 15 17:17:56 2002 f
extern void FDSCNX_Close();// see TopOpeBRepDS_connex.cxx
extern void FDSSDM_Close();// see TopOpeBRepDS_samdom.cxx

//=======================================================================
//function : ~BRepAlgo_BooleanOperation
//purpose  : 
//=======================================================================
BRepAlgo_BooleanOperation::~BRepAlgo_BooleanOperation()
{
  FDSSDM_Close();
  FDSCNX_Close();
}
//modified by NIZNHY-PKV Sun Dec 15 17:17:58 2002 t

//=======================================================================
//function : BRepAlgoAPI_BooleanOperation
//purpose  : 
//=======================================================================
  BRepAlgo_BooleanOperation::BRepAlgo_BooleanOperation(const TopoDS_Shape& S1, 
						       const TopoDS_Shape& S2)
: myS1(S1),myS2(S2),myBuilderCanWork(Standard_False)
{
  TopOpeBRepDS_BuildTool BT;
  myHBuilder = new TopOpeBRepBuild_HBuilder(BT);
}

//=======================================================================
//function : PerformDS
//purpose  : 
//=======================================================================
  void BRepAlgo_BooleanOperation::PerformDS()
{
//  const Standard_Boolean CheckShapes = Standard_True;

  // create a data structure
  Handle(TopOpeBRepDS_HDataStructure) HDS;
  if (myHBuilder->DataStructure().IsNull())
    HDS = new TopOpeBRepDS_HDataStructure();
  else {
    HDS = myHBuilder->DataStructure();
    HDS->ChangeDS().Init();
  }

  // fill the data Structure
  TopOpeBRep_DSFiller DSFiller;
 
  // define face/face intersection tolerances
  Standard_Boolean forcetoli = Standard_False;
  if (forcetoli) {
    Standard_Real tolarc=0,toltang=0;
    TopOpeBRep_ShapeIntersector& tobsi = DSFiller.ChangeShapeIntersector();
    TopOpeBRep_FacesIntersector& tobfi = tobsi.ChangeFacesIntersector();
    tobfi.ForceTolerances(tolarc,toltang);
  }
  DSFiller.Insert(myS1,myS2,HDS);

  // 020499 : JYL : reject if there is an edge of the SD
  // not coded sameparameter and not degenerated
  Standard_Boolean esp = HDS->EdgesSameParameter();
  Standard_Boolean tede = Standard_True;
  if (!esp) {
    Standard_Integer i,n = HDS->NbShapes();
    for (i = 1 ; i <= n; i++) {
      const TopoDS_Shape& s = HDS->Shape(i);
      if ( s.ShapeType() == TopAbs_EDGE ) {
	const TopoDS_Edge& e = TopoDS::Edge(s);
	Standard_Boolean sp = BRep_Tool::SameParameter(e);
	Standard_Boolean de = BRep_Tool::Degenerated(e);
	if ( !sp && !de ) {
	  tede = Standard_False;
	  break;
	}
      }
    }
  }
  myBuilderCanWork = (esp || tede) ;
#ifdef OCCT_DEBUG
  if (!esp) cout<<"BRepAlgo_BooleanOperation(DEB) some edges not SameParameter"<<endl;
#endif  
  if (!myBuilderCanWork) return;
  
  Standard_Real tol3dAPPROX = 1e-7;
  Standard_Real tol2dAPPROX = 1e-7;
  // set tolerance values used by the APPROX process
  
  TopOpeBRepDS_BuildTool& BTofBuilder = myHBuilder->ChangeBuildTool();
  TopOpeBRepTool_GeomTool& GTofBTofBuilder = BTofBuilder.ChangeGeomTool();
  GTofBTofBuilder.SetTolerances(tol3dAPPROX,tol2dAPPROX);
  
  //modified by NIZHNY-MZV  Thu Apr 20 09:35:44 2000
  //see comments at the top of file TopOpeBRepBuild_Builder1.cxx
  //about using of this global variable
  GLOBAL_USE_NEW_BUILDER = Standard_True;
  myHBuilder->Perform(HDS,myS1,myS2);
  GLOBAL_USE_NEW_BUILDER = Standard_False;
}

//=======================================================================
//function : Perform
//purpose  : 
//=======================================================================
  void  BRepAlgo_BooleanOperation::Perform(const TopAbs_State St1, 
					   const TopAbs_State St2)
{
  if ( ! BuilderCanWork() ) {
    return;
  }

  // modif JYL suite aux modifs LBR #if MODIF ...
  // on privilegie le traitement KPart (si c'en est un) 
  // a tous les autres
  Standard_Integer kp = myHBuilder->IsKPart();
  BRep_Builder BB;
  Standard_Boolean sewing = Standard_True;
  if ( kp ) {
    //modified by NIZHNY-MZV  Thu Apr 20 09:34:33 2000
    //see comments at the top of file TopOpeBRepBuild_Builder1.cxx
    //about using of this global variable
    GLOBAL_USE_NEW_BUILDER = Standard_True;
    myHBuilder->MergeKPart(St1,St2);
    GLOBAL_USE_NEW_BUILDER = Standard_False;

    BB.MakeCompound(TopoDS::Compound(myShape));
    Done();

    TopTools_ListIteratorOfListOfShape its(myHBuilder->Merged(myS1,St1));
    for(; its.More(); its.Next()) BB.Add(myShape,its.Value());

  }
  else {
#if MODIF 

    //======================================================================
    //== Exploration of input shapes 
    //== Creation of the list of solids 
    //== Creation of the list of faces OUT OF solid
    //== Creation of the list of edges OUT OF face
    Standard_Integer nbs1,nbs2,nbf1,nbf2,nbe1,nbe2,nbv1,nbv2;

    TopTools_ListOfShape Solids1,Solids2,Faces1,Faces2,Edges1,Edges2,Vertex1,Vertex2;
    TopExp_Explorer Ex;
    for(Ex.Init(myS1,TopAbs_SOLID),nbs1=0; Ex.More(); Ex.Next()) {
      Solids1.Append(Ex.Current()); nbs1++;
    }
    for(Ex.Init(myS2,TopAbs_SOLID),nbs2=0; Ex.More(); Ex.Next()) { 
      Solids2.Append(Ex.Current()); nbs2++;
    }
    //== Faces not  in a solid
    for(Ex.Init(myS1,TopAbs_FACE,TopAbs_SOLID),nbf1=0; Ex.More(); Ex.Next())  { 
      Faces1.Append(Ex.Current()); nbf1++;
    }
    for(Ex.Init(myS2,TopAbs_FACE,TopAbs_SOLID),nbf2=0; Ex.More(); Ex.Next())  {
      Faces2.Append(Ex.Current()); nbf2++;
    }
    //== Edges not in a solid
    for(Ex.Init(myS1,TopAbs_EDGE,TopAbs_FACE),nbe1=0;  Ex.More(); Ex.Next())  { 
      Edges1.Append(Ex.Current()); nbe1++;
    }
    for(Ex.Init(myS2,TopAbs_EDGE,TopAbs_FACE),nbe2=0;  Ex.More(); Ex.Next()) {
      Edges2.Append(Ex.Current()); nbe2++;
    }
    //== Vertices not in an edge
    for(Ex.Init(myS1,TopAbs_VERTEX,TopAbs_EDGE),nbv1=0;  Ex.More(); Ex.Next())  { 
      Vertex1.Append(Ex.Current()); nbv1++;
    }
    for(Ex.Init(myS2,TopAbs_VERTEX,TopAbs_EDGE),nbv2=0;  Ex.More(); Ex.Next()) {
      Vertex2.Append(Ex.Current()); nbv2++;
    }

    //-- cout<<"Solids1: "<<nbs1<<"  Faces1: "<<nbf1<<" Edges1:"<<nbe1<<" Vtx1:"<<nbv1<<endl;
    //-- cout<<"Solids2: "<<nbs2<<"  Faces2: "<<nbf2<<" Edges2:"<<nbe2<<" Vtx2:"<<nbv2<<endl;

    //== 

    //== Reject operations without direction 
  

    //-- Cut Solid by Edge 
//    Standard_Boolean Correct = Standard_True;
    if(    (nbs1 && nbs2==0 && St1==TopAbs_OUT && St2==TopAbs_IN) 
       ||  (nbs2 && nbs1==0 && St2==TopAbs_OUT && St1==TopAbs_IN)) { 
      //-- cout<<"*****  Invalid Operation : Cut of a Solid by a Non Solid "<<endl;
      Done();
      return;
    }

    if(    (nbs1 && nbs2==0 && St1==TopAbs_OUT && St2==TopAbs_OUT) 
       ||  (nbs2 && nbs1==0 && St2==TopAbs_OUT && St1==TopAbs_OUT)) { 
      //-- cout<<"*****  Invalid Operation : Fusion of a Solid and a Non Solid "<<endl;
      Done();
      return;
    }
 

    if(    (nbs1>0 && nbs2>0) 
       &&  (nbe1 || nbe2 || nbf1 || nbf2 || nbv1 || nbv2)) { 
      //-- cout<<"***** Not Yet Implemented : Compound of solid and non Solid"<<endl;
      Done();
      return;
    }
    //======================================================================
    // make a compound with the new solids
    BB.MakeCompound(TopoDS::Compound(myShape));
    
    TopTools_ListIteratorOfListOfShape LIter;
    //----------------------------------------------------------------------
    TopoDS_Shape SNULL;
    
    if (nbf1 && nbf2) {
      SNULL.Nullify();
      if ( Opecom(St1,St2) ) {
	TopTools_ListIteratorOfListOfShape itloe = myHBuilder->Section();
	for(; itloe.More(); itloe.Next()) BB.Add(myShape,itloe.Value());
      } 
      else {
	if(nbf1) { 
	  myHBuilder->MergeShapes(myS1,St1,SNULL,St2);

	  for(LIter.Initialize(Faces1);LIter.More();LIter.Next()) {
	    if (myHBuilder->IsSplit(LIter.Value(),St1)) {
	      TopTools_ListIteratorOfListOfShape its;
	      for(its.Initialize(myHBuilder->Splits(LIter.Value(),St1));
		  its.More();its.Next()) BB.Add(myShape,its.Value());
	    }
	    else {
	      const TopoDS_Shape& LV = LIter.Value();
	      if(  (LV.Orientation() == TopAbs_EXTERNAL && St1==TopAbs_OUT ) 	    
		 ||(LV.Orientation() == TopAbs_INTERNAL && St1==TopAbs_IN  )) {
		BB.Add(myShape,LV);
	      }
	      else { 
		//-- Classify : 
		Sub_Classify(Ex,St1,Solids2,BB,LIter,myShape); 
	      }
	      //-- End Classification 
	    }
	  }
	} // nbf1
	SNULL.Nullify();    
	if ( Opefus(St1,St2) ) {
	  if(nbf2) {
	    myHBuilder->MergeShapes(SNULL,St1,myS2,St2);
	    for(LIter.Initialize(Faces2);LIter.More();LIter.Next()) {
	      if (myHBuilder->IsSplit(LIter.Value(),St2)) {
		TopTools_ListIteratorOfListOfShape its;
		for(its.Initialize(myHBuilder->Splits(LIter.Value(),St2));
		    its.More();its.Next()) BB.Add(myShape,its.Value());
	      }
	      else {
		const TopoDS_Shape& LV = LIter.Value();
		if(  (LV.Orientation() == TopAbs_EXTERNAL && St2==TopAbs_OUT ) 	    
		   ||(LV.Orientation() == TopAbs_INTERNAL && St2==TopAbs_IN  )) {
		  BB.Add(myShape,LV);
		}
		else { 
		  //-- Classify : 
		  Sub_Classify(Ex,St2,Solids1,BB,LIter,myShape); 
		}
		//-- End Classification 	
	      }
	    }
	  } // nbf2
	} // Fus
      }
    } // nbf1 && nbf2
    else if (nbf1 || nbf2) {
      SNULL.Nullify();
      if(nbf1) { 
	myHBuilder->MergeShapes(myS1,St1,SNULL,St2);
	// modified by IFV for treating operation between shell and solid
	const TopTools_ListOfShape& MergedShapes = myHBuilder->Merged(myS1,St1);
	TopTools_IndexedMapOfShape aMapOfFaces;

	sewing = Standard_False;

	if(MergedShapes.Extent() != 0) {
	  TopTools_ListIteratorOfListOfShape its(MergedShapes);
	  for(; its.More(); its.Next()) {
	    BB.Add(myShape,its.Value());
	  }
	  TopExp::MapShapes(myShape, TopAbs_FACE, aMapOfFaces);
	}

	for(LIter.Initialize(Faces1);LIter.More();LIter.Next()) {

	  if (myHBuilder->IsSplit(LIter.Value(),St1)) {
	    TopTools_ListIteratorOfListOfShape its;
	    for(its.Initialize(myHBuilder->Splits(LIter.Value(),St1));
		its.More();its.Next()) {
	      if(!aMapOfFaces.Contains(its.Value())) BB.Add(myShape,its.Value());
	    }
	  }
	  else {
	    const TopoDS_Shape& LV = LIter.Value();
	    if(!aMapOfFaces.Contains(LV)) {
	      if(  (LV.Orientation() == TopAbs_EXTERNAL && St1==TopAbs_OUT ) 	    
		 ||(LV.Orientation() == TopAbs_INTERNAL && St1==TopAbs_IN  )) {
		BB.Add(myShape,LV);
	      }
	      else { 
		//-- Classify : 
		Sub_Classify(Ex,St1,Solids2,BB,LIter,myShape); 
	      }
	      //-- End Classification 
	    }
	  }
	}
      } // nbf1
      SNULL.Nullify();    
      if(nbf2) {
	myHBuilder->MergeShapes(SNULL,St1,myS2,St2);
	// modified by IFV for treating operation between shell and solid
	const TopTools_ListOfShape& MergedShapes = myHBuilder->Merged(myS2,St2);
	TopTools_IndexedMapOfShape aMapOfFaces;
	sewing = Standard_False;

	if(MergedShapes.Extent() != 0) {
	  TopTools_ListIteratorOfListOfShape its(MergedShapes);
	  for(; its.More(); its.Next()) {
	    BB.Add(myShape,its.Value());
	  }
	  TopExp::MapShapes(myShape, TopAbs_FACE, aMapOfFaces);
	}

	for(LIter.Initialize(Faces2);LIter.More();LIter.Next()) {
	  if (myHBuilder->IsSplit(LIter.Value(),St2)) {
	    TopTools_ListIteratorOfListOfShape its;
	    for(its.Initialize(myHBuilder->Splits(LIter.Value(),St2));
		its.More();its.Next()) {
	      if(!aMapOfFaces.Contains(its.Value())) BB.Add(myShape,its.Value());
	    }
	  }
	  else {
	    const TopoDS_Shape& LV = LIter.Value();
	    if(!aMapOfFaces.Contains(LV)) {
	      if(  (LV.Orientation() == TopAbs_EXTERNAL && St2==TopAbs_OUT ) 	    
		 ||(LV.Orientation() == TopAbs_INTERNAL && St2==TopAbs_IN  )) {
		BB.Add(myShape,LV);
	      }
	      else { 
		//-- Classify : 
		Sub_Classify(Ex,St2,Solids1,BB,LIter,myShape); 
	      }
	      //-- End Classification 	
	    }
	  }
	}
      } // nbf2
    } // (nbf1 || nbf2)
    
    //----------------------------------------------------------------------
    if(nbe1) { 
      myHBuilder->MergeShapes(myS1,St1,SNULL,St2);

      for(LIter.Initialize(Edges1);LIter.More();LIter.Next()) {
	if (myHBuilder->IsSplit(LIter.Value(),St1)) {
	  TopTools_ListIteratorOfListOfShape its;
	  for(its.Initialize(myHBuilder->Splits(LIter.Value(),St1));
	      its.More();its.Next()) {
	    BB.Add(myShape,its.Value());
	  }
	}
	else {
	  const TopoDS_Shape& LV = LIter.Value();
	  if(  (LV.Orientation() == TopAbs_EXTERNAL && St1==TopAbs_OUT ) 	    
	     ||(LV.Orientation() == TopAbs_INTERNAL && St1==TopAbs_IN  )) {
	    BB.Add(myShape,LV);
	  }
	  else { 
	    //-- Classify : 
	    Sub_Classify(Ex,St1,Solids2,BB,LIter,myShape); 
	  }
	  //-- End Classification 
	}
      }
    }
    if(nbe2) { 
      myHBuilder->MergeShapes(SNULL,St1,myS2,St2);
      
      for(LIter.Initialize(Edges2);LIter.More();LIter.Next()) {
	if (myHBuilder->IsSplit(LIter.Value(),St2)) {
	  TopTools_ListIteratorOfListOfShape its;
	  for(its.Initialize(myHBuilder->Splits(LIter.Value(),St2));
	      its.More();its.Next()) {
	    BB.Add(myShape,its.Value());
	  }
	}
	else {
	  const TopoDS_Shape& LV = LIter.Value();
	  if(  (LV.Orientation() == TopAbs_EXTERNAL && St2==TopAbs_OUT ) 	    
	     ||(LV.Orientation() == TopAbs_INTERNAL && St2==TopAbs_IN  ))  {	 
	    BB.Add(myShape,LV);
	  }
	  else { 
	    //-- Classify : 
	    Sub_Classify(Ex,St2,Solids1,BB,LIter,myShape); 
	  }
	  //-- End Classification 
	}
      }
    }
    //----------------------------------------------------------------------
    //-- V1:Vertex1   state1 = OUT   -> Preserve V1 if V1 is Out all S2
    //-- V1:Vertex1   state1 = IN    -> Preserve V1 if V1 is In one of S2 
    if(nbv1 && nbs2) { 
      if(St1 == TopAbs_IN) { 
	for(LIter.Initialize(Vertex1);LIter.More();LIter.Next()) {
	  Standard_Boolean keep = Standard_False;
	  Standard_Boolean ok = Standard_True;
	  const TopoDS_Vertex& V=TopoDS::Vertex(LIter.Value());
	  gp_Pnt P=BRep_Tool::Pnt(V);
	  Standard_Real Tol = BRep_Tool::Tolerance(V);
	  TopTools_ListIteratorOfListOfShape SIter;
	  for(SIter.Initialize(Solids2);
	      SIter.More() && ok==Standard_True;
	      SIter.Next()) { 	  
	    BRepClass3d_SolidClassifier SolClass(SIter.Value());
	    SolClass.Perform(P,Tol);
	    if(SolClass.State() == TopAbs_IN) {
	      ok=Standard_False;
	      keep = Standard_True;
	    }
	  }
	  if(keep) { 
	    BB.Add(myShape,LIter.Value());
	  }
	}
      }
      else { 
	if(St1 == TopAbs_OUT) { 
	  for(LIter.Initialize(Vertex1);LIter.More();LIter.Next()) {
	    Standard_Boolean keep = Standard_True;
	    Standard_Boolean ok = Standard_True;
	    const TopoDS_Vertex& V=TopoDS::Vertex(LIter.Value());
	    gp_Pnt P=BRep_Tool::Pnt(V);
	    Standard_Real Tol = BRep_Tool::Tolerance(V);
	    TopTools_ListIteratorOfListOfShape SIter;
	    for(SIter.Initialize(Solids2);
		SIter.More() && ok==Standard_True;
		SIter.Next()) { 	  
	      BRepClass3d_SolidClassifier SolClass(SIter.Value());
	      SolClass.Perform(P,Tol);
	      if(SolClass.State() != TopAbs_OUT) {
		keep = Standard_False;
		ok   = Standard_False;
	      }
	    }
	    if(keep) { 
	      BB.Add(myShape,LIter.Value());
	    }
	  }
	}
      }    
    }
  
    if(nbv2 && nbs1) { 
      if(St2 == TopAbs_IN) { 
	for(LIter.Initialize(Vertex2);LIter.More();LIter.Next()) {
	  Standard_Boolean keep = Standard_False;
	  Standard_Boolean ok = Standard_True;
	  const TopoDS_Vertex& V=TopoDS::Vertex(LIter.Value());
	  gp_Pnt P=BRep_Tool::Pnt(V);
	  Standard_Real Tol = BRep_Tool::Tolerance(V);
	  TopTools_ListIteratorOfListOfShape SIter;
	  for(SIter.Initialize(Solids1);
	      SIter.More() && ok==Standard_True;
	      SIter.Next()) { 	  
	    BRepClass3d_SolidClassifier SolClass(SIter.Value());
	    SolClass.Perform(P,Tol);
	    if(SolClass.State() == TopAbs_IN) {
	      ok=Standard_False;
	      keep = Standard_True;
	    }
	  }
	  if(keep) { 
	    BB.Add(myShape,LIter.Value());
	  }
	}
      }
      else { 
	if(St2 == TopAbs_OUT) { 
	  for(LIter.Initialize(Vertex2);LIter.More();LIter.Next()) {
	    Standard_Boolean keep = Standard_True;
	    Standard_Boolean ok = Standard_True;
	    const TopoDS_Vertex& V=TopoDS::Vertex(LIter.Value());
	    gp_Pnt P=BRep_Tool::Pnt(V);
	    Standard_Real Tol = BRep_Tool::Tolerance(V);
	    TopTools_ListIteratorOfListOfShape SIter;
	    for(SIter.Initialize(Solids1);
		SIter.More() && ok==Standard_True;
		SIter.Next()) { 	  
	      BRepClass3d_SolidClassifier SolClass(SIter.Value());
	      SolClass.Perform(P,Tol);
	      if(SolClass.State() != TopAbs_OUT) {
		keep = Standard_False;
		ok   = Standard_False;
	      }
	    }
	    if(keep) { 
	      BB.Add(myShape,LIter.Value());
	    }
	  }
	}
      }    
    }
    
    if(nbs1 && nbs2 ) { 
      myHBuilder->MergeShapes(myS1,St1,myS2,St2);
      if(myHBuilder->IsMerged(myS1,St1)) { 
        TopTools_ListIteratorOfListOfShape its;
	its = myHBuilder->Merged(myS1,St1);
	Standard_Integer nbSolids = 0;
	for(; its.More(); its.Next(), nbSolids++) { 
	  BB.Add(myShape,its.Value());
	}
      }
    }
  
#else 

    myHBuilder->MergeSolids(myS1,St1,myS2,St2);
    TopTools_ListIteratorOfListOfShape its;
  
    BB.MakeCompound(TopoDS::Compound(myShape));
    its = myHBuilder->Merged(myS1,St1);
    while (its.More()) {
      BB.Add(myShape,its.Value());
      its.Next();
    }

#endif
// #if MODIF

  }

  // Creation of the Map used in IsDeleted.
  TopExp_Explorer ex;
  ex.Init(myShape,TopAbs_FACE);
  for (; ex.More(); ex.Next()) myMap.Add(ex.Current());
  ex.Init(myShape,TopAbs_EDGE); // for FRIKO
  for (; ex.More(); ex.Next()) myMap.Add(ex.Current());
  
  // Checking same parameter of new edges of section
  Standard_Real eTol,cTol;
  for (myHBuilder->InitSection(1); 
       myHBuilder->MoreSection(); 
       myHBuilder->NextSection()) {
    const TopoDS_Shape& cur = myHBuilder->CurrentSection();
    if (cur.ShapeType()==TopAbs_EDGE) {
      BRepCheck_Edge bce(TopoDS::Edge(cur));
      cTol=bce.Tolerance();
      eTol = BRep_Tool::Tolerance(TopoDS::Edge(cur));
      if (eTol<cTol) {
	BB.UpdateEdge(TopoDS::Edge(cur), cTol);
	for (ex.Init(cur, TopAbs_VERTEX); ex.More(); ex.Next()) {
	  eTol = BRep_Tool::Tolerance(TopoDS::Vertex(ex.Current()));
	  if (eTol<cTol) {
	    // Update can only increase tolerance, so if the vertex 
	    // has a greater tolerance thanits edges it is not touched
	    BB.UpdateVertex(TopoDS::Vertex(ex.Current()), cTol);
	  }
	}
      }
    }
  }

  Standard_Real maxTol = RealLast();    // MSV: unlimit tolerance
  TopOpeBRepBuild_Tools::CorrectTolerances(myShape,maxTol);

  TopExp_Explorer ex1, ex2, ex3;
  TopTools_ListOfShape theOldShell, theNewShell;
  Standard_Boolean modif =Standard_False;

#ifdef OCCT_DEBUG
  Standard_Boolean nosew = TopOpeBRepTool_GetcontextNOSEW();
  if (nosew) sewing = Standard_False;
#endif

  if (sewing) {
    topToSew.Clear();
    for (ex1.Init(myShape, TopAbs_SHELL); ex1.More(); ex1.Next()) {
      BRepCheck_Shell bcs(TopoDS::Shell(ex1.Current()));
      if (bcs.Closed()==BRepCheck_NotClosed) {
	// it is required to add them face by face to avoid IsModified on faces
	BRepBuilderAPI_Sewing brts;
	for (ex3.Init(ex1.Current(), TopAbs_FACE); ex3.More(); ex3.Next()) {
	  brts.Add(ex3.Current());
	}
	brts.Perform();
	ex2.Init(brts.SewedShape(), TopAbs_SHELL);
	if (ex2.More()) {
	  ex2.Next();
	  if (!ex2.More()) {
	    ex2.Init(brts.SewedShape(), TopAbs_SHELL);
	    theOldShell.Append(ex1.Current());
	    theNewShell.Append(ex2.Current());
	    modif =Standard_True;
	    for (ex3.Init(ex1.Current(), TopAbs_EDGE); ex3.More(); ex3.Next()) {
	      const TopoDS_Edge& ledg = TopoDS::Edge(ex3.Current());
	      if (brts.IsSectionBound(ledg)) {
		topToSew.Bind(ledg, brts.SectionToBoundary(ledg));
		if (!BRep_Tool::SameParameter(brts.SectionToBoundary(ledg))) {
		  BRepLib::SameParameter(ledg, BRep_Tool::Tolerance(brts.SectionToBoundary(ledg)));
		}
	      }
	    }
	    for (ex3.Init(ex1.Current(), TopAbs_FACE); ex3.More(); ex3.Next()) {
	      if (brts.IsModified(ex3.Current())) {
		topToSew.Bind(ex3.Current(), brts.Modified(ex3.Current()));
	      }
	    }
	  }
	}
      }
    }
  } // sewing

  if (modif) {
    BRepTools_Substitution bsub;
    TopTools_ListIteratorOfListOfShape itl(theOldShell);
    TopTools_ListOfShape forSub;
    for (; itl.More();itl.Next()) {
      forSub.Append(theNewShell.First());
      bsub.Substitute(itl.Value(), forSub);
      theNewShell.RemoveFirst();
      forSub.Clear();
    }
    bsub.Build(myShape);
    if (bsub.IsCopied(myShape)) {
      myShape=(bsub.Copy(myShape)).First();
    }
  }
  
  Done();
}



//=======================================================================
//function : Builder
//purpose  : 
//=======================================================================
  Handle(TopOpeBRepBuild_HBuilder) BRepAlgo_BooleanOperation::Builder()const 
{
  return myHBuilder;
}


//=======================================================================
//function : TopoDS_Shape&
//purpose  : 
//=======================================================================
  const TopoDS_Shape& BRepAlgo_BooleanOperation::Shape1() const 
{
  return myS1;
}


//=======================================================================
//function : TopoDS_Shape&
//purpose  : 
//=======================================================================
  const TopoDS_Shape& BRepAlgo_BooleanOperation::Shape2() const 
{
  return myS2;
}

//=======================================================================
//function : BuilderCanWork
//purpose  : 
//=======================================================================
  void BRepAlgo_BooleanOperation::BuilderCanWork(const Standard_Boolean Val)
{
  myBuilderCanWork = Val;
}

//=======================================================================
//function : BuilderCanWork
//purpose  : 
//=======================================================================
  Standard_Boolean BRepAlgo_BooleanOperation::BuilderCanWork() const
{
  return myBuilderCanWork;
}


void Sub_Classify(TopExp_Explorer& Ex,
		  const TopAbs_State St1,
		  TopTools_ListOfShape& Solids2,
		  BRep_Builder& BB,
		  TopTools_ListIteratorOfListOfShape& LIter,
		  TopoDS_Shape& myShape) { 
  Ex.Init(LIter.Value(),TopAbs_VERTEX);
  if(Ex.More()) { 
    if(St1 == TopAbs_IN) { 
      Standard_Boolean keep = Standard_False;
      Standard_Boolean ok = Standard_True;
      const TopoDS_Vertex& V=TopoDS::Vertex(Ex.Current());
      gp_Pnt P=BRep_Tool::Pnt(V);
      Standard_Real Tol = BRep_Tool::Tolerance(V);
      TopTools_ListIteratorOfListOfShape SIter;
      for(SIter.Initialize(Solids2);
	  SIter.More() && ok==Standard_True;
	  SIter.Next()) { 	  
	BRepClass3d_SolidClassifier SolClass(SIter.Value());
	SolClass.Perform(P,Tol);
	if(SolClass.State() == TopAbs_IN) {
	  ok=Standard_False;
	  keep = Standard_True;
	}
      }
      if(keep) { 
	BB.Add(myShape,LIter.Value());
      }
    }
    else { 
      if(St1 == TopAbs_OUT) { 
	Standard_Boolean keep = Standard_True;
	Standard_Boolean ok = Standard_True;
	const TopoDS_Vertex& V=TopoDS::Vertex(Ex.Current());
	gp_Pnt P=BRep_Tool::Pnt(V);
	Standard_Real Tol = BRep_Tool::Tolerance(V);
	TopTools_ListIteratorOfListOfShape SIter;
	for(SIter.Initialize(Solids2);
	    SIter.More() && ok==Standard_True;
	    SIter.Next()) { 	  
	  BRepClass3d_SolidClassifier SolClass(SIter.Value());
	  SolClass.Perform(P,Tol);
	  if(SolClass.State() != TopAbs_OUT) {
	    keep = Standard_False;
	    ok   = Standard_False;
	  }
	}
	if(keep) { 
	  BB.Add(myShape,LIter.Value());
	}
      }
    }
  }
}


//=======================================================================
//function : InitParameters
//purpose  : Info on geometry : PCurve, Approx, ...
//=======================================================================
void BRepAlgo_BooleanOperation::InitParameters()
{
  TopOpeBRepDS_BuildTool& BTofBuilder = myHBuilder->ChangeBuildTool();
  TopOpeBRepTool_GeomTool& GTofBTofBuilder = BTofBuilder.ChangeGeomTool();

  GTofBTofBuilder.Define(TopOpeBRepTool_APPROX);
  GTofBTofBuilder.DefineCurves(Standard_True);
  GTofBTofBuilder.DefinePCurves1(Standard_True);
  GTofBTofBuilder.DefinePCurves2(Standard_True);
}

//=======================================================================
//function : Modified
//purpose  : 
//=======================================================================
const TopTools_ListOfShape& BRepAlgo_BooleanOperation::Modified(const TopoDS_Shape& S) 
{
  myGenerated.Clear();
  TopTools_MapOfShape aMap; // to check if shape can be added in list more then one time
  aMap.Clear();
  if (myHBuilder->IsSplit(S, TopAbs_OUT)) {
    TopTools_ListIteratorOfListOfShape It(myHBuilder->Splits(S, TopAbs_OUT));
    for(;It.More();It.Next()) {
      if (topToSew.IsBound(It.Value())) 
	{if(aMap.Add(topToSew.Find(It.Value()))) myGenerated.Append(topToSew.Find(It.Value()));}
      else
	{if(aMap.Add(It.Value())) myGenerated.Append(It.Value());}
    }
  }
  if (myHBuilder->IsSplit(S, TopAbs_IN)) {
    TopTools_ListIteratorOfListOfShape It(myHBuilder->Splits(S, TopAbs_IN));
    for(;It.More();It.Next()) {
      if (topToSew.IsBound(It.Value())) 
	{if(aMap.Add(topToSew.Find(It.Value()))) myGenerated.Append(topToSew.Find(It.Value()));}
      else
	{if(aMap.Add(It.Value())) myGenerated.Append(It.Value());}
    }
  }
  if (myHBuilder->IsSplit(S, TopAbs_ON)) {
    TopTools_ListIteratorOfListOfShape It(myHBuilder->Splits(S, TopAbs_ON));
    for(;It.More();It.Next()) {
      if (topToSew.IsBound(It.Value())) 
	{if(aMap.Add(topToSew.Find(It.Value()))) myGenerated.Append(topToSew.Find(It.Value()));}
      else
	{if(aMap.Add(It.Value())) myGenerated.Append(It.Value());}
    }
  }

  if (myHBuilder->IsMerged(S, TopAbs_OUT)) {
    TopTools_ListIteratorOfListOfShape It(myHBuilder->Merged(S, TopAbs_OUT));
    for(;It.More();It.Next()) {
      if (topToSew.IsBound(It.Value())) 
	{if(aMap.Add(topToSew.Find(It.Value()))) myGenerated.Append(topToSew.Find(It.Value()));}
      else
	{if(aMap.Add(It.Value())) myGenerated.Append(It.Value());}
    }
  }
  if (myHBuilder->IsMerged(S, TopAbs_IN)) {
    TopTools_ListIteratorOfListOfShape It(myHBuilder->Merged(S, TopAbs_IN));
    for(;It.More();It.Next()) {
      if (topToSew.IsBound(It.Value())) 
	{if(aMap.Add(topToSew.Find(It.Value()))) myGenerated.Append(topToSew.Find(It.Value()));}
      else
	{if(aMap.Add(It.Value())) myGenerated.Append(It.Value());}
    }
  }
  if (myHBuilder->IsMerged(S, TopAbs_ON)) {
    TopTools_ListIteratorOfListOfShape It(myHBuilder->Merged(S, TopAbs_ON));
    for(;It.More();It.Next()) {
      if (topToSew.IsBound(It.Value())) 
	{if(aMap.Add(topToSew.Find(It.Value()))) myGenerated.Append(topToSew.Find(It.Value()));}
      else
	{if(aMap.Add(It.Value())) myGenerated.Append(It.Value());}
    }
  }
  return myGenerated;
}


//=======================================================================
//function : IsDeleted
//purpose  : 
//=======================================================================
Standard_Boolean BRepAlgo_BooleanOperation::IsDeleted(const TopoDS_Shape& S) 
{
  Standard_Boolean Deleted = Standard_True; 
  if (myMap.Contains(S) || 
      myHBuilder->IsMerged(S, TopAbs_OUT) ||
      myHBuilder->IsMerged(S, TopAbs_IN)  ||
      myHBuilder->IsMerged(S, TopAbs_ON)  ||
      myHBuilder->IsSplit (S, TopAbs_OUT)  ||
      myHBuilder->IsSplit (S, TopAbs_IN)   ||
      myHBuilder->IsSplit (S, TopAbs_ON))
    return Standard_False;

  return Deleted;    
}