// Created on: 1996-09-03
// Created by: Olga KOULECHOVA
// Copyright (c) 1996-1999 Matra Datavision
// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#include <Bnd_Box.hxx>
#include <BRepAlgo.hxx>
#include <BRepAlgoAPI_Cut.hxx>
#include <BRepAlgoAPI_Fuse.hxx>
#include <BRepBndLib.hxx>
#include <BRepFeat.hxx>
#include <BRepFeat_MakeDPrism.hxx>
#include <BRepLib.hxx>
#include <BRepLib_MakeFace.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <BRepTools.hxx>
#include <Geom_Curve.hxx>
#include <Geom_Line.hxx>
#include <Geom_Plane.hxx>
#include <Geom_RectangularTrimmedSurface.hxx>
#include <Geom_Surface.hxx>
#include <gp_Pln.hxx>
#include <gp_Pnt.hxx>
#include <LocOpe.hxx>
#include <LocOpe_CSIntersector.hxx>
#include <LocOpe_DPrism.hxx>
#include <LocOpe_PntFace.hxx>
#include <Precision.hxx>
#include <Standard_ConstructionError.hxx>
#include <TColGeom_SequenceOfCurve.hxx>
#include <TColgp_SequenceOfPnt.hxx>
#include <TopAbs.hxx>
#include <TopExp.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Solid.hxx>
#include <TopTools_MapOfShape.hxx>

#ifdef OCCT_DEBUG
extern Standard_Boolean BRepFeat_GettraceFEAT();
#endif

static void MajMap(const TopoDS_Shape&,
                   const LocOpe_DPrism&,
                   TopTools_DataMapOfShapeListOfShape&, // myMap
                   TopoDS_Shape&,                       // myFShape
                   TopoDS_Shape&);                      // myLShape

static Standard_Real HeightMax(const TopoDS_Shape& theSbase,
                               const TopoDS_Face&  theSkface,
                               const TopoDS_Shape& theSFrom,
                               const TopoDS_Shape& theSUntil);

static Standard_Integer SensOfPrism(const Handle(Geom_Curve)& C, const TopoDS_Shape& Until);

static Handle(Geom_Curve) TestCurve(const TopoDS_Face&);

//=================================================================================================

void BRepFeat_MakeDPrism::Init(const TopoDS_Shape&    Sbase,
                               const TopoDS_Face&     Pbase,
                               const TopoDS_Face&     Skface,
                               const Standard_Real    Angle,
                               const Standard_Integer Mode,
                               const Standard_Boolean Modify)

{
#ifdef OCCT_DEBUG
  Standard_Boolean trc = BRepFeat_GettraceFEAT();
  if (trc)
    std::cout << "BRepFeat_MakeDPrism::Init" << std::endl;
#endif
  mySkface = Skface;
  SketchFaceValid();
  mySbase = Sbase;
  BasisShapeValid();
  myPbase = Pbase;
  mySlface.Clear();
  if (Mode == 0)
  {
    myFuse     = Standard_False;
    myJustFeat = Standard_False;
  }
  else if (Mode == 1)
  {
    myFuse     = Standard_True;
    myJustFeat = Standard_False;
  }
  else if (Mode == 2)
  {
    myFuse     = Standard_True;
    myJustFeat = Standard_True;
  }
  else
  {
  }
  myModify    = Modify;
  myJustGluer = Standard_False;

  //-------------- ifv
  // mySkface.Nullify();
  //-------------- ifv

  myShape.Nullify();
  myMap.Clear();
  myFShape.Nullify();
  myLShape.Nullify();
  myTopEdges.Clear();
  myLatEdges.Clear();
  TopExp_Explorer exp;
  for (exp.Init(mySbase, TopAbs_FACE); exp.More(); exp.Next())
  {
    TopTools_ListOfShape thelist;
    myMap.Bind(exp.Current(), thelist);
    myMap(exp.Current()).Append(exp.Current());
  }
  myAngle = Angle;
#ifdef OCCT_DEBUG
  if (trc)
  {
    if (myJustFeat)
      std::cout << " Just Feature" << std::endl;
    if (myFuse)
      std::cout << " Fuse" << std::endl;
    if (!myFuse)
      std::cout << " Cut" << std::endl;
    if (!myModify)
      std::cout << " Modify = 0" << std::endl;
    //    std::cout <<" Angle = " << myAngle << std::endl;
  }
#endif
}

//=======================================================================
// function : Add
// purpose  : add sliding faces and edges
//=======================================================================

void BRepFeat_MakeDPrism::Add(const TopoDS_Edge& E, const TopoDS_Face& F)
{
#ifdef OCCT_DEBUG
  Standard_Boolean trc = BRepFeat_GettraceFEAT();
  if (trc)
    std::cout << "BRepFeat_MakeDPrism::Add(Edge,face)" << std::endl;
#endif
  TopExp_Explorer exp;
  for (exp.Init(mySbase, TopAbs_FACE); exp.More(); exp.Next())
  {
    if (exp.Current().IsSame(F))
    {
      break;
    }
  }
  if (!exp.More())
  {
    throw Standard_ConstructionError();
  }

  for (exp.Init(myPbase, TopAbs_EDGE); exp.More(); exp.Next())
  {
    if (exp.Current().IsSame(E))
    {
      break;
    }
  }
  if (!exp.More())
  {
    throw Standard_ConstructionError();
  }

  if (!mySlface.IsBound(F))
  {
    TopTools_ListOfShape thelist;
    mySlface.Bind(F, thelist);
  }
  TopTools_ListIteratorOfListOfShape itl(mySlface(F));
  for (; itl.More(); itl.Next())
  {
    if (itl.Value().IsSame(E))
    {
      break;
    }
  }
  if (!itl.More())
  {
    mySlface(F).Append(E);
  }
}

