// File:	BRepTools_Modifier.cxx
// Created:	Thu Aug 25 10:48:00 1994
// Author:	Jacques GOUSSARD
//		<jag@ecolox>

// IFV 04.06.99 - PRO18974 - treatment of INTERNAL shapes.

#include <BRepTools_Modifier.ixx>

#include <TopoDS_Iterator.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Shape.hxx>
#include <TopExp_Explorer.hxx>
#include <TopTools_DataMapIteratorOfDataMapOfShapeShape.hxx>
#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
#include <TopTools_ListOfShape.hxx>
#include <TopTools_ListIteratorOfListOfShape.hxx>
#include <TColStd_ListOfTransient.hxx>
#include <TColStd_ListIteratorOfListOfTransient.hxx>

#if 0
#include <Poly_Triangulation.hxx>
#include <Poly_Polygon3D.hxx>
#include <BRepMesh_IncrementalMesh.hxx>
#endif

#include <Geom2d_Line.hxx>
#include <BRep_Builder.hxx>
#include <BRep_Tool.hxx>
#include <TopoDS.hxx>
#include <BRepTools.hxx>
#include <TopAbs.hxx>
#include <TopExp.hxx>
#include <gp_Pnt.hxx>

#include <gp.hxx>

#include <Standard_NullObject.hxx>
#include <gp_Trsf.hxx>
#include <BRepTools_TrsfModification.hxx>



//=======================================================================
//function : BRepTools_Modifier
//purpose  : 
//=======================================================================

BRepTools_Modifier::BRepTools_Modifier ():myDone(Standard_False)
{}

//=======================================================================
//function : BRepTools_Modifier
//purpose  : 
//=======================================================================

BRepTools_Modifier::BRepTools_Modifier (const TopoDS_Shape& S) :
  myShape(S),myDone(Standard_False)
{
  myMap.Clear();
  Put(S);
}

//=======================================================================
//function : BRepTools_Modifier
//purpose  : 
//=======================================================================

BRepTools_Modifier::BRepTools_Modifier
  (const TopoDS_Shape& S,
   const Handle(BRepTools_Modification)& M) : myShape(S),myDone(Standard_False)
{
  myMap.Clear();
  Put(S);
  Perform(M);
}


//=======================================================================
//function : Init
//purpose  : 
//=======================================================================

void BRepTools_Modifier::Init(const TopoDS_Shape& S)
{
  myShape = S;
  myDone = Standard_False;
  myMap.Clear();
  Put(S);
}


//=======================================================================
//function : Perform
//purpose  : 
//=======================================================================

void BRepTools_Modifier::Perform(const Handle(BRepTools_Modification)& M)
{
  if (myShape.IsNull()) {
    Standard_NullObject::Raise();
  }
  TopTools_DataMapIteratorOfDataMapOfShapeShape theIter(myMap);

  // Remise a Null des shapes value, dans le cas ou on applique une autre
  // modification au shape de depart.

  if (!theIter.Value().IsNull()) {
    while (theIter.More()) {
      myMap(theIter.Value()).Nullify();
      theIter.Next();
    }
    theIter.Reset();
  }

  /*
  while (theIter.More()) {
    Rebuild(theIter.Key(),M);
    theIter.Next();
  }
  */

  Rebuild(myShape,M,0.0);

  if (myShape.ShapeType() == TopAbs_FACE) {
    if (myShape.Orientation() == TopAbs_REVERSED) {
      myMap(myShape).Reverse();
    }
    else{
      myMap(myShape).Orientation(myShape.Orientation());
    } 
  }
  else {
    myMap(myShape).Orientation(myShape.Orientation());
  }

  // Mise a jour des continuites

  TopTools_IndexedDataMapOfShapeListOfShape theEFMap;
  TopExp::MapShapesAndAncestors(myShape,TopAbs_EDGE,TopAbs_FACE,theEFMap);
  BRep_Builder B;

/*
  Standard_Boolean RecomputeTriangles = Standard_False;
  Standard_Real MaxDeflection = RealFirst();
  Handle(Poly_Triangulation) Tr;
  Handle(Poly_Polygon3D) Po;
  TopLoc_Location Loc;
*/

  while (theIter.More()) {
    const TopoDS_Shape& S = theIter.Key();
/*
    if (S.ShapeType() == TopAbs_FACE && !S.IsSame(theIter.Value())) {
      Tr = BRep_Tool::Triangulation(TopoDS::Face(S),Loc);
      if (!Tr.IsNull()) {
	RecomputeTriangles = Standard_True;
	MaxDeflection = Max(MaxDeflection,Tr->Deflection());
      }
    }
    else */ if (S.ShapeType() == TopAbs_EDGE && !S.IsSame(theIter.Value())) {
      const TopoDS_Edge& edg = TopoDS::Edge(S);
/*
      Po = BRep_Tool::Polygon3D(edg,Loc);
      if (!Po.IsNull()) {
	RecomputeTriangles = Standard_True;
	MaxDeflection = Max(MaxDeflection,Po->Deflection());
      }
*/
      TopTools_ListIteratorOfListOfShape it;
      it.Initialize(theEFMap.FindFromKey(edg));
      TopoDS_Face F1,F2;
      while (it.More() && F2.IsNull()) {
	if (F1.IsNull()) {
	  F1 = TopoDS::Face(it.Value());
	}
	else {
	  F2 = TopoDS::Face(it.Value());
	}
	it.Next();
      }
      if (!F2.IsNull()) {
	const TopoDS_Edge& newedg = TopoDS::Edge(myMap(edg));
	const TopoDS_Face& newf1  = TopoDS::Face(myMap(F1));
	const TopoDS_Face& newf2  = TopoDS::Face(myMap(F2));
	GeomAbs_Shape Newcont = M->Continuity(edg,F1,F2,newedg,newf1,newf2);
	if (Newcont > GeomAbs_C0) {
	  B.Continuity(newedg,newf1,newf2,Newcont);
	}
      }
    }
    theIter.Next();
  }
/*
  if (RecomputeTriangles) {
    BRepMesh_IncrementalMesh(myMap(myShape),MaxDeflection);
  }
*/

  myDone = Standard_True;

}

