// Created on: 2004-09-02
// Copyright (c) 2004-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 <BOPAlgo_ArgumentAnalyzer.hxx>
#include <BOPAlgo_BuilderFace.hxx>
#include <BOPAlgo_CheckerSI.hxx>
#include <BOPAlgo_Operation.hxx>
#include <BOPDS_DS.hxx>
#include <BOPDS_MapOfPair.hxx>
#include <BOPTools_AlgoTools.hxx>
#include <BOPTools_AlgoTools3D.hxx>
#include <BRep_Builder.hxx>
#include <BRep_TEdge.hxx>
#include <BRep_Tool.hxx>
#include <BRepExtrema_DistShapeShape.hxx>
#include <Geom_Surface.hxx>
#include <gp_Pnt.hxx>
#include <IntTools_CommonPrt.hxx>
#include <IntTools_Context.hxx>
#include <IntTools_EdgeEdge.hxx>
#include <Standard_ErrorHandler.hxx>
#include <Standard_Failure.hxx>
#include <TColStd_Array2OfBoolean.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopTools_IndexedMapOfShape.hxx>
#include <TopTools_ListOfShape.hxx>
#include <TopTools_MapOfShape.hxx>
#include <TopTools_SequenceOfShape.hxx>

// ================================================================================
// function: Constructor
// purpose:
// ================================================================================
BOPAlgo_ArgumentAnalyzer::BOPAlgo_ArgumentAnalyzer() : 
BOPAlgo_Algo(),
myStopOnFirst(Standard_False),
myOperation(BOPAlgo_UNKNOWN),
myArgumentTypeMode(Standard_False),
mySelfInterMode(Standard_False),
mySmallEdgeMode(Standard_False),
myRebuildFaceMode(Standard_False),
myTangentMode(Standard_False),
myMergeVertexMode(Standard_False),
myMergeEdgeMode(Standard_False),
myContinuityMode(Standard_False),
myCurveOnSurfaceMode(Standard_False),
myEmpty1(Standard_False),
myEmpty2(Standard_False)
{
}
//=======================================================================
// function: ~
// purpose: 
//=======================================================================
BOPAlgo_ArgumentAnalyzer::~BOPAlgo_ArgumentAnalyzer()
{
  myResult.Clear();
}

// ================================================================================
// function: SetShape1
// purpose:
// ================================================================================
void BOPAlgo_ArgumentAnalyzer::SetShape1(const TopoDS_Shape & TheShape)
{
  myShape1 = TheShape;
}

// ================================================================================
// function: SetShape2
// purpose:
// ================================================================================
void BOPAlgo_ArgumentAnalyzer::SetShape2(const TopoDS_Shape & TheShape)
{
  myShape2 = TheShape;
}

// ================================================================================
// function: GetShape1
// purpose:
// ================================================================================
const TopoDS_Shape & BOPAlgo_ArgumentAnalyzer::GetShape1() const
{
  return myShape1;
}

// ================================================================================
// function: GetShape2
// purpose:
// ================================================================================
const TopoDS_Shape & BOPAlgo_ArgumentAnalyzer::GetShape2() const
{
  return myShape2;
}

// ================================================================================
// function: OperationType
// purpose:
// ================================================================================
BOPAlgo_Operation& BOPAlgo_ArgumentAnalyzer::OperationType() 
{
  return myOperation;
}

// ================================================================================
// function: StopOnFirstFaulty
// purpose:
// ================================================================================
Standard_Boolean & BOPAlgo_ArgumentAnalyzer::StopOnFirstFaulty()
{
  return myStopOnFirst;
}