//=======================================================================
// function : Perform
// purpose  : feature of Height
//=======================================================================

void BRepFeat_MakeDPrism::Perform(const Standard_Real Height)
{
#ifdef OCCT_DEBUG
  Standard_Boolean trc = BRepFeat_GettraceFEAT();
  if (trc)
    std::cout << "BRepFeat_MakeDPrism::Perform(Height)" << std::endl;
#endif
  mySFrom.Nullify();
  ShapeFromValid();
  mySUntil.Nullify();
  ShapeUntilValid();
  myGluedF.Clear();
  myPerfSelection = BRepFeat_NoSelection;
  PerfSelectionValid();

  Standard_Real theheight = Height / cos(myAngle);
  //  myPbase.Orientation(TopAbs_FORWARD);

  LocOpe_DPrism       theDPrism(myPbase, theheight, myAngle);
  const TopoDS_Shape& VraiDPrism = theDPrism.Shape();

  MajMap(myPbase, theDPrism, myMap, myFShape, myLShape);

  myGShape = VraiDPrism;
  GeneratedShapeValid();
  const TopoDS_Shape& Base = theDPrism.FirstShape();
  TopExp_Explorer     exp(Base, TopAbs_FACE);
  TopoDS_Face         theBase = TopoDS::Face(exp.Current());
  exp.Next();
  if (exp.More())
  {
    NotDone();
    myStatusError = BRepFeat_InvFirstShape;
    return;
  }

  // management of gluing faces

  GluedFacesValid();

  if (!myGluedF.IsEmpty())
  { // case gluing
    myJustGluer = Standard_True;
    theDPrism.Curves(myCurves);
    myBCurve = theDPrism.BarycCurve();
    GlobalPerform();
  }

  // if there is no gluing -> call topological operations
  if (!myJustGluer)
  {
    if (myFuse == 1)
    {
      BRepAlgoAPI_Fuse f(mySbase, myGShape);
      myShape = f.Shape();
      UpdateDescendants(f, myShape, Standard_False);
      Done();
    }
    else if (myFuse == 0)
    {
      BRepAlgoAPI_Cut c(mySbase, myGShape);
      myShape = c.Shape();
      UpdateDescendants(c, myShape, Standard_False);
      Done();
    }
    else
    {
      myShape = myGShape;
      Done();
    }
  }
}

//=======================================================================
// function : Perform
// purpose  : feature limited by the shape Until
//=======================================================================

void BRepFeat_MakeDPrism::Perform(const TopoDS_Shape& Until)
{
#ifdef OCCT_DEBUG
  Standard_Boolean trc = BRepFeat_GettraceFEAT();
  if (trc)
    std::cout << "BRepFeat_MakeDPrism::Perform(Until)" << std::endl;
#endif
  if (Until.IsNull())
  {
    throw Standard_ConstructionError();
  }
  TopExp_Explorer exp(Until, TopAbs_FACE);
  if (!exp.More())
  {
    throw Standard_ConstructionError();
  }
  //  myPbase.Orientation(TopAbs_FORWARD);

  myGluedF.Clear();
  myPerfSelection = BRepFeat_SelectionU;
  PerfSelectionValid();
  mySFrom.Nullify();
  ShapeFromValid();
  mySUntil             = Until;
  Standard_Boolean Trf = TransformShapeFU(1);
  ShapeUntilValid();
  Handle(Geom_Curve) C    = TestCurve(myPbase);
  Standard_Integer   sens = SensOfPrism(C, mySUntil);

  BRep_Builder        bB;
  Standard_Real       Height = sens * HeightMax(mySbase, mySkface, mySFrom, mySUntil);
  LocOpe_DPrism       theDPrism(myPbase, Height, myAngle);
  const TopoDS_Shape& VraiDPrism = theDPrism.Shape();
  if (!Trf)
  {
    MajMap(myPbase, theDPrism, myMap, myFShape, myLShape);
    myGShape = VraiDPrism;
    GeneratedShapeValid();
    const TopoDS_Shape& Base = theDPrism.FirstShape();
    exp.Init(Base, TopAbs_FACE);
    TopoDS_Face theBase = TopoDS::Face(exp.Current());
    exp.Next();
    if (exp.More())
    {
      NotDone();
      myStatusError = BRepFeat_InvFirstShape;
      return;
    }

    GluedFacesValid();
    theDPrism.Curves(myCurves);
    myBCurve = theDPrism.BarycCurve();
    GlobalPerform();
  }
  else
  {
    MajMap(myPbase, theDPrism, myMap, myFShape, myLShape);
    Handle(Geom_Curve) C1;
    if (sens == -1)
    {
      C1 = C->Reversed();
    }
    else
    {
      C1 = C;
    }

    TColGeom_SequenceOfCurve scur;
    scur.Clear();
    scur.Append(C1);
    LocOpe_CSIntersector ASI(mySUntil);
    ASI.Perform(scur);
    TopAbs_Orientation Or;
    if (ASI.IsDone() && ASI.NbPoints(1) >= 1)
    {
      if (myFuse == 1)
      {
        Or = ASI.Point(1, 1).Orientation();
      }
      else
      {
        Or = ASI.Point(1, ASI.NbPoints(1)).Orientation();
      }
      //      Standard_Real prm = ASI.Point(1,1).Parameter();
      //      if(prm < 0) Or = TopAbs::Reverse(Or);
      TopoDS_Face  FUntil = ASI.Point(1, 1).Face();
      TopoDS_Shape Comp;
      bB.MakeCompound(TopoDS::Compound(Comp));
      TopoDS_Solid S = BRepFeat::Tool(mySUntil, FUntil, Or);
      if (!S.IsNull())
        bB.Add(Comp, S);

      BRepAlgoAPI_Cut trP(VraiDPrism, Comp);
      UpdateDescendants(trP, trP.Shape(), Standard_False);

      TopExp_Explorer     ex(trP.Shape(), TopAbs_SOLID);
      const TopoDS_Shape& Cutsh = ex.Current();
      if (myFuse == 1)
      {
        BRepAlgoAPI_Fuse f(mySbase, Cutsh);
        myShape = f.Shape();
        UpdateDescendants(f, myShape, Standard_False);
        Done();
      }
      else if (myFuse == 0)
      {
        BRepAlgoAPI_Cut c(mySbase, Cutsh);
        myShape = c.Shape();
        UpdateDescendants(c, myShape, Standard_False);
        Done();
      }
      else
      {
        myShape = Cutsh;
        Done();
      }
    }
  }
  TopTools_ListIteratorOfListOfShape ited(myNewEdges);
  for (; ited.More(); ited.Next())
  {
    const TopoDS_Edge& ledg = TopoDS::Edge(ited.Value());
    if (!BRepAlgo::IsValid(ledg))
    {
      bB.SameRange(ledg, Standard_False);
      bB.SameParameter(ledg, Standard_False);
      BRepLib::SameParameter(ledg, BRep_Tool::Tolerance(ledg));
    }
  }
}

