// This code implementation is the intellectual property of
// the GEANT4 collaboration.
//
// By copying, distributing or modifying the Program (or any work
// based on the Program) you indicate your acceptance of this statement,
// and all its terms.
//
// $Id: G4VRML1SceneHandlerFunc.icc,v 1.5 1999/12/27 07:49:22 stanaka Exp $
// GEANT4 tag $Name: geant4-02-00 $
//
// G4VRML1SceneHandlerFunc.icc
// Satoshi Tanaka & Yasuhide Sawada

//#define DEBUG_SCENE_FUNC

// MACRO
#define ADDTHIS_WITH_NAME(Solid)	\
	fCurrentDEF = #Solid "_" + Solid.GetName(); \
	RequestPrimitives(Solid); \
	fCurrentDEF = "";

// End of MACRO

void G4VRML1SCENE::AddThis(const G4Trd& trd)
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** AddThis trd" << "\n" ;
#endif
	VRMLBeginModeling () ;
	ADDTHIS_WITH_NAME(trd)
}

void G4VRML1SCENE::AddThis(const G4Trap& trap)
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** AddThis trap" << "\n" ;
#endif
	VRMLBeginModeling () ;
	ADDTHIS_WITH_NAME(trap)
}

void G4VRML1SCENE::AddThis(const G4Para& para)
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** AddThis para" << "\n" ;
#endif
	VRMLBeginModeling () ;
	ADDTHIS_WITH_NAME(para)
}

void G4VRML1SCENE::AddThis(const G4Torus& torus )
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** AddThis torus" << "\n" ;
#endif
	VRMLBeginModeling () ;
	ADDTHIS_WITH_NAME(torus)
}


void G4VRML1SCENE::AddThis(const G4VSolid& vsolid)
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** AddThis vsolid" << "\n" ;
#endif
	VRMLBeginModeling () ;
	ADDTHIS_WITH_NAME(vsolid)
}

void G4VRML1SCENE::AddThis(const G4Tubs& tubs)
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** AddThis tubs" << "\n" ;
#endif
	VRMLBeginModeling () ;

	// set current name
	fCurrentDEF = "tubs_" + tubs.GetName();

	// Get data
	const G4double R  = tubs.GetRMax(); // outside radius
	const G4double r  = tubs.GetRMin(); // inside radius
	const G4double dz = tubs.GetDz();   // half length in z
	const G4double dp = tubs.GetDPhi(); // angle interval

	// Send data
	if ( r == 0.0 && dp >= 360. * deg ) {
		// Send a built-in VRML node (Cylinder)
		fDest << "Separator {" << "\n";
		SendMatrixTransformNode( fpObjectTransformation );

		fDest << "\t" << "DEF " << fCurrentDEF << " Separator {" << "\n";
		SendMaterialNode();
		SendCylinderNode(R, dz * 2);
		fDest << "\t" << "}" << "\n"; // DEF Separator
		fDest << "}" << "\n"; // Separator
	} else {
		// call AddPrimitive(Polyhedron)
		RequestPrimitives(tubs); 
	}

	// reset current name to null
	fCurrentDEF = "";
}


void G4VRML1SCENE::AddThis(const G4Cons& cons)
{
#if defined DEBUG_SCENE_FUNC
    G4cerr << "***** AddThis cons" << "\n" ;
#endif
	VRMLBeginModeling () ;

	// set current name
	fCurrentDEF = "cons_" + cons.GetName();

	// Get data
	const G4double r1 = cons.GetRmin1(); // inside radius  at -dz
	const G4double R1 = cons.GetRmax1(); // outside radius at -dz
	const G4double r2 = cons.GetRmin2(); // inside radius  at +dz
	const G4double R2 = cons.GetRmax2(); // outside radius at +dz
	const G4double dz = cons.GetDz();    // half length in z
	//const G4double sp = cons.GetSPhi();  // starting angle
	const G4double dp = cons.GetDPhi();  // angle width

	// Send data
	if ( r1 == 0.0 && r2 == 0.0 && R1 == R2 && dp >= 360. * deg) {
		// Send a built-in VRML node (Cylinder)
		fDest << "Separator {" << "\n";
		SendMatrixTransformNode( fpObjectTransformation );

		fDest << "\t" << "DEF " << fCurrentDEF << " Separator {" << "\n";
		SendMaterialNode();
		SendCylinderNode(R1, dz * 2);
		fDest << "\t" << "}" << "\n"; // DEF Separator
		fDest << "}" << "\n"; // Separator
	} else {
		// call AddPrimitive(Polyhedron)
		RequestPrimitives(cons); 
	}

	// reset current name to null
	fCurrentDEF = "";
}

