
#include <BRepGProp.hxx>
#include <BRepGProp_Cinert.hxx>
#include <BRepGProp_Sinert.hxx>
#include <BRepGProp_Vinert.hxx>
#include <BRepGProp_Face.hxx>
#include <BRepGProp_Domain.hxx>
#include <TopoDS.hxx>
#include <BRepAdaptor_Curve.hxx>

#include <TopTools.hxx>
#include <BRep_Tool.hxx>  //Precision::PConfusion();  
#ifdef DEB
static Standard_Integer AffichEps = 0;
#endif

static gp_Pnt roughBaryCenter(const TopoDS_Shape& S){
  Standard_Integer i;  TopExp_Explorer ex;
  gp_XYZ xyz(0,0,0);
  for (ex.Init(S,TopAbs_VERTEX), i = 1; ex.More(); ex.Next(), i++) 
    xyz += BRep_Tool::Pnt(TopoDS::Vertex(ex.Current())).XYZ();
  xyz /= i;
  return gp_Pnt(xyz);
}

void  BRepGProp::LinearProperties(const TopoDS_Shape& S, GProp_GProps& SProps){
  // find the origin
  gp_Pnt P(0,0,0);
  P.Transform(S.Location());
  SProps = GProp_GProps(P);

  BRepAdaptor_Curve   BAC;
//  Standard_Integer n,i;
  TopExp_Explorer ex;
  for (ex.Init(S,TopAbs_EDGE); ex.More(); ex.Next()) {
    BAC.Initialize(TopoDS::Edge(ex.Current()));
    BRepGProp_Cinert CG(BAC,P);
    SProps.Add(CG);
  }
}

static Standard_Real surfaceProperties(const TopoDS_Shape& S, GProp_GProps& Props, const Standard_Real Eps){
  Standard_Integer i, iErrorMax = 0;
  Standard_Real ErrorMax = 0.0, Error;
  TopExp_Explorer ex; 
  gp_Pnt P(roughBaryCenter(S));
  BRepGProp_Sinert G;  G.SetLocation(P);

  BRepGProp_Face   BF;
  BRepGProp_Domain BD;

  for (ex.Init(S,TopAbs_FACE), i = 1; ex.More(); ex.Next(), i++) {
    const TopoDS_Face& F = TopoDS::Face(ex.Current());
    BF.Load(F);
    if(!BF.NaturalRestriction()) BD.Init(F);
    if(Eps < 1.0) {
      G.Perform(BF, BD, Eps); 
      Error = G.GetEpsilon();
      if(ErrorMax < Error){ ErrorMax = Error; iErrorMax = i;}
    } else {
      if(BF.NaturalRestriction()) G.Perform(BF);
      else G.Perform(BF, BD);
    }
    Props.Add(G);
#ifdef DEB
    if(AffichEps) cout<<"\n"<<i<<":\tEpsArea = "<< G.GetEpsilon();
#endif
  }
#ifdef DEB
  if(AffichEps) cout<<"\n-----------------\n"<<iErrorMax<<":\tMaxError = "<<ErrorMax<<"\n";
#endif
  return ErrorMax;
}
void  BRepGProp::SurfaceProperties(const TopoDS_Shape& S, GProp_GProps& Props){
  // find the origin
  gp_Pnt P(0,0,0);
  P.Transform(S.Location());
  Props = GProp_GProps(P);
  surfaceProperties(S,Props,1.0);
}
Standard_Real BRepGProp::SurfaceProperties(const TopoDS_Shape& S, GProp_GProps& Props, const Standard_Real Eps){ 
  // find the origin
  gp_Pnt P(0,0,0);  P.Transform(S.Location());
  Props = GProp_GProps(P);
  Standard_Real ErrorMax = surfaceProperties(S,Props,Eps);
  return ErrorMax;
}

static Standard_Real volumeProperties(const TopoDS_Shape& S, GProp_GProps& Props, const Standard_Real Eps){
  Standard_Integer i, iErrorMax = 0;
  Standard_Real ErrorMax = 0.0, Error = 0.0;
  TopExp_Explorer ex; 
  gp_Pnt P(roughBaryCenter(S)); 
  BRepGProp_Vinert G;  G.SetLocation(P);

  BRepGProp_Face   BF;
  BRepGProp_Domain BD;

  for (ex.Init(S,TopAbs_FACE), i = 1; ex.More(); ex.Next(), i++) {
    const TopoDS_Face& F = TopoDS::Face(ex.Current());
    if ((F.Orientation() == TopAbs_FORWARD) || (F.Orientation() == TopAbs_REVERSED)){
      BF.Load(F);
      if(!BF.NaturalRestriction()) BD.Init(F);
      if(Eps < 1.0) {
	G.Perform(BF, BD, Eps); 
	Error = G.GetEpsilon();
	if(ErrorMax < Error){ ErrorMax = Error; iErrorMax = i;}
      }
      else {
	if(BF.NaturalRestriction()) G.Perform(BF);
	else G.Perform(BF, BD);
      }
      Props.Add(G);
#ifdef DEB
      if(AffichEps) cout<<"\n"<<i<<":\tEpsVolume = "<< G.GetEpsilon();
#endif
    }
  }
#ifdef DEB
  if(AffichEps) cout<<"\n-----------------\n"<<iErrorMax<<":\tMaxError = "<<ErrorMax<<"\n";
#endif
  return ErrorMax;
}
void  BRepGProp::VolumeProperties(const TopoDS_Shape& S, GProp_GProps& Props, const Standard_Boolean OnlyClosed){
  // find the origin
  gp_Pnt P(0,0,0);  P.Transform(S.Location());
  Props = GProp_GProps(P);
  Standard_Real Error = 0.0;
  if(OnlyClosed){
    TopExp_Explorer ex(S,TopAbs_SHELL);
    for (; ex.More(); ex.Next()) {
      const TopoDS_Shape& Sh = ex.Current();
      if(BRep_Tool::IsClosed(Sh)) Error = volumeProperties(Sh,Props,1.0);
    }
  } else Error = volumeProperties(S,Props,1.0);
}

Standard_Real BRepGProp::VolumeProperties(const TopoDS_Shape& S, GProp_GProps& Props, 
					  const Standard_Real Eps, const Standard_Boolean OnlyClosed)
{ 
  // find the origin
  gp_Pnt P(0,0,0);  P.Transform(S.Location());
  Props = GProp_GProps(P);
  Standard_Integer i, iErrorMax = 0;
  Standard_Real ErrorMax = 0.0, Error = 0.0;
  if(OnlyClosed){
    TopExp_Explorer ex(S,TopAbs_SHELL);
    for (i = 1; ex.More(); ex.Next(), i++) {
      const TopoDS_Shape& Sh = ex.Current();
      if(BRep_Tool::IsClosed(Sh)) {
	Error = volumeProperties(Sh,Props,Eps);
	if(ErrorMax < Error){ ErrorMax = Error; iErrorMax = i;}
      }
    }
  } else ErrorMax = volumeProperties(S,Props,Eps);
#ifdef DEB
  if(AffichEps) cout<<"\n\n==================="<<iErrorMax<<":\tMaxEpsVolume = "<<ErrorMax<<"\n";
#endif
  return ErrorMax;
} 