//=======================================================================
// function : Perform
// purpose  : feature limited by two shapes
//=======================================================================

void BRepFeat_MakeDPrism::Perform(const TopoDS_Shape& From, const TopoDS_Shape& Until)
{
#ifdef OCCT_DEBUG
  Standard_Boolean trc = BRepFeat_GettraceFEAT();
  if (trc)
    std::cout << "BRepFeat_MakeDPrism::Perform(From,Until)" << std::endl;
#endif
  if (From.IsNull() || Until.IsNull())
  {
    throw Standard_ConstructionError();
  }

  if (!mySkface.IsNull())
  {
    if (From.IsSame(mySkface))
    {
      myJustGluer = Standard_True;
      Perform(Until);
      if (myJustGluer)
        return;
    }
    else if (Until.IsSame(mySkface))
    {
      myJustGluer = Standard_True;
      Perform(From);
      if (myJustGluer)
        return;
    }
  }
  //  myPbase.Orientation(TopAbs_FORWARD);

  myGluedF.Clear();
  myPerfSelection = BRepFeat_SelectionFU;
  PerfSelectionValid();

  TopExp_Explorer exp(From, TopAbs_FACE);
  if (!exp.More())
  {
    throw Standard_ConstructionError();
  }
  exp.Init(Until, TopAbs_FACE);
  if (!exp.More())
  {
    throw Standard_ConstructionError();
  }
  mySFrom               = From;
  Standard_Boolean Trff = TransformShapeFU(0);
  ShapeFromValid();
  mySUntil              = Until;
  Standard_Boolean Trfu = TransformShapeFU(1);
  ShapeUntilValid();
  if (Trfu != Trff)
  {
    NotDone();
    myStatusError = BRepFeat_IncTypes;
    return;
  }
  Handle(Geom_Curve) C = TestCurve(myPbase);
  Standard_Integer   sens;
  if (From.IsSame(Until))
  {
    sens = 1;
  }
  else
  {
    sens = SensOfPrism(C, mySUntil);
  }

  Standard_Real       Height = sens * HeightMax(mySbase, myPbase, mySFrom, mySUntil);
  LocOpe_DPrism       theDPrism(myPbase, Height, Height, myAngle);
  const TopoDS_Shape& VraiDPrism = theDPrism.Shape();

  if (!Trff)
  {
    MajMap(myPbase, theDPrism, myMap, myFShape, myLShape);

    // Make systematically dprism
    myGShape = VraiDPrism;
    GeneratedShapeValid();

    // management of gluing faces
    //	  mySbase, myPbase, mySlface, theDPrism, myGluedF);
    GluedFacesValid();
    theDPrism.Curves(myCurves);
    myBCurve = theDPrism.BarycCurve();

    // topologic reconstruction
    GlobalPerform();
  }
  else
  {
    // management of descendants
    MajMap(myPbase, theDPrism, myMap, myFShape, myLShape);
    Handle(Geom_Curve) C1;
    if (sens == -1)
    {
      C1 = C->Reversed();
    }
    else
    {
      C1 = C;
    }
    TColGeom_SequenceOfCurve scur;
    scur.Clear();
    scur.Append(C1);
    LocOpe_CSIntersector ASI1(mySUntil);
    LocOpe_CSIntersector ASI2(mySFrom);
    ASI1.Perform(scur);
    ASI2.Perform(scur);
    TopAbs_Orientation OrU, OrF;
    TopoDS_Face        FFrom, FUntil;
    // direction of dprism
    if (ASI1.IsDone() && ASI1.NbPoints(1) >= 1)
    {
      if (myFuse == 1)
      {
        OrU = ASI1.Point(1, 1).Orientation();
      }
      else
      {
        OrU = ASI1.Point(1, ASI1.NbPoints(1)).Orientation();
      }
      //      Standard_Real prm = ASI1.Point(1,1).Parameter();
      //      if(prm < 0) OrU = TopAbs::Reverse(OrU);
      FUntil = ASI1.Point(1, 1).Face();
    }
    else
    {
      NotDone();
      myStatusError = BRepFeat_NoIntersectU;
      return;
    }
    if (ASI2.IsDone() && ASI2.NbPoints(1) >= 1)
    {
      OrF = ASI2.Point(1, 1).Orientation();
      //      Standard_Real prm = ASI2.Point(1,1).Parameter();
      OrF   = TopAbs::Reverse(OrF);
      FFrom = ASI2.Point(1, 1).Face();
    }
    else
    {
      NotDone();
      myStatusError = BRepFeat_NoIntersectF;
      return;
    }
    TopoDS_Shape Comp;
    BRep_Builder B;
    B.MakeCompound(TopoDS::Compound(Comp));
    TopoDS_Solid S = BRepFeat::Tool(mySUntil, FUntil, OrU);
    if (!S.IsNull())
      B.Add(Comp, S);
    else
    {
      NotDone();
      myStatusError = BRepFeat_NullToolU;
      return;
    }
    TopoDS_Solid SS = BRepFeat::Tool(mySFrom, FFrom, OrF);
    if (!SS.IsNull())
      B.Add(Comp, SS);
    else
    {
      NotDone();
      myStatusError = BRepFeat_NullToolF;
      return;
    }

    BRepAlgoAPI_Cut trP(VraiDPrism, Comp);

    if (myFuse == 1)
    {
      BRepAlgoAPI_Fuse f(mySbase, trP.Shape());
      myShape = f.Shape();
      UpdateDescendants(f, myShape, Standard_False);
      Done();
    }
    //
    else if (myFuse == 0)
    {
      BRepAlgoAPI_Cut c(mySbase, trP.Shape());
      myShape = c.Shape();
      UpdateDescendants(c, myShape, Standard_False);
      Done();
    }
    else
    {
      myShape = trP.Shape();
      Done();
    }
  }
}