void G4VRML1SCENE::AddThis(const G4Box& box)
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** AddThis box" << "\n" ;
#endif
	VRMLBeginModeling () ;

	// set current name
	fCurrentDEF = "box_" + box.GetName();

	// Send a built-in VRML node (Cube)
	fDest << "Separator {" << "\n";

	//fDest << "\t" << "renderCulling ON" << "\n";
	SendMatrixTransformNode( fpObjectTransformation );

	fDest << "\t" << "DEF " << fCurrentDEF << " Separator {" << "\n";
	SendMaterialNode();
	SendCubeNode(box.GetXHalfLength() * 2, box.GetYHalfLength() * 2, box.GetZHalfLength() * 2);
	fDest << "\t" << "}" << "\n"; // DEF Separator

	fDest << "}" << "\n"; // Separator

	// reset current name to null
	fCurrentDEF = "";
}


void G4VRML1SCENE::AddThis(const G4Sphere& sphere)
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** AddThis sphere" << "\n" ;
#endif
	VRMLBeginModeling () ;

	// set current name
	fCurrentDEF = "sphere_" + sphere.GetName();

	// Get data
	G4double  dphi   = sphere.GetDPhi  () ;
	G4double  dtheta = sphere.GetDTheta() ;
	G4double  rmax   = sphere.GetRmax  () ;
	G4double  rmin   = sphere.GetRmin  () ;

	// Send data
	if ( (dphi >= 360. * deg) && (dtheta >= 180. * deg) && (rmin == 0.0)  ) {
		// Send a built-in VRML node (Sphere)
		fDest << "Separator {" << "\n";
		SendMatrixTransformNode( fpObjectTransformation );

		fDest << "\t" << "DEF " << fCurrentDEF << " Separator {" << "\n";
		SendMaterialNode();
		SendSphereNode( rmax );
		fDest << "\t" << "}" << "\n"; // DEF Separator
		fDest << "}" << "\n"; // Separator
	} else {
			// call AddPrimitive(Polyhedron)
			RequestPrimitives( sphere ); 
	}

	// reset current name to null
	fCurrentDEF = "";
}

void G4VRML1SCENE::AddPrimitive(const G4Polyline& polyline)
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** AddPrimitive polyline" << "\n" ;
#endif 
	VRMLBeginModeling () ;	

	fDest << "Separator {" << "\n";

	SendMatrixTransformNode (fpObjectTransformation );

	SendMaterialNode( polyline.GetVisAttributes() );

	fDest << "\t" << "Coordinate3 {" << "\n";
	fDest << "\t\t" << "point [" << "\n";
	G4int e, i;
	for (i = 0, e = polyline.entries(); e; i++, e--) {
		fDest << "\t\t\t";
		fDest << polyline(i).x() << " ";
		fDest << polyline(i).y() << " ";
		fDest << polyline(i).z() << "," << "\n";
	}
	fDest << "\t\t" << "]" << "\n";
	fDest << "\t" << "}" << "\n"; // Coordinate3

	fDest << "\t" << "IndexedLineSet {" << "\n";
	fDest << "\t\t" << "coordIndex [";
	for (i = 0, e = polyline.entries(); e; i++, e--) {
		if (i % 10 == 0)
			fDest << "\n" << "\t\t\t";
		fDest << i << ", ";
	}
	fDest << "-1" << "\n";
	fDest << "\t\t" << "]" << "\n";
	fDest << "\t" << "}" << "\n"; // IndexedLineSet

	fDest << "}" << "\n"; // Separator
}