// ================================================================================
// function: Prepare
// purpose:
// ================================================================================
void BOPAlgo_ArgumentAnalyzer::Prepare()
{
  Standard_Boolean isS1 = myShape1.IsNull(), isS2 = myShape2.IsNull();
  if (!isS1) {
    myEmpty1 = BOPTools_AlgoTools3D::IsEmptyShape(myShape1);
  }
  if (!isS2) {
    myEmpty2 = BOPTools_AlgoTools3D::IsEmptyShape(myShape2);
  }
}
// ================================================================================
// function: Perform
// purpose:
// ================================================================================
void BOPAlgo_ArgumentAnalyzer::Perform(const Message_ProgressRange& theRange)
{
  Message_ProgressScope aPS(theRange, "Analyze shapes", 10);
  try {
    OCC_CATCH_SIGNALS
    myResult.Clear();
    if (UserBreak(aPS))
    {
      return;
    }
    // 1. Prepare
    Prepare();
    //
    // 2. Test types
    if(myArgumentTypeMode) {
      TestTypes();
      if (UserBreak(aPS))
      {
        return;
      }
    }
    //
    // 3. Test self-interference
    if(mySelfInterMode) {
      TestSelfInterferences(aPS.Next(8));
      if (UserBreak(aPS))
      {
        return;
      }
    }
    //
    // 4. Test small edges
    if(mySmallEdgeMode) {
      if(!(!myResult.IsEmpty() && myStopOnFirst))
        TestSmallEdge();
      if (UserBreak(aPS))
      {
        return;
      }
    }
    //
    // 5. Test possibility to rebuild faces
    if(myRebuildFaceMode) {
      if(!(!myResult.IsEmpty() && myStopOnFirst))
        TestRebuildFace();
      if (UserBreak(aPS))
      {
        return;
      }
    }
    //
    // 6. Test tangent
    if(myTangentMode) {
      if (!(!myResult.IsEmpty() && myStopOnFirst))
      {
        TestTangent();
        if (UserBreak(aPS))
        {
          return;
        }
      }
    }
    //
    // 7. Test merge vertices
    if(myMergeVertexMode) {
      if(!(!myResult.IsEmpty() && myStopOnFirst))
        TestMergeVertex();
      if (UserBreak(aPS))
      {
        return;
      }
    }
    //
    // 8. Test merge edges
    if(myMergeEdgeMode) {
      if(!(!myResult.IsEmpty() && myStopOnFirst))
        TestMergeEdge();
      if (UserBreak(aPS))
      {
        return;
      }
    }
    //
    // 9. Test shapes continuity
    if(myContinuityMode) {
      if(!(!myResult.IsEmpty() && myStopOnFirst))
        TestContinuity();
      if (UserBreak(aPS))
      {
        return;
      }
    }
    //
    // 10. Test validity of the curves on the surfaces
    if(myCurveOnSurfaceMode) {
      if(!(!myResult.IsEmpty() && myStopOnFirst))
        TestCurveOnSurface();
    }
  }
  catch(Standard_Failure const&) {
    BOPAlgo_CheckResult aResult;
    aResult.SetCheckStatus(BOPAlgo_CheckUnknown);
    myResult.Append(aResult);
  }
}

// ================================================================================
// function: HasFaulty
// purpose:
// ================================================================================
Standard_Boolean BOPAlgo_ArgumentAnalyzer::HasFaulty() const
{
  return ( !myResult.IsEmpty());
}

// ================================================================================
// function: GetCheckResult
// purpose:
// ================================================================================
const BOPAlgo_ListOfCheckResult& BOPAlgo_ArgumentAnalyzer::GetCheckResult() const
{
  return myResult;
}