//=======================================================================
// function : Perform
// purpose  : feature semi-infinie
//=======================================================================

void BRepFeat_MakeDPrism::PerformUntilEnd()
{
#ifdef OCCT_DEBUG
  Standard_Boolean trc = BRepFeat_GettraceFEAT();
  if (trc)
    std::cout << "BRepFeat_MakeDPrism::PerformUntilEnd()" << std::endl;
#endif
  myPerfSelection = BRepFeat_SelectionSh;
  PerfSelectionValid();
  myGluedF.Clear();
  mySUntil.Nullify();
  ShapeUntilValid();
  mySFrom.Nullify();
  ShapeFromValid();
  Standard_Real Height = HeightMax(mySbase, mySkface, mySFrom, mySUntil);
  //  myPbase.Orientation(TopAbs_FORWARD);

  LocOpe_DPrism       theDPrism(myPbase, Height, myAngle);
  const TopoDS_Shape& VraiDPrism = theDPrism.Shape();

  MajMap(myPbase, theDPrism, myMap, myFShape, myLShape);

  myGShape = VraiDPrism;
  GeneratedShapeValid();
  GluedFacesValid();
  theDPrism.Curves(myCurves);
  myBCurve = theDPrism.BarycCurve();
  GlobalPerform();
}

//=======================================================================
// function : PerformFromEnd
// purpose  : feature semiinfinite limited by the shape Until from the other side
//=======================================================================