void G4VRML1SCENE::AddPrimitive(const G4Polyhedron& polyhedron)
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** AddPrimitive(G4Polyhedron)" << "\n";
#endif
	VRMLBeginModeling () ;

	fDest << "Separator {" << "\n";

	SendMatrixTransformNode( fpObjectTransformation );

	fDest << "\t";
	if (fCurrentDEF != "")
		fDest << "DEF " << fCurrentDEF << " ";

	fDest << "Separator {" << "\n";

	//fDest << "\t\t" << "renderCulling ON" << "\n";

	fDest << "\t\t" << "ShapeHints {" << "\n";
	fDest << "\t\t\t" << "vertexOrdering COUNTERCLOCKWISE" << "\n";
	fDest << "\t\t\t" << "shapeType SOLID" << "\n";
	fDest << "\t\t\t" << "faceType CONVEX" << "\n";
	fDest << "\t\t" << "}" << "\n";

	SendMaterialNode();

	fDest << "\t\t" << "Coordinate3 {" << "\n";
	fDest << "\t\t\t" << "point [" << "\n";
	G4int i, j;
	for (i = 1, j = polyhedron.GetNoVertices(); j; j--, i++) {
		G4Point3D point = polyhedron.GetVertex(i);
		fDest << "\t\t\t\t";
		fDest << point.x() << " ";
		fDest << point.y() << " ";
		fDest << point.z() << "," << "\n";
	}
	fDest << "\t\t\t" << "]" << "\n";
	fDest << "\t\t" << "}" << "\n"; // Coordinate3

	fDest << "\t\t" << "IndexedFaceSet {" << "\n";
	fDest << "\t\t\t" << "coordIndex [" << "\n";

	// facet loop
	G4int f;
	for (f = polyhedron.GetNoFacets(); f; f--) {

		// edge loop  
		G4bool notLastEdge;
		G4int index = -1, edgeFlag = 1;
		fDest << "\t\t\t\t";
		do {
			notLastEdge = polyhedron.GetNextVertexIndex(index, edgeFlag);
			fDest << index - 1 << ", ";
		} while (notLastEdge);
		fDest << "-1," << "\n";
	}
	fDest << "\t\t\t" << "]" << "\n";
	fDest << "\t\t" << "}" << "\n"; // IndexFaceSet

	fDest << "\t" << "}" << "\n"; // (DEF) Separator
	fDest << "}" << "\n"; // Separator

}


void G4VRML1SCENE::AddPrimitive(const G4NURBS& nurb)
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** AddPrimitive nurbs" << "\n" ;
#endif 
	G4cerr << "G4VRML1File::AddPrimitive(G4NURBS&): not implemented. " << "\n" ;
	VRMLBeginModeling () ;

}

void G4VRML1SCENE::AddPrimitive( const G4Text& text ) 
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** AddPrimitive text" << "\n" ;
#endif
	G4cerr <<
          "***** void G4VRML1SCENE::AddPrimitive( const G4Text& text )"
          " not implemented yet."
             << "\n";

	VRMLBeginModeling () ;
}

void G4VRML1SCENE::AddPrimitive( const G4Circle& circle ) 
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** AddPrimitive circle" << "\n" ;
#endif

	VRMLBeginModeling () ;

	// begin sending a mark
	fDest << "Separator {" << "\n";

	// send color 
	SendMarkerColor    ( circle ) ;

	// position 
	SendMarkerWorldPosition ( circle ) ;

	// Calc size 
	G4double size = GetMarkerHalfSize ( circle );

	// send shape with size 
	fDest << "\t" << "Sphere {" << "\n";
	fDest << "\t\t" << "radius " << size << "\n";
	fDest << "\t" << "}" << "\n";

	// end sending a mark
	fDest << "}" << "\n"; // Separator
}


void G4VRML1SCENE::AddPrimitive(const G4Square& square)
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** AddPrimitive square" << "\n" ;
#endif

	VRMLBeginModeling () ;

	// begin sending a mark
	fDest << "Separator {" << "\n";

	// send color 
	SendMarkerColor    ( square );

	// position 
	SendMarkerWorldPosition ( square );

	// Calc size 
	G4double size = GetMarkerHalfSize ( square );
	size *= 2.;

	// send shape with size 
	fDest << "\t" << "Cube {" << "\n";
	fDest << "\t\t" << "width "  << size << "\n";
	fDest << "\t\t" << "height " << size << "\n";
	fDest << "\t\t" << "depth "  << size << "\n";
	fDest << "\t" << "}" << "\n";

	// end sending a mark
	fDest << "}" << "\n"; // Separator
}

