// File:	AISViewer.cxx
// Created:	Fri Oct  3 13:46:01 1997
// Author:	Bertrand LESECQ
//		<bbl@phonox.paris3.matra-dtv.fr>

// JR 21 Oct 1999 : Change for Draw_Init_Appli which is in main and is
//                  called from Draw ===> undefined symbol on UNIX
//                                   ===> duplication of code on NT :
//                  One argument added to DrawAppli : Draw_Init_Appli ===>
//                  Draw_Appli of Draw/TKDraw may call Draw_Init_Appli

#include <tcl.h>

#include <BRepTest.hxx>
#include <CorrectTest.hxx>
#include <GeometryTest.hxx>
#include <GeomliteTest.hxx>
#include <MeshTest.hxx>
#include <HLRTest.hxx>
#include <BRepAdaptor_Surface.hxx>
#include <BRepAlgoAPI_Common.hxx>
#include <BRepAlgoAPI_Cut.hxx>
#include <BRepAlgoAPI_Fuse.hxx>
#include <BRepBuilderAPI_Copy.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepBuilderAPI_MakePolygon.hxx>
#include <BRepBuilderAPI_MakeSolid.hxx>
#include <BRepBuilderAPI_MakeVertex.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include <BRepBuilderAPI_TransitionMode.hxx>
#include <BRepCheck_Analyzer.hxx>
#include <BRepGProp.hxx>
#include <BRepLProp_SLProps.hxx>
#include <BRepOffsetAPI_DraftAngle.hxx>
#include <BRepOffsetAPI_MakeEvolved.hxx>
#include <BRepOffsetAPI_MakeOffsetShape.hxx>
#include <BRepOffsetAPI_MakePipe.hxx>
#include <BRepOffsetAPI_MakePipeShell.hxx>
#include <BRepOffsetAPI_MakeThickSolid.hxx>
#include <BRepOffsetAPI_Sewing.hxx>
#include <BRepOffsetAPI_ThruSections.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <BRepPrimAPI_MakeCone.hxx>
#include <BRepPrimAPI_MakeCylinder.hxx>
#include <BRepPrimAPI_MakePrism.hxx>
#include <BRepPrimAPI_MakeRevol.hxx>
#include <BRepPrimAPI_MakeSphere.hxx>
#include <BRepPrimAPI_MakeTorus.hxx>
#include <BRepPrimAPI_MakeWedge.hxx>
#include <BRepTest.hxx>
#include <BRep_Tool.hxx>
#include <BRepTools.hxx>
#include <CorrectTest.hxx>
#include <DBRep.hxx>
#include <Draw_Appli.hxx>
#include <Draw_Interpretor.hxx>
#include <DrawTrSurf.hxx>
#include <ElCLib.hxx>
#include <GccAna_Circ2d2TanRad.hxx>
#include <GccEnt.hxx>
#include <GccEnt_Position.hxx>
#include <GccEnt_QualifiedCirc.hxx>
#include <gce_MakeCirc.hxx>
#include <gce_MakeDir.hxx>
#include <gce_MakePln.hxx>
#include <gce_MakeRotation.hxx>
#include <GC_MakeArcOfCircle.hxx>
#include <Geom2d_Circle.hxx>
#include <Geom2d_TrimmedCurve.hxx>
#include <GeomAPI_Interpolate.hxx>
#include <Geom_BezierCurve.hxx>
#include <Geom_BSplineCurve.hxx>
#include <Geom_CartesianPoint.hxx>
#include <Geom_Circle.hxx>
#include <GeomConvert.hxx>
#include <Geom_Ellipse.hxx>
#include <GeometryTest.hxx>
#include <GeomFill_Pipe.hxx>
#include <GeomLib_IsPlanarSurface.hxx>
#include <Geom_Line.hxx>
#include <GeomliteTest.hxx>
#include <Geom_Plane.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <gp_Ax22d.hxx>
#include <gp_Circ2d.hxx>
#include <gp_Circ.hxx>
#include <gp_Pln.hxx>
#include <GProp_GProps.hxx>
#include <gp_TrsfForm.hxx>
#include <gp_Vec.hxx>
#include <HLRTest.hxx>
#include <Law_Linear.hxx>
#include <MeshTest.hxx>
#include <Precision.hxx>
#include <TColgp_HArray1OfPnt.hxx>
#include <TopAbs_Orientation.hxx>
#include <TopAbs_ShapeEnum.hxx>
#include <TopExp_Explorer.hxx>
#include <TopExp.hxx>
#include <TopLoc_Location.hxx>
#include <TopoDS_Compound.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Shell.hxx>
#include <TopoDS_Solid.hxx>
#include <TopoDS_Wire.hxx>