void BRepFeat_MakeDPrism::PerformFromEnd(const TopoDS_Shape& Until)
{
#ifdef OCCT_DEBUG
  Standard_Boolean trc = BRepFeat_GettraceFEAT();
  if (trc)
    std::cout << "BRepFeat_MakeDPrism::PerformFromEnd(From,Until)" << std::endl;
#endif
  if (Until.IsNull())
  {
    throw Standard_ConstructionError();
  }
  if (!mySkface.IsNull() && Until.IsSame(mySkface))
  {
    PerformUntilEnd();
    return;
  }
  TopExp_Explorer exp;
  exp.Init(Until, TopAbs_FACE);
  if (!exp.More())
  {
    throw Standard_ConstructionError();
  }
  //  myPbase.Orientation(TopAbs_FORWARD);
  myPerfSelection = BRepFeat_SelectionShU;
  PerfSelectionValid();
  mySFrom.Nullify();
  ShapeFromValid();
  mySUntil             = Until;
  Standard_Boolean Trf = TransformShapeFU(1);
  ShapeUntilValid();
  Handle(Geom_Curve) C      = TestCurve(myPbase);
  Standard_Integer   sens   = SensOfPrism(C, mySUntil);
  Standard_Real      Height = sens * HeightMax(mySbase, mySkface, mySFrom, mySUntil);

  LocOpe_DPrism       theDPrism(myPbase, Height, Height, myAngle);
  const TopoDS_Shape& VraiDPrism = theDPrism.Shape();
  if (VraiDPrism.IsNull())
  {
    NotDone();
    myStatusError = BRepFeat_NullRealTool;
    return;
  }

  if (!Trf)
  { // case finite face
    MajMap(myPbase, theDPrism, myMap, myFShape, myLShape);
    myGShape = VraiDPrism;
    GeneratedShapeValid();
    myGluedF.Clear();
    GluedFacesValid();
    theDPrism.Curves(myCurves);
    myBCurve = theDPrism.BarycCurve();
    GlobalPerform();
  }
  else
  { // case support
    MajMap(myPbase, theDPrism, myMap, myFShape, myLShape);
    Handle(Geom_Curve) C2;
    if (sens == -1)
    {
      C2 = C->Reversed();
    }
    else
    {
      C2 = C;
    }
    TColGeom_SequenceOfCurve scur;
    scur.Clear();
    scur.Append(C2);
    LocOpe_CSIntersector ASI1(mySUntil);
    LocOpe_CSIntersector ASI2(mySbase);
    ASI1.Perform(scur);
    ASI2.Perform(scur);
    TopAbs_Orientation OrU = TopAbs_FORWARD, OrF = TopAbs_FORWARD;
    TopoDS_Face        FUntil, FFrom;
    if (ASI1.IsDone() && ASI1.NbPoints(1) >= 1)
    {
      OrU               = ASI1.Point(1, 1).Orientation();
      Standard_Real prm = ASI1.Point(1, 1).Parameter();
      if (prm < 0)
        OrU = TopAbs::Reverse(OrU);
      FUntil = ASI1.Point(1, 1).Face();
    }

    if (ASI2.IsDone() && ASI2.NbPoints(1) >= 1)
    {
      Standard_Integer jj  = ASI2.NbPoints(1);
      Standard_Real    prm = ASI2.Point(1, 1).Parameter();
      FFrom                = ASI2.Point(1, 1).Face();
      OrF                  = ASI2.Point(1, 1).Orientation();
      OrF                  = TopAbs::Reverse(OrF);
      for (Standard_Integer iii = 1; iii <= jj; iii++)
      {
        if (ASI2.Point(1, iii).Parameter() < prm)
        {
          prm   = ASI2.Point(1, iii).Parameter();
          FFrom = ASI2.Point(1, iii).Face();
          OrF   = ASI2.Point(1, iii).Orientation();
          OrF   = TopAbs::Reverse(OrF);
        }
      }
      Handle(Geom_Surface) S = BRep_Tool::Surface(FFrom);
      if (S->DynamicType() == STANDARD_TYPE(Geom_RectangularTrimmedSurface))
      {
        S = Handle(Geom_RectangularTrimmedSurface)::DownCast(S)->BasisSurface();
      }
      BRepLib_MakeFace fac(S, Precision::Confusion());
      mySFrom = fac.Face();
      Trf     = TransformShapeFU(0);
      //      FFrom = TopoDS::Face(mySFrom);
    }

    TopoDS_Shape Comp;
    BRep_Builder B;
    B.MakeCompound(TopoDS::Compound(Comp));
    TopoDS_Solid Sol = BRepFeat::Tool(mySUntil, FUntil, OrU);
    if (!Sol.IsNull())
      B.Add(Comp, Sol);
    else
    {
      NotDone();
      myStatusError = BRepFeat_NullToolU;
      return;
    }

    TopoDS_Solid Sol1 = BRepFeat::Tool(mySFrom, FFrom, OrF);
    if (!Sol1.IsNull())
      B.Add(Comp, Sol1);
    else
    {
      NotDone();
      myStatusError = BRepFeat_NullToolF;
      return;
    }

    BRepAlgoAPI_Cut trP(VraiDPrism, Comp);
    if (myFuse == 1)
    {
      BRepAlgoAPI_Fuse f(mySbase, trP.Shape());
      myShape = f.Shape();
      UpdateDescendants(f, myShape, Standard_False);
      Done();
    }
    else if (myFuse == 0)
    {
      BRepAlgoAPI_Cut c(mySbase, trP.Shape());
      myShape = c.Shape();
      UpdateDescendants(c, myShape, Standard_False);
      Done();
    }
    else
    {
      myShape = trP.Shape();
      Done();
    }
  }
}

//=======================================================================
// function : PerformThruAll
// purpose  : feature throughout the entire initial shape
//=======================================================================

