// Created by: Peter KURNEV
// Copyright (c) 2010-2014 OPEN CASCADE SAS
// Copyright (c) 2007-2010 CEA/DEN, EDF R&D, OPEN CASCADE
// Copyright (c) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, CEDRAT,
//                         EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
//
// 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 <BOPAlgo_Builder.hxx>
#include <BOPAlgo_Alerts.hxx>
#include <BOPAlgo_BuilderFace.hxx>
#include <BOPAlgo_PaveFiller.hxx>
#include <BOPAlgo_Tools.hxx>
#include <BOPDS_DS.hxx>
#include <BOPDS_FaceInfo.hxx>
#include <BOPDS_Interf.hxx>
#include <BOPDS_PaveBlock.hxx>
#include <BOPDS_ShapeInfo.hxx>
#include <BOPDS_VectorOfInterfFF.hxx>
#include <BOPTools_AlgoTools.hxx>
#include <BOPTools_AlgoTools2D.hxx>
#include <BOPTools_AlgoTools3D.hxx>
#include <BOPTools_MapOfSet.hxx>
#include <BOPTools_Parallel.hxx>
#include <BRep_Builder.hxx>
#include <BRepLib.hxx>
#include <BRep_Tool.hxx>
#include <GeomLib.hxx>
#include <NCollection_IncAllocator.hxx>
#include <NCollection_Vector.hxx>
#include <IntTools_Context.hxx>
#include <TColStd_ListOfInteger.hxx>
#include <TColStd_MapOfInteger.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Compound.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopTools_ListOfShape.hxx>

#include <algorithm>
//
static
  TopoDS_Face BuildDraftFace(const TopoDS_Face& theFace,
                             const TopTools_DataMapOfShapeListOfShape& theImages,
                             Handle(IntTools_Context)& theCtx,
                             const Handle(Message_Report)& theReport);

//=======================================================================
//class    : BOPAlgo_PairOfShapeBoolean
//purpose  : 
//=======================================================================
class BOPAlgo_PairOfShapeBoolean : public BOPAlgo_ParallelAlgo {

 public:
  DEFINE_STANDARD_ALLOC

  BOPAlgo_PairOfShapeBoolean() : 
    BOPAlgo_ParallelAlgo(),
    myFlag(Standard_False) {
  }
  //
  virtual ~BOPAlgo_PairOfShapeBoolean() {
  }
  //
  TopoDS_Shape& Shape1() {
    return myShape1;
  }
  //
  TopoDS_Shape& Shape2() {
    return myShape2;
  }
  //
  Standard_Boolean& Flag() {
    return myFlag;
  }
  //
  void SetContext(const Handle(IntTools_Context)& aContext) {
    myContext=aContext;
  }
  //
  const Handle(IntTools_Context)& Context()const {
    return myContext;
  }
  //
  virtual void Perform() {
    Message_ProgressScope aPS(myProgressRange, NULL, 1);
    if (UserBreak(aPS))
    {
      return;
    }

    const TopoDS_Face& aFj=*((TopoDS_Face*)&myShape1);
    const TopoDS_Face& aFk=*((TopoDS_Face*)&myShape2);
    myFlag=BOPTools_AlgoTools::AreFacesSameDomain(aFj, aFk, myContext, myFuzzyValue);
  }
  //
 protected: 
  Standard_Boolean myFlag;
  TopoDS_Shape myShape1;
  TopoDS_Shape myShape2;
  Handle(IntTools_Context) myContext;
};
//
typedef NCollection_Vector<BOPAlgo_PairOfShapeBoolean> BOPAlgo_VectorOfPairOfShapeBoolean;

//=======================================================================
//class   : BOPAlgo_SplitFace
//purpose : Auxiliary class to extend BOPAlgo_BuilderFace with progress support
//=======================================================================
class BOPAlgo_SplitFace : public BOPAlgo_BuilderFace
{
public:
  //! Sets progress range
  void SetProgressRange(const Message_ProgressRange& theRange)
  {
    myRange = theRange;
  }

  // New perform method, using own progress range
  void Perform()
  {
    Message_ProgressScope aPS(myRange, NULL, 1);
    if (!aPS.More())
    {
      return;
    }
    BOPAlgo_BuilderFace::Perform(aPS.Next());
  }

private:
  //! Disable the range enabled method
  virtual void Perform(const Message_ProgressRange& /*theRange*/) {};

private:
  Message_ProgressRange myRange;
};
typedef NCollection_Vector<BOPAlgo_SplitFace> BOPAlgo_VectorOfBuilderFace;

//=======================================================================
//class    : BOPAlgo_VFI
//purpose  : 
//=======================================================================
class BOPAlgo_VFI : public BOPAlgo_ParallelAlgo {

 public:
  DEFINE_STANDARD_ALLOC
  