//=======================================================================
//function : Put
//purpose  : 
//=======================================================================

void BRepTools_Modifier::Put(const TopoDS_Shape& S)
{
  if (!myMap.IsBound(S)) {
    myMap.Bind(S,TopoDS_Shape());
    for(TopoDS_Iterator theIterator(S,Standard_False);theIterator.More();theIterator.Next()) {

      Put(theIterator.Value());
    }
  }
}

//=======================================================================
//function : Rebuild
//purpose  : 
//=======================================================================

Standard_Boolean BRepTools_Modifier::Rebuild
  (const TopoDS_Shape& S,
   //OCC217
   const Handle(BRepTools_Modification)& M,
   const Standard_Real Tol) 
   //const Handle(BRepTools_Modification)& M)
{
  TopoDS_Shape& result = myMap(S);
//  if (!result.IsNull()) return ! S.IsEqual(result);
  if (!result.IsNull()) return ! S.IsSame(result);
  Standard_Boolean rebuild = Standard_False, RevWires = Standard_False;
  TopAbs_Orientation ResOr = TopAbs_FORWARD;
  BRep_Builder B;
  Standard_Real tol = Tol;
  //Standard_Real tol;
  Standard_Boolean No3DCurve = Standard_False; // en fait, si on n`a pas de 
  //modif de geometrie 3d , il faudrait tester l`existence d`une courbe 3d.

  // new geometry ?

  TopAbs_ShapeEnum ts = S.ShapeType();
  switch (ts) {
  case TopAbs_FACE:
    {
      Standard_Boolean RevFace;
      Handle(Geom_Surface) surface;
      TopLoc_Location location;
      rebuild = M->NewSurface(TopoDS::Face(S),surface,location,tol,
			      RevWires,RevFace);
      tol = Max(Tol,tol);  //OCC217
      if (rebuild) {
	B.MakeFace(TopoDS::Face(result),surface,
		   location.Predivided(S.Location()),tol);
	result.Location(S.Location());
//	result.Orientation(S.Orientation());
	if (RevFace) {
	  ResOr = TopAbs_REVERSED;
	}
	// set specifics flags of a Face
	B.NaturalRestriction(TopoDS::Face(result),
			     BRep_Tool::NaturalRestriction(TopoDS::Face(S)));
      }
    }
    break;

  case TopAbs_EDGE:
    {
      Handle(Geom_Curve) curve;
      TopLoc_Location location;
      rebuild = M->NewCurve(TopoDS::Edge(S),curve,location,tol);
      tol = Max(Tol,tol);  //OCC217
      if (rebuild) {
	if (curve.IsNull()) {
	  B.MakeEdge(TopoDS::Edge(result));
	  B.Degenerated(TopoDS::Edge(result),
			BRep_Tool::Degenerated(TopoDS::Edge(S)));
	  B.UpdateEdge(TopoDS::Edge(result),tol);  //OCC217
	  No3DCurve = Standard_True;
	}
	else {
	  B.MakeEdge(TopoDS::Edge(result),curve,
		     location.Predivided(S.Location()),tol);
	  No3DCurve = Standard_False;
	}
	result.Location(S.Location());
//	result.Orientation(S.Orientation());

	// set specifics flags of an Edge
	B.SameParameter(TopoDS::Edge(result),
			BRep_Tool::SameParameter(TopoDS::Edge(S)));
	B.SameRange(TopoDS::Edge(result),
		    BRep_Tool::SameRange(TopoDS::Edge(S)));
      }
    }
    break;

  case TopAbs_VERTEX:
    {
      gp_Pnt vtx;
      rebuild = M->NewPoint(TopoDS::Vertex(S),vtx,tol);
      // PTV ACIS907 file el8.sat. 
      // initial shape has tol 5.4 (the sameparameter problem)  and in scale case
      // tolerance on vertex must be modified.
      Standard_Real aTolOfVrtx = BRep_Tool::Tolerance(TopoDS::Vertex(S));
      if ( M->IsKind( STANDARD_TYPE( BRepTools_TrsfModification ) ) ) {
        gp_Trsf aTrsf = Handle(BRepTools_TrsfModification)::DownCast(M) ->Trsf();
        aTolOfVrtx *= aTrsf.ScaleFactor();
      }
      tol = Max(Tol, aTolOfVrtx);  //OCC217
      if (rebuild) {
	B.MakeVertex(TopoDS::Vertex(result),vtx,tol);
      }
    }
    break;

  default:
    {
    }
  }

  // rebuild sub-shapes and test new sub-shape ?

  Standard_Boolean newgeom = rebuild;

  TopoDS_Iterator it;

  for (it.Initialize(S, Standard_False); it.More(); it.Next()) {
    // always call Rebuild
    Standard_Boolean subrebuilt = Rebuild(it.Value(),M,tol);  //OCC217
    //Standard_Boolean subrebuilt = Rebuild(it.Value(),M);
    rebuild =  subrebuilt || rebuild ;
  }

  // make an empty copy
  if (rebuild && !newgeom) {
    result = S.EmptyCopied();
    result.Orientation(TopAbs_FORWARD);
  }

  // copy the sub-elements 
  
  if (rebuild) {
    TopAbs_Orientation orient;
    for (it.Initialize(S,Standard_False); it.More(); it.Next()) {
      orient = it.Value().Orientation();
      if (RevWires || myMap(it.Value()).Orientation() == TopAbs_REVERSED) {
	orient = TopAbs::Reverse(orient);
      }
      B.Add(result,myMap(it.Value()).Oriented(orient));
    }


    if (ts == TopAbs_FACE) {
      // pcurves
      Handle(Geom2d_Curve) curve2d; //,curve2d1;
      const TopoDS_Face& face = TopoDS::Face(S);
      TopAbs_Orientation fcor = face.Orientation();
      if(fcor != TopAbs_REVERSED) fcor = TopAbs_FORWARD;

      TopExp_Explorer ex(face.Oriented(fcor),TopAbs_EDGE);

      
      for (;ex.More(); ex.Next()) {
	const TopoDS_Edge& edge = TopoDS::Edge(ex.Current());

	if (M->NewCurve2d(edge, face,TopoDS::Edge(myMap(ex.Current())),
			  TopoDS::Face(result),curve2d, tol)) {
	  //Roman Lygin Dec 12, 2009
	  //save original range on pcurve to restore it later
	  Standard_Real aFOrig, aLOrig;
	  BRep_Tool::Range (edge, face, aFOrig, aLOrig);

	  TopoDS_Edge CurE = TopoDS::Edge(myMap(ex.Current()));
	  TopoDS_Face CurF = TopoDS::Face(result);

	  tol = Max(Tol,tol);  //OCC217
	  // rem dub 16/09/97 : On fait de la topologie constante ou on
	  // n'en fait pas.
	  // On n'en fait pas si CopySurface = 1
	  // Atention, les VRAIES aretes de couture (RealyClosed) le 
	  // restent meme si CopySurface est vrai.
	  if ((BRepTools::IsReallyClosed(edge,face)) ||
	      (BRep_Tool::IsClosed(edge,face) && !newgeom)) {
	    Handle(Geom2d_Curve) BidCurv = new Geom2d_Line(gp::OX2d());
	    CurF.Orientation(TopAbs_FORWARD);
//	    TopoDS_Face CurF = TopoDS::Face(result.Oriented(TopAbs_FORWARD));
	    Handle(Geom2d_Curve) curve2d1, currcurv;
	    Standard_Real f,l;
	    if ((!RevWires && fcor != edge.Orientation()) ||
		( RevWires && fcor == edge.Orientation())) {
	      CurE.Orientation(TopAbs_FORWARD);
	      curve2d1 = BRep_Tool::CurveOnSurface(CurE,CurF,f,l);
	      if (curve2d1.IsNull()) curve2d1 = BidCurv;
	      B.UpdateEdge(CurE,
			   curve2d1,
			   curve2d,
			   CurF,
               tol);
	    }
	    else {
	      CurE.Orientation(TopAbs_REVERSED);
	      curve2d1 = BRep_Tool::CurveOnSurface(CurE,CurF,f,l);
	      if (curve2d1.IsNull()) curve2d1 = BidCurv;
	      B.UpdateEdge(CurE,
			   curve2d,
			   curve2d1,
			   CurF,
			   tol);
	    }
	  }
	  else {
	    B.UpdateEdge(CurE,
			 curve2d,
			 CurF,
			 tol);
	  }
	  //restore original range which was reset by BRep_Builder::UpdateEdge()
	  B.Range (CurE, CurF, aFOrig, aLOrig);

	  TopLoc_Location theLoc;
	  Standard_Real theF,theL;
	  Handle(Geom_Curve) C3D =
	    BRep_Tool::Curve(TopoDS::Edge(myMap(ex.Current())),
			     theLoc,theF,theL);
	  if (C3D.IsNull()) { // Update vertices
	    Standard_Real param;
	    TopExp_Explorer ex2(edge,TopAbs_VERTEX);
	    while (ex2.More()) {
	      const TopoDS_Vertex& vertex = TopoDS::Vertex(ex2.Current());
	      if (!M->NewParameter(vertex, edge, param, tol)) {
		tol = Max(Tol, BRep_Tool::Tolerance(vertex));  //OCC217
		//tol = BRep_Tool::Tolerance(vertex);
		param = BRep_Tool::Parameter(vertex,edge);
	      }

	      TopAbs_Orientation vtxrelat = vertex.Orientation();
	      if (edge.Orientation() == TopAbs_REVERSED) {
		// Update considere l'edge FORWARD, et le vertex en relatif
		vtxrelat= TopAbs::Reverse(vtxrelat);
	      }
//	      if (myMap(edge).Orientation() == TopAbs_REVERSED) {
//		vtxrelat= TopAbs::Reverse(vtxrelat);
//	      }
	      TopoDS_Vertex aLocalVertex = TopoDS::Vertex(myMap(vertex));
	      aLocalVertex.Orientation(vtxrelat);
//	      B.UpdateVertex(TopoDS::Vertex
//			     (myMap(vertex).Oriented(vtxrelat)),
	      B.UpdateVertex(aLocalVertex,
			     param,
			     TopoDS::Edge(myMap(edge)),
			     tol);
	      ex2.Next();
	    }
	  }

	}
      }

    }

//    else if (ts == TopAbs_EDGE) {
    else if (ts == TopAbs_EDGE && !No3DCurve) {
      // Vertices
      Standard_Real param;
      const TopoDS_Edge& edge = TopoDS::Edge(S);
      TopAbs_Orientation edor = edge.Orientation();
      if(edor != TopAbs_REVERSED) edor = TopAbs_FORWARD;
      TopExp_Explorer ex(edge.Oriented(edor), TopAbs_VERTEX);
      while (ex.More()) {
	const TopoDS_Vertex& vertex = TopoDS::Vertex(ex.Current());

	if (!M->NewParameter(vertex, edge, param, tol)) {
          tol = Max(Tol, BRep_Tool::Tolerance(vertex));  //OCC217
	  //tol = BRep_Tool::Tolerance(vertex);
	  param = BRep_Tool::Parameter(vertex,edge);
	}


	TopAbs_Orientation vtxrelat = vertex.Orientation();
	if (edor == TopAbs_REVERSED) {
	  // Update considere l'edge FORWARD, et le vertex en relatif
	  vtxrelat= TopAbs::Reverse(vtxrelat);
	}
//	if (result.Orientation() == TopAbs_REVERSED) {
//	  vtxrelat= TopAbs::Reverse(vtxrelat);
//	}
	TopoDS_Vertex aLocalVertex = TopoDS::Vertex(myMap(vertex));
	aLocalVertex.Orientation(vtxrelat);
//	B.UpdateVertex(TopoDS::Vertex
//		       (myMap(vertex).Oriented(vtxrelat)),
	B.UpdateVertex(aLocalVertex,
		       param,
		       TopoDS::Edge(result),
		       tol);

	ex.Next();
      }

    }

    // update flags

    result.Orientable(S.Orientable());
    result.Closed(S.Closed());
    result.Infinite(S.Infinite());
  }
  else
    result = S;

  // Set flag of the shape.
  result.Orientation(ResOr);

  result.Free      (S.Free());
  result.Modified  (S.Modified());
  result.Checked   (S.Checked());
  result.Orientable(S.Orientable());
  result.Closed    (S.Closed());
  result.Infinite  (S.Infinite());
  result.Convex    (S.Convex());

  return rebuild;
}