// ================================================================================
// function: TestTypes
// purpose:
// ================================================================================
void BOPAlgo_ArgumentAnalyzer::TestTypes()
{
  Standard_Boolean isS1 = myShape1.IsNull(), isS2 = myShape2.IsNull();

  if(isS1 && isS2) {
    BOPAlgo_CheckResult aResult;
    aResult.SetCheckStatus(BOPAlgo_BadType);
    myResult.Append(aResult);
    return;
  }

  //single shape check
  if((isS1 && !isS2) || (!isS1 && isS2)) {
    Standard_Boolean bIsEmpty = (isS1) ? myEmpty2 : myEmpty1;

    if(bIsEmpty || myOperation!=BOPAlgo_UNKNOWN) {
      const TopoDS_Shape & aS = (isS1) ? myShape2 : myShape1;
      BOPAlgo_CheckResult aResult;
      aResult.SetShape1(aS);
      aResult.SetCheckStatus(BOPAlgo_BadType);
      myResult.Append(aResult);
      return;
    }
  }
  // two shapes check (begin)
  else {
    if(myEmpty1 || myEmpty2) {
      BOPAlgo_CheckResult aResult;
      if(myEmpty1 && myEmpty2) {
        aResult.SetShape1(myShape1);
        aResult.SetShape2(myShape2);
      }
      else {
        const TopoDS_Shape & aS = myEmpty1 ? myShape1 : myShape2;
        if(myEmpty1)
          aResult.SetShape1(aS);
        else
          aResult.SetShape2(aS);
      }
      aResult.SetCheckStatus(BOPAlgo_BadType);
      myResult.Append(aResult);
      return;
    }
    //
    if (myOperation != BOPAlgo_UNKNOWN &&
        myOperation != BOPAlgo_COMMON)
    {
      Standard_Integer iDimMin[2], iDimMax[2];
      BOPTools_AlgoTools::Dimensions(myShape1, iDimMin[0], iDimMax[0]);
      BOPTools_AlgoTools::Dimensions(myShape2, iDimMin[1], iDimMax[1]);

      Standard_Boolean bBadTypes =
        ((myOperation == BOPAlgo_FUSE) &&
           (iDimMin[0] != iDimMax[0] || iDimMin[1] != iDimMax[1] || iDimMin[0] != iDimMin[1])) ||
        ((myOperation == BOPAlgo_CUT)   && (iDimMax[0] > iDimMin[1])) ||
        ((myOperation == BOPAlgo_CUT21) && (iDimMin[0] < iDimMax[1]));

      if (bBadTypes) {
        BOPAlgo_CheckResult aResult;
        aResult.SetShape1(myShape1);
        aResult.SetShape2(myShape2);
        aResult.SetCheckStatus(BOPAlgo_BadType);
        myResult.Append(aResult);
      }
    }
  }
}
//=======================================================================
//function : TestSelfInterferences
//purpose  : 
//=======================================================================
void BOPAlgo_ArgumentAnalyzer::TestSelfInterferences(const Message_ProgressRange& theRange)
{
  Message_ProgressScope aPS(theRange, NULL, (!myShape1.IsNull() && !myShape2.IsNull() ? 2 : 1));
  Standard_Integer ii;
  //
  for(ii = 0; ii < 2; ii++) {
    const TopoDS_Shape& aS = (ii == 0) ? myShape1 : myShape2;
    if(aS.IsNull()) {
      continue;
    }
    //
    Standard_Boolean bIsEmpty = (ii == 0) ? myEmpty1 : myEmpty2;
    if (bIsEmpty) {
      continue;
    }
    //
    Standard_Integer n1, n2;
    BOPDS_MapIteratorOfMapOfPair aItMPK;
    TopTools_ListOfShape anArgs;
    BOPAlgo_CheckerSI aChecker;
    //
    anArgs.Append(aS);
    aChecker.SetArguments(anArgs);
    aChecker.SetNonDestructive(Standard_True);
    aChecker.SetRunParallel(myRunParallel);
    aChecker.SetFuzzyValue(myFuzzyValue);
    //
    aChecker.Perform(aPS.Next());
    Standard_Boolean hasError = aChecker.HasErrors();
    //
    const BOPDS_DS& aDS=*(aChecker.PDS());
    const BOPDS_MapOfPair& aMPK=aDS.Interferences();
    //
    aItMPK.Initialize(aMPK);
    for (; aItMPK.More(); aItMPK.Next()) {
      const BOPDS_Pair& aPK=aItMPK.Value();
      aPK.Indices(n1, n2);
      if(aDS.IsNewShape(n1) || aDS.IsNewShape(n2)) {
        continue;
      }
      //
      const TopoDS_Shape& aS1=aDS.Shape(n1);
      const TopoDS_Shape& aS2=aDS.Shape(n2);
      //
      BOPAlgo_CheckResult aResult;
      if(ii == 0) {
        aResult.SetShape1(myShape1);
        aResult.AddFaultyShape1(aS1);
        if (!aS1.IsSame(aS2))
          aResult.AddFaultyShape1(aS2);
      }
      else {
        aResult.SetShape2(myShape2);
        aResult.AddFaultyShape2(aS1);
        if (!aS1.IsSame(aS2))
          aResult.AddFaultyShape2(aS2);
      }
      aResult.SetCheckStatus(BOPAlgo_SelfIntersect);
      myResult.Append(aResult);
    }
    //
    if (hasError) {
      BOPAlgo_CheckResult aResult;
      if(ii == 0) {
        aResult.SetShape1(myShape1);
        aResult.AddFaultyShape1(myShape1);
      }
      else {
        aResult.SetShape2(myShape2);
        aResult.AddFaultyShape2(myShape2);
      }
      aResult.SetCheckStatus(BOPAlgo_OperationAborted);
      myResult.Append(aResult);
    }
  }// for(ii = 0; ii < 2; ii++) {
}
// ================================================================================
// function: TestSmallEdge
// purpose:
// ================================================================================
void BOPAlgo_ArgumentAnalyzer::TestSmallEdge() 
{
  Standard_Integer i = 0;
  BRepExtrema_DistShapeShape aDist;
  Handle(IntTools_Context) aCtx;
  //
  aCtx = new IntTools_Context;
  
  for(i = 0; i < 2; i++) {
    const TopoDS_Shape& aS = (i == 0) ? myShape1 : myShape2;

    if(aS.IsNull())
      continue;

    TopExp_Explorer anExp(aS, TopAbs_EDGE);

    for(; anExp.More(); anExp.Next()) {
      const TopoDS_Edge& anEdge = *(TopoDS_Edge*)&anExp.Current();
      if (BRep_Tool::Degenerated(anEdge)) {
        continue;
      }

      if(BOPTools_AlgoTools::IsMicroEdge(anEdge, aCtx)) {
        Standard_Boolean bKeepResult = Standard_True;
        
        if(myOperation == BOPAlgo_SECTION) {
          const TopoDS_Shape& anOtherS = (i == 0) ? myShape2 : myShape1;
          
          if(!anOtherS.IsNull()) {
            aDist.LoadS2(anOtherS);
            
            Standard_Boolean bVertexIsOnShape = Standard_False;
            Standard_Integer ii = 0;
            TopExp_Explorer anExpV(anEdge, TopAbs_VERTEX);
            
            for(; anExpV.More(); anExpV.Next()) {
              const TopoDS_Shape& aV = anExpV.Current();
              
              aDist.LoadS1(aV);
              aDist.Perform();
              
              if(aDist.IsDone()) {
                
                for(ii = 1; ii <= aDist.NbSolution(); ii++) {
                  Standard_Real aTolerance = BRep_Tool::Tolerance(*(TopoDS_Vertex*)&aV);
                  const TopoDS_Shape& aSupportShape = aDist.SupportOnShape2(ii);
                  
                  switch(aSupportShape.ShapeType()) {
                  case TopAbs_VERTEX: {
                    aTolerance += BRep_Tool::Tolerance(*(TopoDS_Vertex*)&(aSupportShape));
                    break;
                  }
                  case TopAbs_EDGE: {
                    aTolerance += BRep_Tool::Tolerance(*(TopoDS_Edge*)&(aSupportShape));
                    break;
                  }
                  case TopAbs_FACE: {
                    aTolerance += BRep_Tool::Tolerance(*(TopoDS_Face*)&(aSupportShape));
                    break;
                  }
                  default:
                    break;
                  }
                  
                  if(aDist.Value() < aTolerance) {
                    bVertexIsOnShape = Standard_True;
                    break;
                  }
                }
              }
            }
            
            if(!bVertexIsOnShape) {
              bKeepResult = Standard_False;
            }
          }
        }
        
        if(bKeepResult) {
          BOPAlgo_CheckResult aResult;
          
          if(i == 0) {
            aResult.SetShape1(myShape1);
            aResult.AddFaultyShape1(anEdge);
          }
          else {
            aResult.SetShape2(myShape2);
            aResult.AddFaultyShape2(anEdge);
          }
          
          aResult.SetCheckStatus(BOPAlgo_TooSmallEdge);
          myResult.Append(aResult);
          
          if(myStopOnFirst) {
            return;
          }
        }
      }
    }
  }
}
// ================================================================================
// function: TestRebuildFace
// purpose:
// ================================================================================
void BOPAlgo_ArgumentAnalyzer::TestRebuildFace() 
{
  if((myOperation == BOPAlgo_SECTION) ||
     (myOperation == BOPAlgo_UNKNOWN))
    return;
  Standard_Integer i = 0;

  for(i = 0; i < 2; i++) {
    const TopoDS_Shape& aS = (i == 0) ? myShape1 : myShape2;

    if(aS.IsNull())
      continue;

    TopExp_Explorer anExp(aS, TopAbs_FACE);
    TopTools_ListOfShape aLS;

    for(; anExp.More(); anExp.Next()) {
      const TopoDS_Face& aFace = *(TopoDS_Face*)&(anExp.Current());

      TopoDS_Face aFF = aFace;
      aFF.Orientation(TopAbs_FORWARD);
      TopExp_Explorer anExpE(aFF, TopAbs_EDGE);
      Standard_Integer nbstartedges = 0;
      aLS.Clear();
      //
      for(; anExpE.More(); anExpE.Next()) {
        const TopoDS_Edge& aE=(*(TopoDS_Edge*)(&anExpE.Current()));
        TopAbs_Orientation anOriE=aE.Orientation();
        //
        if (anOriE==TopAbs_INTERNAL) {
          TopoDS_Edge aEE=aE;
          aEE.Orientation(TopAbs_FORWARD);
          aLS.Append(aEE);
          aEE.Orientation(TopAbs_REVERSED);
          aLS.Append(aEE);
        }
        else {
          aLS.Append(aE);
        }
        nbstartedges++;
      }
      BOPAlgo_BuilderFace aBF;
      aBF.SetFace(aFace);
      aBF.SetShapes(aLS);
      aBF.Perform();
      const TopTools_ListOfShape& aLF = aBF.Areas();
      Standard_Boolean bBadFace = Standard_False;

      if(aLF.Extent() != 1) {
        bBadFace = Standard_True;
      }
      else {
        Standard_Integer nbedgeused = 0;
        anExpE.Init(aLF.First(), TopAbs_EDGE);
        
        for(; anExpE.More(); anExpE.Next(), nbedgeused++);
        
        if(nbstartedges != nbedgeused) {
          bBadFace = Standard_True;
        }
      }

      if(bBadFace) {
        BOPAlgo_CheckResult aResult;
        
        if(i == 0) {
          aResult.SetShape1(myShape1);
          aResult.AddFaultyShape1(aFace);
        }
        else {
          aResult.SetShape2(myShape2);
          aResult.AddFaultyShape2(aFace);
        }
        
        aResult.SetCheckStatus(BOPAlgo_NonRecoverableFace);
        myResult.Append(aResult);
        
        if(myStopOnFirst) {
          return;
        }
      }
    }
  }
}