  BOPAlgo_VFI() :
    BOPAlgo_ParallelAlgo(),
    myIsInternal(Standard_False) {
  }
  //
  virtual ~BOPAlgo_VFI(){
  }
  //
  void SetVertex(const TopoDS_Vertex& aV) {
    myV=aV;
  }
  //
  TopoDS_Vertex& Vertex() {
    return myV;
  }
  //
  void SetFace(const TopoDS_Face& aF) {
    myF=aF;
  }
  //
  TopoDS_Face& Face() {
    return myF;
  }
  //
  Standard_Boolean IsInternal()const {
    return myIsInternal;
  }
  //
  void SetContext(const Handle(IntTools_Context)& aContext) {
    myContext=aContext;
  }
  //
  const Handle(IntTools_Context)& Context()const {
    return myContext;
  }
  //
  virtual void Perform() {
    Message_ProgressScope aPS(myProgressRange, NULL, 1);
    if (UserBreak(aPS))
    {
      return;
    }

    Standard_Real aT1, aT2, dummy;
    //
    Standard_Integer iFlag =
      myContext->ComputeVF(myV, myF, aT1, aT2, dummy, myFuzzyValue);
    myIsInternal = (iFlag == 0);
  }
  //
 protected:
  Standard_Boolean myIsInternal;
  TopoDS_Vertex myV;
  TopoDS_Face myF;
  Handle(IntTools_Context) myContext;
};
//
typedef NCollection_Vector<BOPAlgo_VFI> BOPAlgo_VectorOfVFI; 