void G4VRML1SCENE::ClearStore()
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** ClearStore(): No effects" << "\n" ;
#endif 
}

void G4VRML1SCENE::BeginModeling()
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** BeginModeling " << "\n" ;
#endif
	G4VSceneHandler::BeginModeling();
}

void G4VRML1SCENE::EndModeling()
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** EndModeling " << "\n" ;
#endif
	G4VSceneHandler::EndModeling();
}

void G4VRML1SCENE::BeginPrimitives(const G4Transform3D& objectTransformation)
{
  G4VSceneHandler::BeginPrimitives (objectTransformation);
  fpObjectTransformation = &objectTransformation;
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** BeginPrimitives " << "\n" ;
#endif
	VRMLBeginModeling();
}

void G4VRML1SCENE::EndPrimitives()
{
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** EndPrimitives " << "\n" ;
#endif
  G4VSceneHandler::EndPrimitives();
}

void G4VRML1SCENE::SendMaterialNode( const G4VisAttributes*  pVA )
{
//	const double TRANSPARENCY = 0.9 ; 
	const double TRANSPARENCY = 0.5 ; 

	if (!pVA)
		return;

	const G4Color& color = pVA->GetColor();

	fDest << "\t\t" << "Material {" << "\n";

	if (pVA->IsForceDrawingStyle() &&
	   (pVA->GetForcedDrawingStyle() == G4VisAttributes::wireframe)) {
		fDest << "\t\t\t" << "transparency " << TRANSPARENCY << "\n";
	}

	fDest << "\t\t\t" << "diffuseColor";
	fDest << " " << color.GetRed();
	fDest << " " << color.GetGreen();
	fDest << " " << color.GetBlue();
	fDest << "\n";
	fDest << "\t\t" << "}" << "\n";
}


void G4VRML1SCENE::SendMaterialNode()
{
	SendMaterialNode
	( fpViewer->GetApplicableVisAttributes (fpVisAttribs) );
}


void G4VRML1SCENE::SendMatrixTransformNode(const G4Transform3D *trans)
{
	G4Point3D  B  (0.0, 0.0, 0.0);

	G4Point3D  x1 (1.0, 0.0, 0.0);
	G4Point3D  y1 (0.0, 1.0, 0.0);

	G4Vector3D e1 (1.0, 0.0, 0.0);
	G4Vector3D e2 (0.0, 1.0, 0.0);
	G4Vector3D e3 (0.0, 0.0, 1.0);

		//----- transformed origin of body coord
	B.transform(*trans);

		//----- transformed base vectors of body coord
	x1.transform(*trans);
	e1 = x1 - B;

	y1.transform(*trans);
 	e2 = y1 - B;

	e3 = e1.cross(e2);

	e1 = e1.unit(); // normalize again for accuracy
	e2 = e2.unit(); //
	e3 = e3.unit(); //

	fDest << "\t" << "MatrixTransform {" << "\n";
	fDest << "\t\t" << "matrix ";
	fDest << e1.x() << " " << e1.y() << " " << e1.z() << " 0 ";
	fDest << e2.x() << " " << e2.y() << " " << e2.z() << " 0 ";
	fDest << e3.x() << " " << e3.y() << " " << e3.z() << " 0 ";
	fDest << B.x()  << " " << B.y()  << " " << B.z()  << " 1" << "\n";
	fDest << "\t" << "}" << "\n";
}

void G4VRML1SCENE::SendCubeNode(G4double w, G4double h, G4double d)
{
	fDest << "\t\t" << "Cube {" << "\n";
	fDest << "\t\t\t" << "width "  << w << "\n";
	fDest << "\t\t\t" << "height " << h << "\n";
	fDest << "\t\t\t" << "depth "  << d << "\n";
	fDest << "\t\t" << "}" << "\n";
}

void G4VRML1SCENE::SendCylinderNode(G4double R, G4double h)
{
	fDest << "\t\t" << "Transform {" << "\n";
	fDest << "\t\t\t" << "rotation 1 0 0 " << (90. * deg) << "\n";
	fDest << "\t\t" << "}" << "\n";

	fDest << "\t\t" << "Cylinder {" << "\n";
	fDest << "\t\t\t" << "radius " << R << "\n";
	fDest << "\t\t\t" << "height " << h << "\n";
	fDest << "\t\t" << "}" << "\n";
}