// ================================================================================
// function: TestTangent
// purpose:
// ================================================================================
void BOPAlgo_ArgumentAnalyzer::TestTangent() 
{
 // not implemented
}

// ================================================================================
// function: TestMergeSubShapes
// purpose:
// ================================================================================
 void BOPAlgo_ArgumentAnalyzer::TestMergeSubShapes(const TopAbs_ShapeEnum theType) 
{
  if(myShape1.IsNull() || myShape2.IsNull())
    return;

  if (myEmpty1 || myEmpty2) 
    return;

  BOPAlgo_CheckStatus aStatus = BOPAlgo_CheckUnknown;

  switch(theType) {
  case TopAbs_VERTEX: {
    aStatus = BOPAlgo_IncompatibilityOfVertex;
    break;
  }
  case TopAbs_EDGE: {
    aStatus = BOPAlgo_IncompatibilityOfEdge;
    break;
  }
  case TopAbs_FACE: {
    aStatus = BOPAlgo_IncompatibilityOfFace;
    break;
  }
  default: 
    return;
  }
  TopExp_Explorer anExp1(myShape1, theType);
  TopExp_Explorer anExp2(myShape2, theType);
  TopTools_SequenceOfShape aSeq1, aSeq2;
  TopTools_MapOfShape aMap1, aMap2;

  for(; anExp1.More(); anExp1.Next()) {
    const TopoDS_Shape& aS1 = anExp1.Current();

    if(aMap1.Contains(aS1))
      continue;
    aSeq1.Append(aS1);
    aMap1.Add(aS1);
  }

  for(; anExp2.More(); anExp2.Next()) {
    const TopoDS_Shape& aS2 = anExp2.Current();
    if(aMap2.Contains(aS2))
      continue;
    aSeq2.Append(aS2);
    aMap2.Add(aS2);
  }

  TColStd_Array2OfBoolean anArrayOfFlag(1, aSeq1.Length(), 1, aSeq2.Length());
  Standard_Integer i = 0, j = 0;
  for(i = 1; i <= aSeq1.Length(); i++)
    for(j = 1; j <= aSeq2.Length(); j++)
      anArrayOfFlag.SetValue(i, j, Standard_False);

  for(i = 1; i <= aSeq1.Length(); i++) {
    const TopoDS_Shape& aS1 = aSeq1.Value(i);
    TopTools_ListOfShape aListOfS2;
    Standard_Integer nbs = 0;

    for(j = 1; j <= aSeq2.Length(); j++) {
      const TopoDS_Shape& aS2 = aSeq2.Value(j);
      Standard_Boolean bIsEqual = Standard_False;

      if(theType == TopAbs_VERTEX) {

        const TopoDS_Vertex& aV1 = *(TopoDS_Vertex*)&(aS1);
        const TopoDS_Vertex& aV2 = *(TopoDS_Vertex*)&(aS2);
        gp_Pnt aP1 = BRep_Tool::Pnt(aV1);
        gp_Pnt aP2 = BRep_Tool::Pnt(aV2);
        Standard_Real aDist = aP1.Distance(aP2);

        if(aDist <= (BRep_Tool::Tolerance(aV1) + BRep_Tool::Tolerance(aV2))) {
          bIsEqual = Standard_True;
        }
      }
      else if(theType == TopAbs_EDGE) {
        const TopoDS_Edge& aE1 = *(TopoDS_Edge*)&(aS1);
        const TopoDS_Edge& aE2 = *(TopoDS_Edge*)&(aS2);
        //
        IntTools_EdgeEdge aEE(aE1, aE2);
        //
        aEE.Perform();
        if (aEE.IsDone()) {
          const IntTools_SequenceOfCommonPrts& aCPrts = aEE.CommonParts();
          Standard_Integer ii = 0;
          
          for (ii = 1; ii <= aCPrts.Length(); ii++) {
            const IntTools_CommonPrt& aCPart = aCPrts(ii);
            
            if (aCPart.Type() == TopAbs_EDGE) {
              bIsEqual = Standard_True;
            }
          }
        }
      }
      else if(theType == TopAbs_FACE) {
        // not yet implemented!
      }

      if(bIsEqual) {
        anArrayOfFlag.SetValue(i, j, Standard_True );
        aListOfS2.Append(aS2);
        nbs++;
      }
    }

    if(nbs > 1) {
      BOPAlgo_CheckResult aResult;

      aResult.SetShape1(myShape1);
      aResult.SetShape2(myShape2);
      aResult.AddFaultyShape1(aS1);
      TopTools_ListIteratorOfListOfShape anIt(aListOfS2);

      for(; anIt.More(); anIt.Next()) {
        aResult.AddFaultyShape2(anIt.Value());
      }

      aResult.SetCheckStatus(aStatus);
      myResult.Append(aResult);

      if(myStopOnFirst) {
        return;
      }
    }
  }

  for(i = 1; i <= aSeq2.Length(); i++) {
    const TopoDS_Shape& aS2 = aSeq2.Value(i);
    TopTools_ListOfShape aListOfS1;
    Standard_Integer nbs = 0;

    for(j = 1; j <= aSeq1.Length(); j++) {
      const TopoDS_Shape& aS1 = aSeq1.Value(j);

      if(anArrayOfFlag.Value(j, i)) {
        aListOfS1.Append(aS1);
        nbs++;
      }
    }
    
    if(nbs > 1) {
      BOPAlgo_CheckResult aResult;

      aResult.SetShape1(myShape1);
      aResult.SetShape2(myShape2);
      TopTools_ListIteratorOfListOfShape anIt(aListOfS1);

      for(; anIt.More(); anIt.Next()) {
        aResult.AddFaultyShape1(anIt.Value());
      }
      aResult.AddFaultyShape2(aS2);

      aResult.SetCheckStatus(aStatus);
      myResult.Append(aResult);

      if(myStopOnFirst) {
        return;
      }
    }
  }
}