void BRepFeat_MakeDPrism::PerformThruAll()
{
#ifdef OCCT_DEBUG
  Standard_Boolean trc = BRepFeat_GettraceFEAT();
  if (trc)
    std::cout << "BRepFeat_MakeDPrism::PerformThruAll()" << std::endl;
#endif
  mySUntil.Nullify();
  ShapeUntilValid();
  mySFrom.Nullify();
  ShapeFromValid();
  if (myFuse == 0)
  {
    myPerfSelection = BRepFeat_NoSelection;
  }
  else
  {
    myPerfSelection = BRepFeat_SelectionSh;
  }

  PerfSelectionValid();
  //  myPbase.Orientation(TopAbs_FORWARD);
  myGluedF.Clear();
  GluedFacesValid();

  Standard_Real       Height = HeightMax(mySbase, mySkface, mySFrom, mySUntil);
  LocOpe_DPrism       theDPrism(myPbase, Height, Height, myAngle);
  const TopoDS_Shape& VraiDPrism = theDPrism.Shape();
  MajMap(myPbase, theDPrism, myMap, myFShape, myLShape);

  myGShape = VraiDPrism;
  GeneratedShapeValid();

  if (myFuse == 0)
  {
    BRepAlgoAPI_Cut c(mySbase, myGShape);
    if (c.IsDone())
    {
      myShape = c.Shape();
      UpdateDescendants(c, myShape, Standard_False);
      Done();
    }
  }
  else
  {
    theDPrism.Curves(myCurves);
    myBCurve = theDPrism.BarycCurve();
    GlobalPerform();
  }
}

//=======================================================================
// function : PerformUntilHeight
// purpose  : feature until the shape is of the given height
//=======================================================================

void BRepFeat_MakeDPrism::PerformUntilHeight(const TopoDS_Shape& Until, const Standard_Real Height)
{
#ifdef OCCT_DEBUG
  Standard_Boolean trc = BRepFeat_GettraceFEAT();
  if (trc)
    std::cout << "BRepFeat_MakeDPrism::PerformUntilHeight(Until,Height)" << std::endl;
#endif
  if (Until.IsNull())
  {
    Perform(Height);
  }
  if (Height == 0)
  {
    Perform(Until);
  }
  TopExp_Explorer exp(Until, TopAbs_FACE);
  if (!exp.More())
  {
    throw Standard_ConstructionError();
  }
  //  myPbase.Orientation(TopAbs_FORWARD);
  myGluedF.Clear();
  myPerfSelection = BRepFeat_NoSelection;
  PerfSelectionValid();
  mySFrom.Nullify();
  ShapeFromValid();
  mySUntil             = Until;
  Standard_Boolean Trf = TransformShapeFU(1);
  ShapeUntilValid();
  Handle(Geom_Curve) C    = TestCurve(myPbase);
  Standard_Integer   sens = SensOfPrism(C, mySUntil);

  LocOpe_DPrism       theDPrism(myPbase, sens * Height, myAngle);
  const TopoDS_Shape& VraiDPrism = theDPrism.Shape();

  if (!Trf)
  { // case face finished
    MajMap(myPbase, theDPrism, myMap, myFShape, myLShape);
    myGShape = VraiDPrism;
    GeneratedShapeValid();
    const TopoDS_Shape& Base = theDPrism.FirstShape();
    exp.Init(Base, TopAbs_FACE);
    TopoDS_Face theBase = TopoDS::Face(exp.Current());
    exp.Next();
    if (exp.More())
    {
      NotDone();
      myStatusError = BRepFeat_InvFirstShape;
      return;
    }

    GluedFacesValid();
    theDPrism.Curves(myCurves);
    myBCurve = theDPrism.BarycCurve();
    GlobalPerform();
  }
  else
  { // case support
    MajMap(myPbase, theDPrism, myMap, myFShape, myLShape);
    Handle(Geom_Curve) C1;
    if (sens == -1)
    {
      C1 = C->Reversed();
    }
    else
    {
      C1 = C;
    }
    TColGeom_SequenceOfCurve scur;
    scur.Clear();
    scur.Append(C1);
    LocOpe_CSIntersector ASI(mySUntil);
    ASI.Perform(scur);
    TopAbs_Orientation Or;
    if (ASI.IsDone() && ASI.NbPoints(1) >= 1)
    {
      if (myFuse == 1)
      {
        Or = ASI.Point(1, 1).Orientation();
      }
      else
      {
        Or = ASI.Point(1, ASI.NbPoints(1)).Orientation();
      }
      //      Standard_Real prm = ASI.Point(1,1).Parameter();
      //      if(prm < 0) Or = TopAbs::Reverse(Or);
      TopoDS_Face  FUntil = ASI.Point(1, 1).Face();
      TopoDS_Shape Comp;
      BRep_Builder B;
      B.MakeCompound(TopoDS::Compound(Comp));
      TopoDS_Solid S = BRepFeat::Tool(mySUntil, FUntil, Or);
      if (!S.IsNull())
        B.Add(Comp, S);
      BRepAlgoAPI_Cut trP(VraiDPrism, Comp);
      if (myFuse == 1)
      {
        BRepAlgoAPI_Fuse f(mySbase, trP.Shape());
        myShape = f.Shape();
        UpdateDescendants(f, myShape, Standard_False);
        Done();
      }
      else if (myFuse == 0)
      {
        BRepAlgoAPI_Cut c(mySbase, trP.Shape());
        myShape = c.Shape();
        UpdateDescendants(c, myShape, Standard_False);
        Done();
      }
      else
      {
        myShape = trP.Shape();
        Done();
      }
    }
  }
}

//=======================================================================
// function : Curves
// purpose  : curves parallel to the axis of the prism
//=======================================================================

void BRepFeat_MakeDPrism::Curves(TColGeom_SequenceOfCurve& scur)
{
  scur = myCurves;
}