void G4VRML1SCENE::SendSphereNode(G4double R)
{
	fDest << "\t\t" << "Sphere {" << "\n";
	fDest << "\t\t\t" << "radius " << R << "\n";
	fDest << "\t\t" << "}" << "\n";
}

void G4VRML1SCENE::VRMLBeginModeling()
{
	if (!IS_CONNECTED ) {
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** VRMLBeginModeling() (started)" << "\n" ;
#endif
		this->connectPort();
		fDest << "#VRML V1.0 ascii" << "\n";
		fDest << "# Generated by VRML 1.0 driver of GEANT4\n" << "\n";
	}
}

void G4VRML1SCENE::VRMLEndModeling()
{
	if ( IS_CONNECTED ) {
#if defined DEBUG_SCENE_FUNC
	G4cerr << "***** VRMLEndModeling() (started)" << "\n" ;
#endif

		fDest << "#End of file." << "\n";
		this->closePort();
	}
}

void      G4VRML1SCENE::SendMarkerColor ( const G4VMarker& mark ) 
{
	const G4Color& color = GetColor( mark );

	fDest << "\t" << "Material {" << "\n";
	fDest << "\t\t";
	fDest << "ambientColor [] ";
	fDest << "specularColor [] ";
	fDest << "\t\t" << "diffuseColor";
	fDest << " " << color.GetRed();
	fDest << " " << color.GetGreen();
	fDest << " " << color.GetBlue();
	fDest << "\n";
	fDest << "\t\t" << "emissiveColor";
	fDest << " " << color.GetRed();
	fDest << " " << color.GetGreen();
	fDest << " " << color.GetBlue();
	fDest << "\n";
	fDest << "\t" << "}" << "\n";
}

void      
G4VRML1SCENE::SendMarkerWorldPosition ( const G4VMarker& mark )
{
	G4Point3D point = mark.GetPosition();

	SendMatrixTransformNode( fpObjectTransformation );

	fDest << "\t\t" << "Transform {" << "\n";
	fDest << "\t\t\t" << "translation ";
	fDest << point.x() << " " << point.y() << " " << point.z() << "\n";
	fDest << "\t\t" << "}" << "\n" ;
}


G4double  G4VRML1SCENE::GetMarkerHalfSize ( const G4VMarker& mark ) 
{
	//----- return value ( marker radius in 3d units) 
	G4double size       = 1.0 ; // initialization

	//----- parameters to calculate 3d size from 2d size
	const double HALF_SCREEN_SIZE_2D = 300.0 ; // pixels
	double zoom_factor  = fpViewer->GetViewParameters().GetZoomFactor() ;
	if ( zoom_factor <=  0.0 ) { zoom_factor = 1.0 ; }
	double extent_radius_3d = GetScene()->GetExtent().GetExtentRadius() ;
	if ( extent_radius_3d <= 0.0 ) { extent_radius_3d = 1.0 ; } 

	//----- get marker radius in 3D units
	if        ( size = mark.GetWorldSize()  ) {

		// get mark radius in 3D units
		size = 0.5 * mark.GetWorldSize()  ; 

	} else if ( size = mark.GetScreenSize() ) {

		// local
		double mark_radius_2d   = 0.5 * mark.GetScreenSize() ;

		// get mark radius in 3D units
		size \
		 = extent_radius_3d * ( mark_radius_2d / HALF_SCREEN_SIZE_2D );
		size *= zoom_factor ;

	} else {
		// local
		double mark_radius_2d \
		 = fpViewer->GetViewParameters().GetDefaultMarker().GetScreenSize();

		// get mark radius in 3D units
		size \
		 = extent_radius_3d * ( mark_radius_2d / HALF_SCREEN_SIZE_2D );
		size *= zoom_factor ;
	}

		//----- global rescaling
	size *= fpViewer->GetViewParameters().GetGlobalMarkerScale(); 

		//----- return size
	return size ;

} 


// #undef ADDTHIS_WITH_NAME(Solid)
// #undef MAKE_NAME(Solid)
// End of file.