//=======================================================================
//function : FillImagesFaces
//purpose  : 
//=======================================================================
void BOPAlgo_Builder::FillImagesFaces(const Message_ProgressRange& theRange)
{
  Message_ProgressScope aPS(theRange, "Filing spligs of faces", 10);
  BuildSplitFaces(aPS.Next(9));
  if (HasErrors())
  {
    return;
  }
  FillSameDomainFaces(aPS.Next(0.5));
  if (HasErrors())
  {
    return;
  }
  FillInternalVertices(aPS.Next(0.5));
}
//=======================================================================
//function : BuildSplitFaces
//purpose  : 
//=======================================================================
void BOPAlgo_Builder::BuildSplitFaces(const Message_ProgressRange& theRange)
{
  Standard_Boolean bHasFaceInfo, bIsClosed, bIsDegenerated, bToReverse;
  Standard_Integer i, j, k, aNbS, aNbPBIn, aNbPBOn, aNbPBSc, aNbAV, nSp;
  TopoDS_Face aFF, aFSD;
  TopoDS_Edge aSp, aEE;
  TopAbs_Orientation anOriF, anOriE;
  TopExp_Explorer aExp;
  TopTools_ListIteratorOfListOfShape aIt;
  TColStd_ListOfInteger aLIAV;
  TopTools_MapOfShape aMFence;
  Handle(NCollection_BaseAllocator) aAllocator;
  BOPAlgo_VectorOfBuilderFace aVBF;
  //
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~scope f
  aAllocator=
    NCollection_BaseAllocator::CommonBaseAllocator();
  //
  TopTools_ListOfShape aLE(aAllocator);
  TopTools_MapOfShape aMDE(100, aAllocator);
  //
  Message_ProgressScope aPSOuter(theRange, NULL, 10);
  // Build temporary map of faces images to avoid rebuilding
  // of the faces without any IN or section edges
  NCollection_IndexedDataMap<Standard_Integer, TopTools_ListOfShape> aFacesIm;
  //
  aNbS=myDS->NbSourceShapes();
  //
  for (i=0; i<aNbS; ++i) {
    const BOPDS_ShapeInfo& aSI=myDS->ShapeInfo(i);
    if (aSI.ShapeType()!=TopAbs_FACE) {
      continue;
    }
    if (UserBreak(aPSOuter))
    {
      return;
    }
    //
    const TopoDS_Face& aF=(*(TopoDS_Face*)(&aSI.Shape()));
    Standard_Boolean isUClosed = Standard_False,
                     isVClosed = Standard_False,
                     isChecked = Standard_False;
    //
    bHasFaceInfo=myDS->HasFaceInfo(i);
    if(!bHasFaceInfo) {
      continue;
    }
    //
    const BOPDS_FaceInfo& aFI=myDS->FaceInfo(i);
    //
    const BOPDS_IndexedMapOfPaveBlock& aMPBIn=aFI.PaveBlocksIn();
    const BOPDS_IndexedMapOfPaveBlock& aMPBOn=aFI.PaveBlocksOn();
    const BOPDS_IndexedMapOfPaveBlock& aMPBSc=aFI.PaveBlocksSc();
    aLIAV.Clear();
    myDS->AloneVertices(i, aLIAV);
    
    aNbPBIn=aMPBIn.Extent();
    aNbPBOn=aMPBOn.Extent();
    aNbPBSc=aMPBSc.Extent();
    aNbAV=aLIAV.Extent();
    if (!aNbPBIn && !aNbPBOn && !aNbPBSc && !aNbAV) { // not compete
      continue;
    }

    if (!aNbPBIn && !aNbPBSc)
    {
      // If there are any alone vertices to be put in the face,
      // the new face has to be created even if the wires of the
      // face have not been modified.

      // It is also necessary to check if the face contains any internal edges,
      // as such edges may split the face on parts and it is better
      // to send the face be treated by the BuilderFace algorithm.
      // In case of alone vertices the check for internals will be performed
      // in the BuildDraftFace method.
      Standard_Boolean hasInternals = Standard_False;
      if (!aNbAV)
      {
        // Check if any wires of the face have been modified.
        // If no modified and internal wires present in the face
        // there is no need to create the new face.
        Standard_Boolean hasModified = Standard_False;

        TopoDS_Iterator aItW(aF);
        for (; aItW.More(); aItW.Next())
        {
          TopoDS_Iterator itE(aItW.Value());
          hasInternals = (itE.More() && (itE.Value().Orientation() == TopAbs_INTERNAL));
          if (hasInternals)
            break;

          hasModified |= myImages.IsBound(aItW.Value());
        }

        if (!hasInternals && !hasModified)
          continue;
      }

      if (!hasInternals)
      {
        // No internal parts for the face, so just build the draft face
        // and keep it to pass directly into result.
        // If the original face has any internal edges or multi-connected vertices,
        // the draft face will be null, as such sub-shapes may split the face on parts
        // (as in the case "bugs modalg_5 bug25245_1").
        // The BuilderFace algorithm will be called in this case.
        TopoDS_Face aFD = BuildDraftFace(aF, myImages, myContext, myReport);
        if (!aFD.IsNull())
        {
          aFacesIm(aFacesIm.Add(i, TopTools_ListOfShape())).Append(aFD);
          continue;
        }
      }
    }

    aMFence.Clear();
    //
    anOriF=aF.Orientation();
    aFF=aF;
    aFF.Orientation(TopAbs_FORWARD);
    //
    // 1. Fill the edges set for the face aFF -> LE
    aLE.Clear();

    // 1.1 Bounding edges
    aExp.Init(aFF, TopAbs_EDGE);
    for (; aExp.More(); aExp.Next()) {
      const TopoDS_Edge& aE=(*(TopoDS_Edge*)(&aExp.Current()));
      anOriE=aE.Orientation();
      //
      if (!myImages.IsBound(aE)) {
        if (anOriE==TopAbs_INTERNAL) {
          aEE=aE;
          aEE.Orientation(TopAbs_FORWARD);
          aLE.Append(aEE);
          aEE.Orientation(TopAbs_REVERSED);
          aLE.Append(aEE);
        }
        else {
          aLE.Append(aE);
        }

        continue;
      }

      if(!isChecked)
      {
        const Handle(Geom_Surface) aSurf = BRep_Tool::Surface(aF);
        GeomLib::IsClosed(aSurf, BRep_Tool::Tolerance(aE),
          isUClosed, isVClosed);

        isChecked = Standard_True;
      }

      bIsClosed = Standard_False;

      if((isUClosed || isVClosed) && BRep_Tool::IsClosed(aE, aF)) 
      {

        Standard_Boolean isUIso = Standard_False, isVIso = Standard_False;
        BOPTools_AlgoTools2D::IsEdgeIsoline(aE, aF, isUIso, isVIso);

        bIsClosed = ((isUClosed && isUIso) || (isVClosed && isVIso));
      }

      bIsDegenerated=BRep_Tool::Degenerated(aE);

      const TopTools_ListOfShape& aLIE=myImages.Find(aE);
      aIt.Initialize(aLIE);
      for (; aIt.More(); aIt.Next()) {
        aSp=(*(TopoDS_Edge*)(&aIt.Value()));
        if (bIsDegenerated) {
          aSp.Orientation(anOriE);
          aLE.Append(aSp);
          continue;
        }
        //
        if (anOriE==TopAbs_INTERNAL) {
          aSp.Orientation(TopAbs_FORWARD);
          aLE.Append(aSp);
          aSp.Orientation(TopAbs_REVERSED);
          aLE.Append(aSp);
          continue;
        }
          //
        if (bIsClosed) {
          if (aMFence.Add(aSp)) {
            if (!BRep_Tool::IsClosed(aSp, aF)){
              if (!BOPTools_AlgoTools3D::DoSplitSEAMOnFace(aSp, aF))
              {
                // try different approach
                if (!BOPTools_AlgoTools3D::DoSplitSEAMOnFace(aE, aSp, aF))
                {
                  TopoDS_Compound aWS;
                  BRep_Builder().MakeCompound (aWS);
                  BRep_Builder().Add (aWS, aF);
                  BRep_Builder().Add (aWS, aSp);
                  AddWarning (new BOPAlgo_AlertUnableToMakeClosedEdgeOnFace (aWS));
                }
              }
            }
            //
            aSp.Orientation(TopAbs_FORWARD);
            aLE.Append(aSp);
            aSp.Orientation(TopAbs_REVERSED);
            aLE.Append(aSp);
          }// if (aMFence.Add(aSp))
          continue;
        }// if (bIsClosed){
        //
        aSp.Orientation(anOriE);
        bToReverse=BOPTools_AlgoTools::IsSplitToReverseWithWarn(aSp, aE, myContext, myReport);
        if (bToReverse) {
          aSp.Reverse();
        }
        aLE.Append(aSp);
      }// for (; aIt.More(); aIt.Next()) {
    }// for (; aExp.More(); aExp.Next()) {
    // 
    //
    // 1.2 In edges
    for (j=1; j<=aNbPBIn; ++j) {
      const Handle(BOPDS_PaveBlock)& aPB=aMPBIn(j);
      nSp=aPB->Edge();
      Standard_ASSERT(nSp >= 0, "Face information is not up to date", continue);
      aSp=(*(TopoDS_Edge*)(&myDS->Shape(nSp)));
      //
      aSp.Orientation(TopAbs_FORWARD);
      aLE.Append(aSp);
      aSp.Orientation(TopAbs_REVERSED);
      aLE.Append(aSp);
    }
    //
    //
    // 1.3 Section edges
    for (j=1; j<=aNbPBSc; ++j) {
      const Handle(BOPDS_PaveBlock)& aPB=aMPBSc(j);
      nSp=aPB->Edge();
      aSp=(*(TopoDS_Edge*)(&myDS->Shape(nSp)));
      //
      aSp.Orientation(TopAbs_FORWARD);
      aLE.Append(aSp);
      aSp.Orientation(TopAbs_REVERSED);
      aLE.Append(aSp);
    }
    //
    if (!myPaveFiller->NonDestructive()) {
      // speed up for planar faces
      BRepLib::BuildPCurveForEdgesOnPlane(aLE, aFF);
    }
    // 3 Build split faces
    BOPAlgo_SplitFace& aBF=aVBF.Appended();
    aBF.SetFace(aF);
    aBF.SetShapes(aLE);
    aBF.SetRunParallel(myRunParallel);
    //
  }// for (i=0; i<aNbS; ++i) {

  // close preparation task
  aPSOuter.Next();
  //
  Standard_Integer aNbBF = aVBF.Length();
  // Set progress range for each task to be run in parallel
  Message_ProgressScope aPSParallel(aPSOuter.Next(9), "Splitting faces", aNbBF);
  for (Standard_Integer iF = 0; iF < aNbBF; iF++)
  {
    BOPAlgo_SplitFace& aBF = aVBF.ChangeValue(iF);
    aBF.SetProgressRange(aPSParallel.Next());
  }
  //===================================================
  BOPTools_Parallel::Perform (myRunParallel, aVBF);
  //===================================================
  if (UserBreak(aPSOuter))
  {
    return;
  }
  for (k = 0; k < aNbBF; ++k)
  {
    BOPAlgo_BuilderFace& aBF = aVBF(k);
    aFacesIm.Add(myDS->Index(aBF.Face()), aBF.Areas());
    myReport->Merge(aBF.GetReport());
  }

  aNbBF = aFacesIm.Extent();
  for (k = 1; k <= aNbBF; ++k)
  {
    const TopoDS_Face& aF = TopoDS::Face(myDS->Shape(aFacesIm.FindKey(k)));
    anOriF = aF.Orientation();
    const TopTools_ListOfShape& aLFR = aFacesIm(k);
    //
    TopTools_ListOfShape* pLFIm = myImages.Bound(aF, TopTools_ListOfShape());
    aIt.Initialize(aLFR);
    for (; aIt.More(); aIt.Next()) {
      TopoDS_Shape& aFR=aIt.ChangeValue();
      if (anOriF==TopAbs_REVERSED)
        aFR.Orientation(TopAbs_REVERSED);
      pLFIm->Append(aFR);
    }
  }
  //
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~scope t
}