//============================================================================
// function : BRepFeat_BossEgdes
// purpose: Determination of TopEdges and LatEdges.
//          sig = 1 -> TopEdges = FirstShape of the DPrism
//          sig = 2 -> TOpEdges = LastShape of the DPrism
//============================================================================
void BRepFeat_MakeDPrism::BossEdges(const Standard_Integer signature)
{
#ifdef OCCT_DEBUG
  Standard_Boolean trc = BRepFeat_GettraceFEAT();
  if (trc)
    std::cout << "BRepFeat_MakeDPrism::BossEdges (integer)" << std::endl;
#endif
  TopTools_ListOfShape theLastShape;
  theLastShape.Clear();
  if (signature == 1 || signature == -1)
  {
    theLastShape = FirstShape();
  }
  else if (signature == 2 || signature == -2)
  {
    theLastShape = LastShape();
  }
  else
  {
    return;
  }

  // Edges Top
  TopTools_ListIteratorOfListOfShape itLS;
  TopExp_Explorer                    ExpE;
  for (itLS.Initialize(theLastShape); itLS.More(); itLS.Next())
  {
    const TopoDS_Face& FF = TopoDS::Face(itLS.Value());
    for (ExpE.Init(FF, TopAbs_EDGE); ExpE.More(); ExpE.Next())
    {
      const TopoDS_Edge& EE = TopoDS::Edge(ExpE.Current());
      myTopEdges.Append(EE);
    }
  }

  // Edges Bottom
  if (signature < 0)
  {
    // Attention check if TgtEdges is important
    myLatEdges = NewEdges();
  }
  else if (signature > 0)
  {
    if (!myShape.IsNull())
    {
      TopTools_MapOfShape MapE;
      Standard_Boolean    Found;

      TopExp_Explorer ExpF;
      for (ExpF.Init(myShape, TopAbs_FACE); ExpF.More(); ExpF.Next())
      {
        Found                 = Standard_False;
        const TopoDS_Face& FF = TopoDS::Face(ExpF.Current());
        for (itLS.Initialize(theLastShape); itLS.More(); itLS.Next())
        {
          const TopoDS_Face& TopFace = TopoDS::Face(itLS.Value());
          if (!FF.IsSame(TopFace))
          {
            for (ExpE.Init(FF, TopAbs_EDGE); ExpE.More() && !Found; ExpE.Next())
            {
              const TopoDS_Edge& E1 = TopoDS::Edge(ExpE.Current());
              TopoDS_Vertex      V1, V2;
              TopExp::Vertices(E1, V1, V2);
              TopTools_ListIteratorOfListOfShape it(myTopEdges);
              for (; it.More() && !Found; it.Next())
              {
                TopoDS_Edge   E2 = TopoDS::Edge(it.Value());
                TopoDS_Vertex VT1, VT2;
                TopExp::Vertices(E2, VT1, VT2);

                if (V1.IsSame(VT1) || V1.IsSame(VT2) || V2.IsSame(VT1) || V2.IsSame(VT2))
                {
                  Found = Standard_True;
                  TopExp_Explorer ExpE2;
                  for (ExpE2.Init(FF, TopAbs_EDGE); ExpE2.More(); ExpE2.Next())
                  {
                    const TopoDS_Edge& E3 = TopoDS::Edge(ExpE2.Current());
                    if (MapE.Contains(E3))
                    {
                      MapE.Remove(E3);
                    }
                    else
                    {
                      MapE.Add(E3);
                    }
                  }
                }
              }
            }
          }
        }
      }

      TopTools_ListIteratorOfListOfShape it(myTopEdges);
      for (; it.More(); it.Next())
      {
        if (MapE.Contains(it.Value()))
        {
          MapE.Remove(it.Value());
        }
      }

      TopTools_MapIteratorOfMapOfShape itMap;
      for (itMap.Initialize(MapE); itMap.More(); itMap.Next())
      {
        if (!BRep_Tool::Degenerated(TopoDS::Edge(itMap.Key())))
          myLatEdges.Append(itMap.Key());
      }
    }
  }
}

//============================================================================
// function : BRepFeat_TopEgdes
// Purpose: Returns the list of TopoDS Edges of the top of the boss
//============================================================================
const TopTools_ListOfShape& BRepFeat_MakeDPrism::TopEdges()
{
  return myTopEdges;
}

//============================================================================
// function : BRepFeat_LatEgdes
// Purpose: Returns the list of TopoDS Edges of the top of the boss
//============================================================================
const TopTools_ListOfShape& BRepFeat_MakeDPrism::LatEdges()
{
  return myLatEdges;
}

//=======================================================================
// function : BarycCurve
// purpose  : passe par le centre de masses de la primitive
//=======================================================================

Handle(Geom_Curve) BRepFeat_MakeDPrism::BarycCurve()
{
  return myBCurve;
}

//=======================================================================
// function : HeightMax
// purpose  : Calculate the height of the prism following the parameters of the bounding box
//=======================================================================