// ================================================================================
// function: TestMergeVertex
// purpose:
// ================================================================================
void BOPAlgo_ArgumentAnalyzer::TestMergeVertex() 
{
  TestMergeSubShapes(TopAbs_VERTEX); 
}

// ================================================================================
// function: TestMergeEdge
// purpose:
// ================================================================================
void BOPAlgo_ArgumentAnalyzer::TestMergeEdge() 
{
  TestMergeSubShapes(TopAbs_EDGE); 
}

// ================================================================================
// function: TestContinuity
// purpose:
// ================================================================================
void BOPAlgo_ArgumentAnalyzer::TestContinuity() 
{
  Standard_Integer i, j, aNbS;
  Standard_Real f, l;
  TopExp_Explorer aExp;
  //
  for (i = 0; i < 2; ++i) {
    const TopoDS_Shape& aS = !i ? myShape1 : myShape2;
    if(aS.IsNull()) {
      continue;
    }
    //
    TopTools_IndexedMapOfShape aMS;
    //Edges
    aExp.Init(aS, TopAbs_EDGE);
    for (; aExp.More(); aExp.Next()) {
      const TopoDS_Edge& aE = *(TopoDS_Edge*)&aExp.Current();
      if (BRep_Tool::Degenerated(aE)) {
        continue;
      }
      const Handle(Geom_Curve)& aC = BRep_Tool::Curve(aE, f, l);
      if (aC->Continuity() == GeomAbs_C0) {
        aMS.Add(aE);
      }
    }
    //Faces
    aExp.Init(aS, TopAbs_FACE);
    for (; aExp.More(); aExp.Next()) {
      const TopoDS_Face& aF = *(TopoDS_Face*)&aExp.Current();
      Handle(Geom_Surface) aSurf = BRep_Tool::Surface(aF);
      if (aSurf->Continuity() == GeomAbs_C0) {
        aMS.Add(aF);
      }
    }
    //
    //add shapes with continuity C0 to result
    aNbS = aMS.Extent();
    for (j = 1; j <= aNbS; ++j) {
      const TopoDS_Shape& aFS = aMS(j);
      BOPAlgo_CheckResult aResult;
      if(i == 0) {
        aResult.SetShape1(myShape1);
        aResult.AddFaultyShape1(aFS);
      } else {
        aResult.SetShape2(myShape2);
        aResult.AddFaultyShape2(aFS);
      }
      aResult.SetCheckStatus(BOPAlgo_GeomAbs_C0);
      myResult.Append(aResult);
    }
  }
}