#include <Draw_Appli.hxx>

// Specific Command for AISViewer
#include <ViewerTest.hxx>
#include <Viewer2dTest.hxx>

#include "wb3dutility.hpp"

static Standard_Integer btube(Draw_Interpretor& di,
			      Standard_Integer argc, char** argv)
{

  // Used to Display Geometry or Topolgy
  char name[255];
  char *pname = name;
  bool check = true;

  // Set default arguments
  double radius_l = 20.0;
  double radius_r = 80.0;
  double bend_angle = M_PI/2.0;
  double major_rad = 280.0;
  double wall_thickness = 10.0;
		    

  // Convert arguments
  if (argc>1) radius_l = atof(argv[1]);
  if (argc>2) radius_r = atof(argv[2]);
  if (argc>3) bend_angle = atof(argv[3]);
  if (argc>4) major_rad = atof(argv[4]);
  if (argc>5) wall_thickness = atof(argv[5]);

  if ((bend_angle >= 2.0*M_PI)) {
    cerr << "The arguments are invalid." << endl;
    return(TCL_ERROR);
  }
  cout << "creating the shape for a bent tube" << endl;
 
  gp_Ax2 origin(gp_Pnt(500.0,-300.0, 100.0),
		gp_Dir(0.0, -1.0/sqrt(2.0), -1.0/sqrt(2.0)));

  TopoDS_Face firstFace, lastFace;
  TopoDS_Solid wallSolid, myShape;
  // Construct a circle for the first face, on the xy-plane at the origin
  gp_Pln circ1Plane(origin.Location(), origin.Direction());
  gp_Circ faceCircle(origin, radius_l);
  gp_Circ outFaceCircle(origin, radius_l+wall_thickness);

  // Construct center for a circle to be the spine of
  // revolution, on the xz-plane at x=major_rad
  gp_Pnt circ_center = origin.Location().Translated(
						    major_rad*origin.XDirection()
						    );

  // This point will be the center of the second face.
  gp_Pnt endPoint = origin.Location();
  endPoint.Translate(major_rad*(1.0-cos(bend_angle))*origin.XDirection()) ;
  endPoint.Translate((-major_rad*sin(bend_angle))*origin.Direction());

  // Construct the plane for the second face to sit on.
  gp_Pln circ2Plane = gce_MakePln(circ_center, endPoint,
				  endPoint.Translated(major_rad*origin.YDirection())
				  ).Value();

  // The circle used for the spine.
  gp_Ax2 spineAxis(circ_center, origin.YDirection(), origin.XDirection());
  gp_Circ circle(spineAxis, major_rad);

  gp_Ax2 circ2axis(endPoint, circ2Plane.Axis().Direction(), origin.YDirection());
  gp_Circ faceCircle2(circ2axis,radius_r);
  gp_Circ outFaceCircle2(circ2axis,radius_r+wall_thickness);

  TopoDS_Edge E1_1 = BRepBuilderAPI_MakeEdge(faceCircle, 0, PI);
  TopoDS_Edge E1_2 = BRepBuilderAPI_MakeEdge(faceCircle, PI, 2.*PI);
  TopoDS_Wire Wire1_ = BRepBuilderAPI_MakeWire(E1_1, E1_2);
  
  // Create the face at the near end for the wall solid, an annular ring.
  TopoDS_Edge Eout1_1 = BRepBuilderAPI_MakeEdge(outFaceCircle, 0, PI);
  TopoDS_Edge Eout1_2 = BRepBuilderAPI_MakeEdge(outFaceCircle, PI, 2.*PI);
  TopoDS_Wire outerWire1_ = BRepBuilderAPI_MakeWire(Eout1_1, Eout1_2);
 
  TopoDS_Edge E2_1 = BRepBuilderAPI_MakeEdge(faceCircle2, 0, PI);
  TopoDS_Edge E2_2 = BRepBuilderAPI_MakeEdge(faceCircle2, PI, 2.*PI);
  TopoDS_Wire Wire2_ = BRepBuilderAPI_MakeWire(E2_1, E2_2);
  
  // Create the face at the far end for the wall solid, an annular ring.
  TopoDS_Edge Eout2_1 = BRepBuilderAPI_MakeEdge(outFaceCircle2, 0, PI);
  TopoDS_Edge Eout2_2 = BRepBuilderAPI_MakeEdge(outFaceCircle2, PI, 2.*PI);
  TopoDS_Wire outerWire2_ = BRepBuilderAPI_MakeWire(Eout2_1, Eout2_2);

  BRepBuilderAPI_MakeFace mkFace;

  Handle(Geom_Curve) SpineCurve = GC_MakeArcOfCircle(circle,
						     endPoint,
						     origin.Location(),
						     Standard_True).Value();
  Handle(Law_Linear) myLaw = new Law_Linear();
  Handle(Law_Linear) myLaw2 = new Law_Linear();

  myLaw->Set(SpineCurve->FirstParameter(),
	     radius_r/radius_l,
	     SpineCurve->LastParameter(),
	     1.0);

  myLaw2->Set(SpineCurve->FirstParameter(),
	      (radius_r+wall_thickness)/(radius_l+wall_thickness),
	      SpineCurve->LastParameter(),
	      1.0);

  cout << "SpineCurve->FirstParameter() is " << SpineCurve->FirstParameter() << endl;
  cout << "SpineCurve->LastParameter() is " << SpineCurve->LastParameter() << endl;
  cout << "Law1 Value at FirstParameter() is " << myLaw->Value(SpineCurve->FirstParameter()) << endl;
  cout << "Law1 Value at LastParameter() is " << myLaw->Value(SpineCurve->LastParameter()) << endl;
  cout << "radius_r / radius_l is " << radius_r/radius_l << endl;

  BRepBuilderAPI_MakeEdge mkEdge;

  mkEdge.Init(SpineCurve);
  if (!mkEdge.IsDone()) return TCL_ERROR;
  TopoDS_Wire SpineWire = BRepBuilderAPI_MakeWire(mkEdge.Edge()).Wire();

  sprintf (name,"SpineWire");
  DBRep::Set(name,SpineWire);

  sprintf (name,"Wire1_");
  DBRep::Set(name,Wire1_);

  sprintf (name,"outerWire1_");
  DBRep::Set(name,outerWire1_);

  sprintf (name,"Wire2_");
  DBRep::Set(name,Wire2_);

  sprintf (name,"outerWire2_");
  DBRep::Set(name,outerWire2_);

  di.Eval("fit");

  TopoDS_Vertex Location1, Location2;

  TopExp::Vertices(SpineWire, Location2, Location1);

  sprintf (name,"Location1");
  DBRep::Set(name,Location1);

  sprintf (name,"Location2");
  DBRep::Set(name,Location2);

  // Make inner pipe shell
  BRepOffsetAPI_MakePipeShell mkPipe1(SpineWire);
  mkPipe1.SetTolerance(1.0e-8,1.0e-8,1.0e-6);
  mkPipe1.SetTransitionMode(BRepBuilderAPI_Transformed);
  mkPipe1.SetMode(Standard_False);
  mkPipe1.SetLaw(Wire1_, myLaw, Location1, Standard_False, Standard_False);
  mkPipe1.Build();
  if (!mkPipe1.IsDone()) return TCL_ERROR;

  // Make outer pipe shell
  BRepOffsetAPI_MakePipeShell mkPipe2(SpineWire);
  mkPipe2.SetTolerance(1.0e-8,1.0e-8,1.0e-6);
  mkPipe2.SetTransitionMode(BRepBuilderAPI_Transformed);
  mkPipe2.SetMode(Standard_False);
  mkPipe2.SetLaw(outerWire1_, myLaw2, Location1, Standard_False, Standard_False);
 mkPipe2.Build();
  if (!mkPipe2.IsDone()) return TCL_ERROR;

//    sprintf(name,"w1-first");
//    DBRep::Set(name,mkPipe1.FirstShape());

//    sprintf(name,"w1-last");
//    DBRep::Set(name,mkPipe1.LastShape());

//    sprintf(name,"w2-first");
//    DBRep::Set(name,mkPipe2.FirstShape());

//    sprintf(name,"w2-last");
//    DBRep::Set(name,mkPipe2.LastShape());

  BRepOffsetAPI_Sewing SewIt(1.0e-4);

  // Make tube
  TopExp_Explorer getFaces;
  TopoDS_Face test_face;
  getFaces.Init(mkPipe1.Shape(), TopAbs_FACE);
  while (getFaces.More())
    {
      SewIt.Add(getFaces.Current().Reversed());
      getFaces.Next();
    }

  // Make face for first opening
  Handle(Geom_Plane) Plane1 = new Geom_Plane(circ1Plane);
  mkFace.Init(Plane1,Standard_False);
  mkFace.Add(TopoDS::Wire(outerWire1_));
  mkFace.Add(TopoDS::Wire(Wire1_.Reversed()));
  if (!mkFace.IsDone()) return TCL_ERROR;
  TopoDS_Face Face1 = mkFace.Face();

  // Make face for second opening
  Handle(Geom_Plane) Plane2 = new Geom_Plane(circ2Plane);
  mkFace.Init(Plane2,Standard_False);
  mkFace.Add(TopoDS::Wire(outerWire2_));
  mkFace.Add(TopoDS::Wire(Wire2_.Reversed()));
  if (!mkFace.IsDone()) return TCL_ERROR;
  TopoDS_Face Face2 = mkFace.Face();

  // Grab the gas solid now that we've extracted the faces.
  mkPipe1.MakeSolid();
  myShape = TopoDS::Solid(mkPipe1.Shape());

  getFaces.Clear();
  getFaces.Init(mkPipe2.Shape(), TopAbs_FACE);
  while (getFaces.More())
    {
      SewIt.Add(getFaces.Current());
      getFaces.Next();
    }

  SewIt.Add(Face1.Reversed());
  SewIt.Add(Face2);

  SewIt.Perform();

  cout << "The result of the Sewing operation is a ";
  // Check to see if we have a solid
  switch (SewIt.SewedShape().ShapeType()) {
  case (TopAbs_COMPOUND):
    cout << "TopAbs_COMPOUND" << endl;
    break;
  case (TopAbs_COMPSOLID):
    cout << "TopAbs_COMPSOLID" << endl;
    break;
  case (TopAbs_SOLID):
    cout << "TopAbs_SOLID" << endl;
    break;
  case (TopAbs_SHELL):
    cout << "TopAbs_SHELL" << endl;
    break;
  case (TopAbs_FACE):
    cout << "TopAbs_FACE" << endl;
    break;
  case (TopAbs_WIRE):
    cout << "TopAbs_WIRE" << endl;
    break;
  case (TopAbs_EDGE):
    cout << "TopAbs_EDGE" << endl;
    break;
  case (TopAbs_VERTEX):
    cout << "TopAbs_VERTEX" << endl;
    break;
  case (TopAbs_SHAPE):
    cout << "TopAbs_SHAPE" << endl;
  }

  BRep_Builder B;

  TopoDS_Shell TubeShell;
  cout << "Can we turn it into a shell? ";
  try {
    TubeShell = TopoDS::Shell(SewIt.SewedShape());
    B.MakeSolid(wallSolid);
    B.Add(wallSolid,TubeShell);
    cout << " yes" << endl;
  }
  catch (Standard_TypeMismatch) {
    cout << "Can't convert to shell..." << endl;
    TopExp_Explorer getSol;
    getSol.Init(SewIt.SewedShape(), TopAbs_SOLID);
    if (getSol.More()) {
      cout << "First solid found in compound" << endl;
      wallSolid = TopoDS::Solid(getSol.Current());
      TopoDS_Solid test_solid;
      while (getSol.More())
	{
	  cout << "Next solid found in compound" << endl;
	  getSol.Next();
	  test_solid = TopoDS::Solid(getSol.Current());
	  BRepAlgoAPI_Fuse fuser(test_solid, wallSolid);
	  fuser.Build();
	  wallSolid = TopoDS::Solid(fuser.Shape());
	}
    } else {
      // Let's see if we can extract shells instead of solids.
      TopExp_Explorer getShel;
      getShel.Init(SewIt.SewedShape(), TopAbs_SHELL);
      if (getShel.More()) {
	cout << "First shell found in compound" << endl;
	B.MakeSolid(wallSolid);
	cout << "B.Add(wallSolid,TopoDS::Shell(getShel.Current()));" << endl;
	int i = 1;
	while (getShel.More())
	  {
	    cout << "Next shell found in compound" << endl;
	    cout << "B.Add(wallSolid,TopoDS::Shell(getShel.Current()));" << endl;
	    sprintf(name,"shell%d", i++);
	    DBRep::Set(name,getShel.Current());
	    B.Add(wallSolid,TopoDS::Shell(getShel.Current()));
	    getShel.Next();
	  }
      }
    }
  }

  sprintf(name,"result");
  DBRep::Set(name,wallSolid);

  // Now calculated the volume of the outside tube.
  GProp_GProps gprops;
  BRepGProp::VolumeProperties(wallSolid, gprops);
  cout << "The wallSolid's volume is: " << gprops.Mass() << endl;

  if (check) {
    if (!(BRepCheck_Analyzer(wallSolid).IsValid()))
      cerr << "The TopoDS_Solid was checked, and it was invalid!" << endl;
    else
      cout << "The TopoDS_Solid was checked, and it was valid." << endl;
    if (!wallSolid.Closed())
      cerr << "The TopoDS_Solid is not closed!" << endl;
    else
      cout << "The TopoDS_Solid is closed." << endl;
    if (!wallSolid.Checked())
      cerr << "The TopoDS_Solid is not checked!" << endl;
    else
      cout << "The TopoDS_Solid has been checked." << endl;
    if (wallSolid.Infinite())
      cerr << "The TopoDS_Solid is infinite!" << endl;
    else
      cout << "The TopoDS_Solid is finite." << endl;
  }

  cout << "The result is a ";
  // Check to see if we have a solid
  switch (wallSolid.ShapeType()) {
  case (TopAbs_COMPOUND):
    cout << "TopAbs_COMPOUND" << endl;
    break;
  case (TopAbs_COMPSOLID):
    cout << "TopAbs_COMPSOLID" << endl;
    break;
  case (TopAbs_SOLID):
    cout << "TopAbs_SOLID" << endl;
    break;
  case (TopAbs_SHELL):
    cout << "TopAbs_SHELL" << endl;
    break;
  case (TopAbs_FACE):
    cout << "TopAbs_FACE" << endl;
    break;
  case (TopAbs_WIRE):
    cout << "TopAbs_WIRE" << endl;
    break;
  case (TopAbs_EDGE):
    cout << "TopAbs_EDGE" << endl;
    break;
  case (TopAbs_VERTEX):
    cout << "TopAbs_VERTEX" << endl;
    break;
  case (TopAbs_SHAPE):
    cout << "TopAbs_SHAPE" << endl;
  }

  return 0;
}