//=======================================================================
//function : AddEdgeSet
//purpose  : 
//=======================================================================
typedef
  NCollection_IndexedDataMap<BOPTools_Set,
                             TopTools_ListOfShape> BOPAlgo_IndexedDataMapOfSetListOfShape;

static void AddEdgeSet(const TopoDS_Shape& theS,
                       BOPAlgo_IndexedDataMapOfSetListOfShape& theMap,
                       const Handle(NCollection_BaseAllocator)& theAllocator)
{
  // Make set
  BOPTools_Set aSE;
  aSE.Add(theS, TopAbs_EDGE);
  // Add set to the map, keeping connection to the shape
  TopTools_ListOfShape* pLF = theMap.ChangeSeek(aSE);
  if (!pLF)
    pLF = &theMap(theMap.Add(aSE, TopTools_ListOfShape(theAllocator)));
  pLF->Append(theS);
}

//=======================================================================
//function : FillSameDomainFaces
//purpose  : 
//=======================================================================
void BOPAlgo_Builder::FillSameDomainFaces(const Message_ProgressRange& theRange)
{
  // It is necessary to analyze all Face/Face intersections
  // and find all faces with equal sets of edges
  const BOPDS_VectorOfInterfFF& aFFs = myDS->InterfFF();
  Standard_Integer aNbFFs = aFFs.Length();
  if (!aNbFFs)
    return;

  Message_ProgressScope aPSOuter(theRange, NULL, 10);

  Handle(NCollection_BaseAllocator) aAllocator = new NCollection_IncAllocator;

  // Vector to store the indices of faces for future sorting
  // for making the SD face for the group from the face with
  // smallest index in Data structure
  NCollection_Vector<Standard_Integer> aFIVec(256, aAllocator);
  // Fence map to avoid repeated checks of the same face.
  TColStd_MapOfInteger aMFence(1, aAllocator);

  // Fill the vector with indices of faces
  for (Standard_Integer i = 0; i < aNbFFs; ++i)
  {
    if (UserBreak(aPSOuter))
    {
      return;
    }
    const BOPDS_InterfFF& aFF = aFFs(i);
    // get indices
    Standard_Integer nF[2];
    aFF.Indices(nF[0], nF[1]);
    // store indices to the vector
    for (Standard_Integer j = 0; j < 2; ++j)
    {
      if (!myDS->HasFaceInfo(nF[j]))
        continue;

      if (!aMFence.Add(nF[j]))
        continue;

      aFIVec.Appended() = nF[j];
    }
  }

  // Sort the indices
  std::sort(aFIVec.begin(), aFIVec.end());

  // Data map of set of edges with all faces having this set
  NCollection_IndexedDataMap<BOPTools_Set,
                             TopTools_ListOfShape> anESetFaces(1, aAllocator);
  // Map of planar bounded faces. If such faces have the same Edge set
  // they are considered Same domain, without additional check.
  TopTools_MapOfShape aMFPlanar(1, aAllocator);

  Standard_Integer aNbF = aFIVec.Length();
  for (Standard_Integer i = 0; i < aNbF; ++i)
  {
    if (UserBreak(aPSOuter))
    {
      return;
    }
    const Standard_Integer nF = aFIVec(i);
    const BOPDS_ShapeInfo& aSI = myDS->ShapeInfo(nF);
    const TopoDS_Shape& aF = aSI.Shape();

    Standard_Boolean bCheckPlanar = Standard_False;
    {
      // At this stage, context should contain adaptor for all intersected faces,
      // so getting a type of the underlying surface should be done at no cost.
      if (myContext->SurfaceAdaptor(TopoDS::Face(aF)).GetType() == GeomAbs_Plane)
      {
        // Check bounding box of the face - it should not be open in any side
        const Bnd_Box& aBox = aSI.Box();
        bCheckPlanar = !(aBox.IsOpenXmin() || aBox.IsOpenXmax() ||
                         aBox.IsOpenYmin() || aBox.IsOpenYmax() ||
                         aBox.IsOpenZmin() || aBox.IsOpenZmax());
      }
    }

    const TopTools_ListOfShape* pLFSp = myImages.Seek(aF);
    if (pLFSp)
    {
      TopTools_ListIteratorOfListOfShape aItLF(*pLFSp);
      for (; aItLF.More(); aItLF.Next())
      {
        AddEdgeSet(aItLF.Value(), anESetFaces, aAllocator);
        if (bCheckPlanar)
          aMFPlanar.Add(aItLF.Value());
      }
    }
    else
    {
      AddEdgeSet(aF, anESetFaces, aAllocator);
      if (bCheckPlanar)
        aMFPlanar.Add(aF);
    }
  }

  // Store pairs of faces with equal set of edges to check if they are really Same Domain
  BOPAlgo_VectorOfPairOfShapeBoolean aVPSB;

  // Back and forth map of SD faces to make the blocks
  TopTools_IndexedDataMapOfShapeListOfShape aDMSLS(1, aAllocator);

  Standard_Integer aNbSets = anESetFaces.Extent();
  for (Standard_Integer i = 1; i <= aNbSets; ++i)
  {
    if (UserBreak(aPSOuter))
    {
      return;
    }
    const TopTools_ListOfShape& aLF = anESetFaces(i);
    if (aLF.Extent() < 2)
      continue;

    // All possible pairs from <aLF> should be checked
    TopTools_ListIteratorOfListOfShape aIt1(aLF);
    for (; aIt1.More(); aIt1.Next())
    {
      const TopoDS_Shape& aF1 = aIt1.Value();
      Standard_Boolean bCheckPlanar = aMFPlanar.Contains(aF1);

      TopTools_ListIteratorOfListOfShape aIt2 = aIt1;
      for (aIt2.Next(); aIt2.More(); aIt2.Next())
      {
        const TopoDS_Shape& aF2 = aIt2.Value();
        if (bCheckPlanar && aMFPlanar.Contains(aF2))
        {
          // Consider planar bounded faces as Same Domain without additional check
          BOPAlgo_Tools::FillMap(aF1, aF2, aDMSLS, aAllocator);
          continue;
        }
        // Add pair for analysis
        BOPAlgo_PairOfShapeBoolean& aPSB = aVPSB.Appended();
        aPSB.Shape1() = aF1;
        aPSB.Shape2() = aF2;
        aPSB.SetFuzzyValue(myFuzzyValue);
      }
    }
  }

  aPSOuter.Next();

  // Set progress range for each task to be run in parallel
  Message_ProgressScope aPSParallel(aPSOuter.Next(6), "Checking SD faces", aVPSB.Size());
  for (Standard_Integer iPSB = 0; iPSB < aVPSB.Size(); ++iPSB)
  {
    aVPSB.ChangeValue(iPSB).SetProgressRange(aPSParallel.Next());
  }
  //================================================================
  // Perform analysis
  BOPTools_Parallel::Perform (myRunParallel, aVPSB, myContext);
  //================================================================
  if (UserBreak(aPSOuter))
  {
    return;
  }

  NCollection_List<TopTools_ListOfShape> aMBlocks(aAllocator);
  // Fill map with SD faces to make the blocks
  Standard_Integer aNbPairs = aVPSB.Length();
  for (Standard_Integer i = 0; i < aNbPairs; ++i)
  {
    BOPAlgo_PairOfShapeBoolean& aPSB = aVPSB(i);
    if (aPSB.Flag())
      BOPAlgo_Tools::FillMap(aPSB.Shape1(), aPSB.Shape2(), aDMSLS, aAllocator);
  }
  aVPSB.Clear();

  // Make blocks of SD faces using the back and forth map
  BOPAlgo_Tools::MakeBlocks(aDMSLS, aMBlocks, aAllocator);

  Message_ProgressScope aPS(aPSOuter.Next(3), "Filling same domain faces map", aMBlocks.Size());
  // Fill same domain faces map
  NCollection_List<TopTools_ListOfShape>::Iterator aItB(aMBlocks);
  for (; aItB.More(); aItB.Next(), aPS.Next())
  {
    if (UserBreak(aPS))
    {
      return;
    }
    const TopTools_ListOfShape& aLSD = aItB.Value();
    // If the group contains some original faces, the one with minimal
    // index in the DS will be chosen as the SD for the whole group.
    // If there are no original faces in the group, the first face from
    // the group will be used as the SD face.
    // Such SD face will be representative of the whole group in the result.
    TopoDS_Face* pFSD = NULL;
    Standard_Integer nFMin = ::IntegerLast();
    TopTools_ListIteratorOfListOfShape aItLF(aLSD);
    for (; aItLF.More(); aItLF.Next())
    {
      const TopoDS_Shape& aF = aItLF.Value();
      // Check the index of the face in DS
      const Standard_Integer nF = myDS->Index(aF);
      if (nF >= 0)
      {
        // The fact that the face is found in the DS, means that
        // the face has not been change, and thus it is original one.
        //
        // Such face does not have any splits, but have an SD face.
        // Consider it being split.
        myImages.Bound(aF, TopTools_ListOfShape())->Append(aF);

        // For the SD face chose the one with minimal index
        if (nF < nFMin)
        {
          nFMin = nF;
          pFSD = (TopoDS_Face*)&aF;
        }
      }
    }

    if (!pFSD)
    {
      // No original faces in the group, take the first one
      pFSD = (TopoDS_Face*)&aLSD.First();
    }

    // Save all SD connections
    aItLF.Initialize(aLSD);
    for (; aItLF.More(); aItLF.Next())
    {
      const TopoDS_Shape& aF = aItLF.Value();
      myShapesSD.Bind(aF, *pFSD);
    }
  }

  // Update the map of images with SD faces and
  // fill the map of origins.
  Standard_Integer aNbS = myDS->NbSourceShapes();
  for (Standard_Integer i = 0; i < aNbS; ++i)
  {
    const BOPDS_ShapeInfo& aSI = myDS->ShapeInfo(i);
    if (aSI.ShapeType() != TopAbs_FACE)
      continue;

    const TopoDS_Shape& aF = aSI.Shape();
    TopTools_ListOfShape* pLFIm = myImages.ChangeSeek(aF);
    if (!pLFIm)
      continue;

    TopTools_ListIteratorOfListOfShape aItLFIm(*pLFIm);
    for (; aItLFIm.More(); aItLFIm.Next())
    {
      TopoDS_Shape& aFIm = aItLFIm.ChangeValue();
      const TopoDS_Shape* pFSD = myShapesSD.Seek(aFIm);
      if (pFSD)
        // Update image with SD face
        aFIm = *pFSD;

      // Fill the map of origins
      TopTools_ListOfShape* pLFOr = myOrigins.ChangeSeek(aFIm);
      if (!pLFOr)
        pLFOr = myOrigins.Bound(aFIm, TopTools_ListOfShape());
      pLFOr->Append(aF);
    }
  }

  aMBlocks.Clear();
  aDMSLS.Clear();
}
//=======================================================================
// function: FillImagesFaces1
// purpose: 
//=======================================================================
void BOPAlgo_Builder::FillInternalVertices(const Message_ProgressRange& theRange)
{
  Message_ProgressScope aPSOuter(theRange, NULL, 1);

  // Vector of pairs of Vertex/Face for classification of the vertices
  // relatively faces, and adding them as internal into the faces
  BOPAlgo_VectorOfVFI aVVFI;

  Standard_Integer aNbS = myDS->NbSourceShapes();
  for (Standard_Integer i = 0; i < aNbS; ++i)
  {
    const BOPDS_ShapeInfo& aSI = myDS->ShapeInfo(i);
    if (aSI.ShapeType() != TopAbs_FACE)
      continue;

    if (UserBreak(aPSOuter))
    {
      return;
    }

    const TopoDS_Shape& aF = aSI.Shape();
    const TopTools_ListOfShape* pLFIm = myImages.Seek(aF);
    if (!pLFIm)
      continue;

    // Find vertices to add as internal into the splits
    TColStd_ListOfInteger aLIAV;
    myDS->AloneVertices(i, aLIAV);

    // Add vertices and faces for classification
    TColStd_ListIteratorOfListOfInteger aItLV(aLIAV);
    for (; aItLV.More(); aItLV.Next())
    {
      TopoDS_Vertex aV = TopoDS::Vertex(myDS->Shape(aItLV.Value()));
      aV.Orientation(TopAbs_INTERNAL);

      TopTools_ListIteratorOfListOfShape aItLFIm(*pLFIm);
      for (; aItLFIm.More(); aItLFIm.Next())
      {
        const TopoDS_Face& aFIm = TopoDS::Face(aItLFIm.Value());
        // Make the pair
        BOPAlgo_VFI& aVFI = aVVFI.Appended();
        aVFI.SetVertex(aV);
        aVFI.SetFace(aFIm);
        aVFI.SetFuzzyValue(myFuzzyValue);
      }
    }
  }

  // Set progress range for each task to be run in parallel
  Message_ProgressScope aPSParallel(aPSOuter.Next(), "Looking for internal shapes", aVVFI.Size());
  for (Standard_Integer iVFI = 0; iVFI< aVVFI.Size(); ++iVFI)
  {
    aVVFI.ChangeValue(iVFI).SetProgressRange(aPSParallel.Next());
  }
  // Perform classification
  //================================================================
  BOPTools_Parallel::Perform (myRunParallel, aVVFI, myContext);
  //================================================================
  if (UserBreak(aPSOuter))
  {
    return;
  }

  Standard_Integer aNbVFI = aVVFI.Length();
  for (Standard_Integer i = 0; i < aNbVFI; ++i)
  {
    BOPAlgo_VFI& aVFI = aVVFI(i);
    if (aVFI.IsInternal())
    {
      TopoDS_Vertex& aV = aVFI.Vertex();
      TopoDS_Face& aF = aVFI.Face();
      BRep_Builder().Add(aF, aV);
    }
  }
}
//=======================================================================
//function : HasMultiConnected
//purpose  : Checks if the edge has multi-connected vertices.
//=======================================================================
static Standard_Boolean HasMultiConnected(const TopoDS_Edge& theEdge,
                                          TopTools_DataMapOfShapeListOfShape& theMap)
{
  TopoDS_Iterator itV(theEdge);
  for (; itV.More(); itV.Next())
  {
    const TopoDS_Shape& aV = itV.Value();
    TopTools_ListOfShape *pList = theMap.ChangeSeek(aV);
    if (!pList)
    {
      pList = theMap.Bound(aV, TopTools_ListOfShape());
      pList->Append(theEdge);
    }
    else
    {
      // The list is expected to be 1-2 elements long,
      // thus using "Contains" is safe.
      if (!pList->Contains(theEdge))
        pList->Append(theEdge);

      if (pList->Extent() > 2)
        return Standard_True;
    }
  }
  return Standard_False;
}
//=======================================================================
//function : BuildDraftFace
//purpose  : Build draft faces, updating the bounding edges,
//           according to the information stored into the <theImages> map
//=======================================================================
TopoDS_Face BuildDraftFace(const TopoDS_Face& theFace,
                           const TopTools_DataMapOfShapeListOfShape& theImages,
                           Handle(IntTools_Context)& theCtx,
                           const Handle(Message_Report)& theReport)
{
  BRep_Builder aBB;
  // Take the information from the original face
  TopLoc_Location aLoc;
  const Handle(Geom_Surface)& aS = BRep_Tool::Surface(theFace, aLoc);
  const Standard_Real aTol = BRep_Tool::Tolerance(theFace);
  // Make the new face, without any wires
  TopoDS_Face aDraftFace;
  aBB.MakeFace(aDraftFace, aS, aLoc, aTol);

  // Check if the thin face can be split by a vertex - in this case
  // this vertex will be contained in more than two edges. Thus, count
  // the vertices appearance, and if the multi-connexity is met return
  // the null face to use the BuilderFace algorithm for checking the
  // possibility of split.
  TopTools_DataMapOfShapeListOfShape aVerticesCounter;

  // Check that the edges of the initial face have not been unified during intersection.
  // Otherwise, it will be necessary to check validity of the new wires.
  TopTools_MapOfShape aMEdges;

  // Update wires of the original face and add them to draft face
  TopoDS_Iterator aItW(theFace.Oriented(TopAbs_FORWARD));
  for (; aItW.More(); aItW.Next())
  {
    const TopoDS_Shape& aW = aItW.Value();
    if (aW.ShapeType() != TopAbs_WIRE)
      continue;

    // Rebuild wire using images of edges
    TopoDS_Iterator aItE(aW.Oriented(TopAbs_FORWARD));
    if (!aItE.More())
      continue;

    TopoDS_Wire aNewWire;
    aBB.MakeWire(aNewWire);

    for (; aItE.More(); aItE.Next())
    {
      const TopoDS_Edge& aE = TopoDS::Edge(aItE.Value());

      TopAbs_Orientation anOriE = aE.Orientation();
      if (anOriE == TopAbs_INTERNAL)
      {
        // The internal edges could split the original face on halves.
        // Thus, use the BuilderFace algorithm to build the new face.
        return TopoDS_Face();
      }

      // Check if the original edge is degenerated
      Standard_Boolean bIsDegenerated = BRep_Tool::Degenerated(aE);
      // Check if the original edge is closed on the face
      Standard_Boolean bIsClosed = BRep_Tool::IsClosed(aE, theFace);

      // Check for the splits of the edge
      const TopTools_ListOfShape* pLEIm = theImages.Seek(aE);
      if (!pLEIm)
      {
        // Check if the edge has multi-connected vertices
        if (!bIsDegenerated && HasMultiConnected(aE, aVerticesCounter))
          return TopoDS_Face();

        // Check edges unification
        if (!bIsClosed && !aMEdges.Add(aE))
          return TopoDS_Face();

        aBB.Add(aNewWire, aE);
        continue;
      }

      TopTools_ListIteratorOfListOfShape aItLEIm(*pLEIm);
      for (; aItLEIm.More(); aItLEIm.Next())
      {
        TopoDS_Edge& aSp = TopoDS::Edge(aItLEIm.ChangeValue());

        // Check if the split has multi-connected vertices
        if (!bIsDegenerated && HasMultiConnected(aSp, aVerticesCounter))
          return TopoDS_Face();

        // Check edges unification
        if (!bIsClosed && !aMEdges.Add(aSp))
          return TopoDS_Face();

        aSp.Orientation(anOriE);
        if (bIsDegenerated)
        {
          aBB.Add(aNewWire, aSp);
          continue;
        }

        // If the original edge is closed on the face check closeness
        // of the split edge and if it is not closed make the second PCurve
        if (bIsClosed && !BRep_Tool::IsClosed(aSp, theFace))
          BOPTools_AlgoTools3D::DoSplitSEAMOnFace(aSp, theFace);

        // Check if the split should be reversed
        if (BOPTools_AlgoTools::IsSplitToReverseWithWarn(aSp, aE, theCtx, theReport))
          aSp.Reverse();

        aBB.Add(aNewWire, aSp);
      }
    }

    aNewWire.Orientation(aW.Orientation());
    aNewWire.Closed(BRep_Tool::IsClosed(aNewWire));
    aBB.Add(aDraftFace, aNewWire);
  }

  if (theFace.Orientation() == TopAbs_REVERSED)
    aDraftFace.Reverse();

  return aDraftFace;
}