// ================================================================================
// function: TestCurveOnSurface
// purpose:
// ================================================================================
void BOPAlgo_ArgumentAnalyzer::TestCurveOnSurface()
{
  Standard_Integer i;
  Standard_Real aT, aD, aTolE;
  TopExp_Explorer aExpF, aExpE;
  //
  for(i = 0; i < 2; i++) {
    const TopoDS_Shape& aS = (i == 0) ? myShape1 : myShape2;
    if(aS.IsNull()) {
      continue;
    }
    //
    aExpF.Init(aS, TopAbs_FACE);
    for (; aExpF.More(); aExpF.Next()) {
      const TopoDS_Face& aF = *(TopoDS_Face*)&aExpF.Current();
      //
      aExpE.Init(aF, TopAbs_EDGE);
      for (; aExpE.More(); aExpE.Next()) {
        const TopoDS_Edge& aE = *(TopoDS_Edge*)&aExpE.Current();
        //
        if (BOPTools_AlgoTools::ComputeTolerance(aF, aE, aD, aT)) {
          aTolE = BRep_Tool::Tolerance(aE);
          if (aD > aTolE) {
            BOPAlgo_CheckResult aResult;
            aResult.SetCheckStatus(BOPAlgo_InvalidCurveOnSurface);
            if(i == 0) {
              aResult.SetShape1(myShape1);
              aResult.AddFaultyShape1(aE);
              aResult.AddFaultyShape1(aF);
              aResult.SetMaxDistance1(aD);
              aResult.SetMaxParameter1(aT);
            }
            else {
              aResult.SetShape2(myShape2);
              aResult.AddFaultyShape2(aE);
              aResult.AddFaultyShape2(aF);
              aResult.SetMaxDistance2(aD);
              aResult.SetMaxParameter2(aT);
            }
            myResult.Append(aResult);
          }
        }
      }
    }
  }
}