static Standard_Real HeightMax(const TopoDS_Shape& theSbase,  // shape initial
                               const TopoDS_Face&  theSkface, // face de sketch
                               const TopoDS_Shape& theSFrom,  // shape from
                               const TopoDS_Shape& theSUntil) // shape until
{
  Bnd_Box Box;
  BRepBndLib::Add(theSbase, Box);
  BRepBndLib::Add(theSkface, Box);
  if (!theSFrom.IsNull())
  {
    BRepBndLib::Add(theSFrom, Box);
  }
  if (!theSUntil.IsNull())
  {
    BRepBndLib::Add(theSUntil, Box);
  }
  Standard_Real c[6];

  Box.Get(c[0], c[2], c[4], c[1], c[3], c[5]);
  //  Standard_Real parmin=c[0], parmax = c[0];
  //  for(Standard_Integer i = 0 ; i < 6; i++) {
  //    if(c[i] > parmax) parmax = c[i];
  //    if(c[i] < parmin ) parmin = c[i];
  //  }
  //  Standard_Real Height = abs(2.*(parmax - parmin));
  //  return(2.*Height);
  // #ifndef OCCT_DEBUG
  Standard_Real par = Max(Max(fabs(c[1] - c[0]), fabs(c[3] - c[2])), fabs(c[5] - c[4]));
  // #else
  //   Standard_Real par = Max(  Max( abs(c[1] - c[0]), abs(c[3] - c[2]) ), abs(c[5] - c[4]) );
  // #endif
#ifdef OCCT_DEBUG
  std::cout << "Height = > " << par << std::endl;
#endif
  return par;
}

//=======================================================================
// function : SensOfPrism
// purpose  : determine the direction of prism generation
//=======================================================================
Standard_Integer SensOfPrism(const Handle(Geom_Curve)& C, const TopoDS_Shape& Until)
{
  LocOpe_CSIntersector     ASI1(Until);
  TColGeom_SequenceOfCurve scur;
  scur.Append(C);
  ASI1.Perform(scur);
  Standard_Integer sens = 1;
  if (ASI1.IsDone() && ASI1.NbPoints(1) >= 1)
  {
    Standard_Integer nb   = ASI1.NbPoints(1);
    Standard_Real    prm1 = ASI1.Point(1, 1).Parameter();
    Standard_Real    prm2 = ASI1.Point(1, nb).Parameter();
    if (prm1 < 0. && prm2 < 0.)
    {
      sens = -1;
    }
  }
  else if (BRepFeat::ParametricBarycenter(Until, C) < 0)
  {
    sens = -1;
  }
  else
  {
  }
  return sens;
}

//=================================================================================================

static void MajMap(const TopoDS_Shape&                 theB,
                   const LocOpe_DPrism&                theP,
                   TopTools_DataMapOfShapeListOfShape& theMap,    // myMap
                   TopoDS_Shape&                       theFShape, // myFShape
                   TopoDS_Shape&                       theLShape)                       // myLShape
{
  TopExp_Explorer exp;
  if (!theP.FirstShape().IsNull())
  {
    exp.Init(theP.FirstShape(), TopAbs_WIRE);
    if (exp.More())
    {
      theFShape = exp.Current();
      TopTools_ListOfShape thelist;
      theMap.Bind(theFShape, thelist);
      for (exp.Init(theP.FirstShape(), TopAbs_FACE); exp.More(); exp.Next())
      {
        theMap(theFShape).Append(exp.Current());
      }
    }
  }

  if (!theP.LastShape().IsNull())
  {
    exp.Init(theP.LastShape(), TopAbs_WIRE);
    if (exp.More())
    {
      theLShape = exp.Current();
      TopTools_ListOfShape thelist1;
      theMap.Bind(theLShape, thelist1);
      for (exp.Init(theP.LastShape(), TopAbs_FACE); exp.More(); exp.Next())
      {
        theMap(theLShape).Append(exp.Current());
      }
    }
  }

  for (exp.Init(theB, TopAbs_EDGE); exp.More(); exp.Next())
  {
    if (!theMap.IsBound(exp.Current()))
    {
      const TopoDS_Edge&   edg = TopoDS::Edge(exp.Current());
      TopTools_ListOfShape thelist2;
      theMap.Bind(edg, thelist2);
      theMap(edg) = theP.Shapes(edg);
    }
  }
}

//=================================================================================================

static Handle(Geom_Curve) TestCurve(const TopoDS_Face& Base)
{
  gp_Pnt               bar(0., 0., 0.);
  TColgp_SequenceOfPnt spt;
  LocOpe::SampleEdges(Base, spt);
  for (Standard_Integer jj = 1; jj <= spt.Length(); jj++)
  {
    const gp_Pnt& pvt = spt(jj);
    bar.ChangeCoord() += pvt.XYZ();
  }
  bar.ChangeCoord().Divide(spt.Length());
  Handle(Geom_Surface) s = BRep_Tool::Surface(Base);
  if (s->DynamicType() == STANDARD_TYPE(Geom_RectangularTrimmedSurface))
  {
    s = Handle(Geom_RectangularTrimmedSurface)::DownCast(s)->BasisSurface();
  }
  Handle(Geom_Plane) P = Handle(Geom_Plane)::DownCast(s);
  if (P.IsNull())
  {
    Handle(Geom_Curve) toto;
    return toto;
  }
  gp_Pln            pp = P->Pln();
  gp_Dir            Normale(pp.Position().XDirection() ^ pp.Position().YDirection());
  gp_Ax1            theAx(bar, Normale);
  Handle(Geom_Line) theLin = new Geom_Line(theAx);
  return theLin;
}