// Create a Draw Harness including the Viewer

void Draw_InitAppli(Draw_Interpretor& i) ;

void Draw_InitAppli(Draw_Interpretor& theCommands)
{
  // basic Harness Command  
  Draw::Commands(theCommands);

  // Geometrical and topological commands
  GeomliteTest::AllCommands(theCommands);
  GeometryTest::AllCommands(theCommands);
  BRepTest::AllCommands(theCommands);
  MeshTest::Commands(theCommands);
  CorrectTest::CorrectCommands(theCommands);

  // definition of Viewer Command
  ViewerTest::Commands(theCommands);
  //Viewer2dTest::Commands(theCommands);

  theCommands.Add("btube", "btube",__FILE__, btube, "btube wT d1 d2 R length");
}


#ifndef WIN32
// -------------------- Specific Unix ------------ //

int main(int argc, char** argv)
{
//  Draw_Appli(argc,argv);
  Draw_Appli(argc,argv,Draw_InitAppli);
  return 1;
}


#else
// ----------------- Specfic Windows  ----------- //

#include <Draw_Interpretor.hxx>

#include <sys/stat.h>
#include <fcntl.h>

#include <stdio.h>
#include <io.h>


int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevinstance, LPSTR IpCmdLine, int nCmdShow)
{
  // Redirection of the standard output
  
 
  char * p;
  Standard_Boolean CONSOLE = Standard_True;
  char p2[255];
  char logfile[255];

  strcpy(p2,IpCmdLine);
  p = strtok(p2," \t");

  while (p)
    {
      if (stricmp(p,"-o") == 0 ) // -o option
	{
	  p=strtok(NULL," \t");
	  if (p)
	    {
	      CONSOLE=Standard_False;
	      strcpy(logfile, p);
	      p=NULL;
	    }
	}
      else
	{
	  p=strtok(NULL," \t");
	}
    }
  
  int hCrt;
  BOOL rep;

  if (CONSOLE)
    {
      FILE *hf;
      _SYSTEM_INFO lps;
      GetSystemInfo(&lps);
      rep = AllocConsole();
      hCrt = _open_osfhandle(
			     (long) GetStdHandle(STD_OUTPUT_HANDLE),
			     _O_TEXT
			     );
      hf = _fdopen( hCrt, "w" );
      *stdout = *hf;
      // stop the buffer on stdout
      int i = setvbuf( stdout, NULL, _IONBF, 0 );
      filebuf ff(hCrt);
      cout = &ff;
      cout << "AISViewer Test Harness " << endl;
      
//POP  : Since Tcl8x We need to redefine cint
      int hCrt2;
      FILE *hf2;
      hCrt2 = _open_osfhandle((long) GetStdHandle(STD_INPUT_HANDLE),
			      O_TEXT);
      hf2 = _fdopen( hCrt2, "w" );
      *stdin = *hf2;
      int ii = setvbuf( stdin, NULL, _IONBF, 0 );
      filebuf ff2(hCrt2);
      cin =  &ff2 ;
// End cin redefintion
      
      Draw_Appli(hInstance, hPrevinstance, IpCmdLine, nCmdShow,Draw_InitAppli);
      hCrt = _fcloseall();
      rep = FreeConsole();
    } //CONSOLE
  else
    {
      FILE *hf;
      hCrt = _open(logfile, _O_TEXT | _O_CREAT | _O_RDWR, _S_IREAD | _S_IWRITE);
      SetStdHandle (  STD_OUTPUT_HANDLE, ( HANDLE )_get_osfhandle ( hCrt )  );
      
      hf = _fdopen(hCrt, "w");
      setvbuf( hf, NULL, _IONBF, 0 );
      *stdout = *hf;
      close ( 1 );
      dup ( hCrt );
      
      
      filebuf ff(hCrt);
      cout = &ff;
//POP  : Since Tcl8x We need to redefine cint
      int hCrt2;
      FILE *hf2;
      hCrt2 = _open_osfhandle((long) GetStdHandle(STD_INPUT_HANDLE),
			      O_TEXT);
      hf2 = _fdopen( hCrt2, "w" );
      *stdin = *hf2;
      int ii = setvbuf( stdin, NULL, _IONBF, 0 );
      filebuf ff2(hCrt2);
      cin =  &ff2 ;
// End cin redefintion
      
      Draw_Appli(hInstance, hPrevinstance, IpCmdLine, nCmdShow,Draw_InitAppli);
    }

  
  return 0;
}
//
// for TCl
//

Standard_Integer Tcl_AppInit (Tcl_Interp *)
{
  return 0;
}


#endif
