//
// ********************************************************************
// * License and Disclaimer                                           *
// *                                                                  *
// * The  Geant4 software  is  copyright of the Copyright Holders  of *
// * the Geant4 Collaboration.  It is provided  under  the terms  and *
// * conditions of the Geant4 Software License,  included in the file *
// * LICENSE and available at  http://cern.ch/geant4/license .  These *
// * include a list of copyright holders.                             *
// *                                                                  *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work  make  any representation or  warranty, express or implied, *
// * regarding  this  software system or assume any liability for its *
// * use.  Please see the license in the file  LICENSE  and URL above *
// * for the full disclaimer and the limitation of liability.         *
// *                                                                  *
// * This  code  implementation is the result of  the  scientific and *
// * technical work of the GEANT4 collaboration.                      *
// * By using,  copying,  modifying or  distributing the software (or *
// * any work based  on the software)  you  agree  to acknowledge its *
// * use  in  resulting  scientific  publications,  and indicate your *
// * acceptance of all terms of the Geant4 Software license.          *
// ********************************************************************
//
//
// $Id: G4OpenGLXmViewer.cc,v 1.24 2007/08/21 14:05:51 allison Exp $
// GEANT4 tag $Name:  $
//
// 
// Andrew Walkden  10th February 1997
// G4OpenGLXmViewer : Class derived from G4OpenGLXViewer, to provide
//                  (Motif) widget OpenGL functionality for GEANT4.

#ifdef G4VIS_BUILD_OPENGLXM_DRIVER

#include "globals.hh"

#include "G4OpenGLXmViewer.hh"

#include "G4VisExtent.hh"
#include "G4LogicalVolume.hh"
#include "G4VSolid.hh"
#include "G4Point3D.hh"
#include "G4Normal3D.hh"

#include "G4OpenGLXmSliderBar.hh"

#include "G4Xt.hh"
#include <X11/Shell.h>

#include <sstream>

void G4OpenGLXmViewer::ShowView () {

  G4Xt::getInstance () -> SecondaryLoop ();

}

void G4OpenGLXmViewer::GetXmConnection () {
  
  G4Xt* interactorManager = G4Xt::getInstance ();
  toplevel = (Widget)interactorManager->GetMainInteractor();
  app      = XtWidgetToApplicationContext(toplevel);

  if (!toplevel) {
    fViewId = -1;  // This flags an error.
    G4cerr << "G4OpenGLXmViewer::GetXmConnection unable to Initialize"
      " application context." << G4endl;
    return;
  }

  // Better to put this in an X11 resource file !!!
  std::ostringstream oss;
  oss <<
    "*glxarea*width: " << fVP.GetWindowSizeHintX() << "\n"
    "*glxarea*height: " << fVP.GetWindowSizeHintY() << "\n"
    /*
    // Tried this as a replacement for the above two lines, but
    // sub-windows (rotation, etc.) came same size!!
    "*geometry: " << fVP.GetXGeometryString() << "\n"
    */
    "*frame*x: 10\n"
    "*frame*y: 10\n"
    "*frame*topOffset: 10\n"
    "*frame*bottomOffset: 10\n"
    "*frame*rightOffset: 10\n"
    "*frame*leftOffset: 10\n"
    "*frame*shadowType: SHADOW_IN\n"
    "*frame*useColorObj: False\n"
    "*frame*primaryColorSetId: 3\n"
    "*frame*secondaryColorSetId: 3\n"
    "*menubar*useColorObj: False\n"
    "*menubar*primaryColorSetId: 3\n"
    "*menubar*secondaryColorSetId: 3\n"
    "*toplevel*useColorObj: False\n"
    "*toplevel*primaryColorSetId: 3\n"
    "*toplevel*secondaryColorSetId: 3\n";
  interactorManager->PutStringInResourceDatabase ((char*)oss.str().c_str());

  //  interactorManager->AddSecondaryLoopPostAction ((G4SecondaryLoopAction)G4OpenGLXmViewerSecondaryLoopPostAction);
  
  shell = XtAppCreateShell ((String)fName.data(),(String)fName.data(),topLevelShellWidgetClass,XtDisplay(toplevel),NULL,0); 
  interactorManager->AddShell (shell);

  dpy = XtDisplay (shell);

  if (!dpy) {
    fViewId = -1;  // This flags an error.
    G4cerr << "G4OpenGLXmViewer::GetXmConnection unable to connect to display."
	 << G4endl;
    return;
  }

  if (!glXQueryExtension (dpy, &errorBase, &eventBase)) {
    fViewId = -1;  // This flags an error.
    G4cerr << "G4OpenGLXmViewer::GetXmConnection. X Server has no GLX extension."
	 << G4endl;;
    return;
  }
}

void G4OpenGLXmViewer::CreateMainWindow () {

  bgnd = XWhitePixelOfScreen (XtScreen(shell));
  borcol = XBlackPixelOfScreen (XtScreen(shell));
  
  XtVaSetValues (shell, 
		 XtNvisual, vi -> visual, 
       		 XtNdepth, vi -> depth, 
       		 XtNcolormap, cmap, 
		 XtNborderColor, &borcol,
		 XtNbackground, &bgnd,
		 XmNtitle, fName.data(),
		 NULL);

  main_win = XtVaCreateManagedWidget ("main_win", 
				      xmMainWindowWidgetClass,
				      shell,
				      XtNvisual, vi -> visual, 
				      XtNdepth, vi -> depth, 
				      XtNcolormap, cmap, 
				      XtNborderColor, borcol,
				      XtNbackground, bgnd,
				      NULL);

  //*********Create a menu bar for the window********
  style_str = XmStringCreateLocalized ((char*)"Style");
  actions_str = XmStringCreateLocalized ((char*)"Actions");
  misc_str = XmStringCreateLocalized ((char*)"Miscellany");
  spec_str = XmStringCreateLocalized ((char*)"Special");

  menubar = XmVaCreateSimpleMenuBar (main_win,
				     (char*)"menubar",
				     XmVaCASCADEBUTTON, style_str, 'S',
				     XmVaCASCADEBUTTON, actions_str, 'A',
				     XmVaCASCADEBUTTON, misc_str, 'M',
				     XmVaCASCADEBUTTON, spec_str, 'p',
				     XtNvisual, vi -> visual, 
				     XtNdepth, vi -> depth, 
				     XtNcolormap, cmap, 
				     XtNborderColor, borcol,
				     XtNbackground, bgnd,
				     NULL);

  XmStringFree (style_str);
  XmStringFree (actions_str);
  XmStringFree (misc_str);
  XmStringFree (spec_str);

  G4cout << "Created menubar" << G4endl;


  //*********Create style pulldown menu on menubar*********
  rep_str = XmStringCreateLocalized ((char*)"Representation");
  draw_str = XmStringCreateLocalized ((char*)"Drawing");
  bgnd_str = XmStringCreateLocalized ((char*)"Background color");

  style_cascade = XmVaCreateSimplePulldownMenu
    (menubar,
     (char*)"style",
     0,
     NULL,
     XmVaCASCADEBUTTON, rep_str, 'R',
     XmVaCASCADEBUTTON, draw_str, 'D',
     XmVaCASCADEBUTTON, bgnd_str, 'B',
     XtNvisual, vi -> visual, 
     XtNdepth, vi -> depth, 
     XtNcolormap, cmap, 
     XtNborderColor, borcol,
     XtNbackground, bgnd,
     NULL);
  
  XmStringFree (rep_str);
  XmStringFree (draw_str);
  XmStringFree (bgnd_str);

  //  G4cout << "Created Style pulldown menu" << G4endl;

  //Add Representation pullright menu to style cascade...
  polyhedron_str = XmStringCreateLocalized ((char*)"Polyhedron");
  nurbs_str = XmStringCreateLocalized ((char*)"NURBS");

  rep_style_pullright = XmVaCreateSimplePulldownMenu 
    (style_cascade,
     (char*)"rep_style",
     0,
     rep_style_callback,
     XmVaRADIOBUTTON, polyhedron_str, 'P', NULL, NULL,
     XmVaRADIOBUTTON, nurbs_str, 'N', NULL, NULL,
     XmNradioBehavior, True, 
     XmNradioAlwaysOne, True, 
     XmNuserData, this, 
     XtNvisual, vi -> visual, 
     XtNdepth, vi -> depth, 
     XtNcolormap, cmap, 
     XtNborderColor, borcol,
     XtNbackground, bgnd,
     NULL);
  
  Widget special_widget;

  G4ViewParameters::RepStyle style;
  style = fVP.GetRepStyle();
  
  if (style == G4ViewParameters::polyhedron) {
    special_widget = XtNameToWidget(rep_style_pullright, "button_0");
    if(special_widget) {
      XtVaSetValues (special_widget, XmNset, True, NULL);
    }
  } else if (style == G4ViewParameters::nurbs) {
    special_widget = XtNameToWidget(rep_style_pullright, "button_1");
    if(special_widget) {
      XtVaSetValues (special_widget, XmNset, True, NULL);
    }
  } else {
    G4Exception("Invalid Representation style in G4OpenGLXmViewer::CreateContext");
  }
  XmStringFree (polyhedron_str);
  XmStringFree (nurbs_str);
  
  //  G4cout << "Created Representation pulldown menu" << G4endl;

  //Add Drawing pullright menu to style cascade...
  wireframe_str = XmStringCreateLocalized ((char*)"Wireframe");
  hlr_str = XmStringCreateLocalized ((char*)"Hidden line removal");
  hsr_str = XmStringCreateLocalized ((char*)"Hidden surface removal");
  hlhsr_str = XmStringCreateLocalized ((char*)"Hidden line and surface removal");

  drawing_style_pullright = XmVaCreateSimplePulldownMenu 
    (style_cascade,
     (char*)"drawing_style",
     1,
     drawing_style_callback,
     XmVaRADIOBUTTON, wireframe_str, 'W', NULL, NULL,
     XmVaRADIOBUTTON, hlr_str, 'L', NULL, NULL,
     XmVaRADIOBUTTON, hsr_str, 'S', NULL, NULL,
     XmVaRADIOBUTTON, hlhsr_str, 'H', NULL, NULL,
     XmNradioBehavior, True, 
     XmNradioAlwaysOne, True, 
     XmNuserData, this,
     XtNvisual, vi -> visual, 
     XtNdepth, vi -> depth, 
     XtNcolormap, cmap, 
     XtNborderColor, borcol,
     XtNbackground, bgnd,
     NULL);
  
  G4ViewParameters::DrawingStyle d_style;
  d_style = fVP.GetDrawingStyle();
  
  if (d_style == G4ViewParameters::wireframe) {
    special_widget = XtNameToWidget(drawing_style_pullright, "button_0");
    if(special_widget) {
      XtVaSetValues (special_widget, XmNset, True, NULL);
    }
  } else if (d_style == G4ViewParameters::hlr) {
    special_widget = XtNameToWidget(drawing_style_pullright, "button_1");
    if(special_widget) {
      XtVaSetValues (special_widget, XmNset, True, NULL);
    }
  } else if (d_style == G4ViewParameters::hsr) {
    special_widget = XtNameToWidget(drawing_style_pullright, "button_2");
    if(special_widget) {
      XtVaSetValues (special_widget, XmNset, True, NULL);
    }
  } else if (d_style == G4ViewParameters::hlhsr) {
    special_widget = XtNameToWidget(drawing_style_pullright, "button_3");
    if(special_widget) {
      XtVaSetValues (special_widget, XmNset, True, NULL);
    }
  } else {
    G4Exception("Invalid Drawing style in G4OpenGLXmViewer::CreateContext");
  }

  XmStringFree (wireframe_str);
  XmStringFree (hlr_str);
  XmStringFree (hsr_str);
  XmStringFree (hlhsr_str);

  //  G4cout << "Created Drawing pullright menu" << G4endl;

  //Add Drawing pullright menu to style cascade...
  white_str = XmStringCreateLocalized ((char*)"White");
  black_str = XmStringCreateLocalized ((char*)"Black");

  background_color_pullright = XmVaCreateSimplePulldownMenu 
    (style_cascade,
     (char*)"background_color",
     2,
     background_color_callback,
     XmVaRADIOBUTTON, white_str, 'W', NULL, NULL,
     XmVaRADIOBUTTON, black_str, 'B', NULL, NULL,
     XmNradioBehavior, True, 
     XmNradioAlwaysOne, True, 
     XmNuserData, this,
     XtNvisual, vi -> visual, 
     XtNdepth, vi -> depth, 
     XtNcolormap, cmap, 
     XtNborderColor, borcol,
     XtNbackground, bgnd,
     NULL);
  
  if (background.GetRed() == 1. &&
      background.GetGreen() == 1. &&
      background.GetBlue() == 1.) {
    special_widget = XtNameToWidget(background_color_pullright, "button_0");
    if(special_widget) {
      XtVaSetValues (special_widget, XmNset, True, NULL);
    }
  } else {
    special_widget = XtNameToWidget(background_color_pullright, "button_1");
    if(special_widget) {
      XtVaSetValues (special_widget, XmNset, True, NULL);
    }
  }

  XmStringFree (white_str);
  XmStringFree (black_str);

  //  G4cout << "Created Background color pullright menu" << G4endl;

  //*********Create actions pulldown menu on menubar*********
  rot_str = XmStringCreateLocalized ((char*)"Rotation control panel");
  pan_str = XmStringCreateLocalized ((char*)"Panning control panel");
  set_str = XmStringCreateLocalized ((char*)"Set control panel limits");

  actions_cascade = XmVaCreateSimplePulldownMenu
    (menubar,
     (char*)"actions",
     1,
     actions_callback,
     XmVaPUSHBUTTON, rot_str, 'R', NULL, NULL,
     XmVaPUSHBUTTON, pan_str, 'P', NULL, NULL,
     XmVaPUSHBUTTON, set_str, 'S', NULL, NULL,
     XmNuserData, this, 
     XtNvisual, vi -> visual, 
     XtNdepth, vi -> depth, 
     XtNcolormap, cmap, 
     XtNborderColor, borcol,
     XtNbackground, bgnd,
     NULL);
  
  XmStringFree (rot_str);
  XmStringFree (pan_str);
  XmStringFree (set_str);
  G4cout << "Created Actions pulldown menu" << G4endl;

  misc_str = XmStringCreateLocalized ((char*)"Miscellany control panel");
  exit_str = XmStringCreateLocalized ((char*)"Exit to G4Vis>");
  print_str = XmStringCreateLocalized ((char*)"Create .eps file");

  //*********Create miscellany pulldown menu on menubar*********
  misc_cascade = XmVaCreateSimplePulldownMenu
    (menubar,
     (char*)"miscellany",
     2,
     misc_callback,
     XmVaPUSHBUTTON, misc_str, 'M', NULL, NULL,
     XmVaPUSHBUTTON, exit_str, 'E', NULL, NULL,
     XmVaPUSHBUTTON, print_str, 'P', NULL, NULL,
     XmNuserData, this,
     XtNvisual, vi -> visual, 
     XtNdepth, vi -> depth, 
     XtNcolormap, cmap, 
     XtNborderColor, borcol,
     XtNbackground, bgnd,
     NULL);
  
  XmStringFree (misc_str);
  XmStringFree (exit_str);
  XmStringFree (print_str);
  G4cout << "Created Miscellany pulldown menu" << G4endl;

  trans_str = XmStringCreateLocalized ((char*)"Transparency");
  anti_str = XmStringCreateLocalized ((char*)"Antialiasing");
  halo_str = XmStringCreateLocalized ((char*)"Haloing");
  aux_edge_str = XmStringCreateLocalized ((char*)"Auxiliary edges");

  //*********Create special pulldown menu on menubar*********
  spec_cascade = XmVaCreateSimplePulldownMenu
    (menubar,
     (char*)"special",
     3,
     NULL,
     XmVaCASCADEBUTTON, trans_str, 'T',
     XmVaCASCADEBUTTON, anti_str, 'A',
     XmVaCASCADEBUTTON, halo_str, 'H',
     XmVaCASCADEBUTTON, aux_edge_str, 'E',
     XtNvisual, vi -> visual, 
     XtNdepth, vi -> depth, 
     XtNcolormap, cmap, 
     XtNborderColor, borcol,
     XtNbackground, bgnd,
     NULL);
  
  XmStringFree (trans_str);
  XmStringFree (anti_str);
  XmStringFree (halo_str);
  XmStringFree (aux_edge_str);

  //  G4cout << "Created Special pulldown menu" << G4endl;

  //Add Transparency pullright menu to special cascade...
  off_str = XmStringCreateLocalized ((char*)"Off");
  on_str = XmStringCreateLocalized ((char*)"On");

  transparency_pullright = XmVaCreateSimplePulldownMenu 
    (spec_cascade,
     (char*)"transparency",
     0,
     transparency_callback,
     XmVaRADIOBUTTON, off_str, 'f', NULL, NULL,
     XmVaRADIOBUTTON, on_str, 'n', NULL, NULL,
     XmNradioBehavior, True, 
     XmNradioAlwaysOne, True, 
     XmNuserData, this,
     XtNvisual, vi -> visual, 
     XtNdepth, vi -> depth, 
     XtNcolormap, cmap, 
     XtNborderColor, borcol,
     XtNbackground, bgnd,
     NULL);
  
  if (transparency_enabled == false) {
    special_widget = XtNameToWidget(transparency_pullright, "button_0");
    if(special_widget) {
      XtVaSetValues (special_widget, XmNset, True, NULL);
    }
  } else if (transparency_enabled == true) {
    special_widget = XtNameToWidget(transparency_pullright, "button_1");
    if(special_widget) {
      XtVaSetValues (special_widget, XmNset, True, NULL);
    }
  } else {
    G4Exception("transparency_enabled in G4OpenGLXmViewer is neither true nor false!!");
  }

  //Add antialias pullright menu to special cascade...
  antialias_pullright = XmVaCreateSimplePulldownMenu 
    (spec_cascade,
     (char*)"antialias",
     1,
     antialias_callback,
     XmVaRADIOBUTTON, off_str, 'f', NULL, NULL,
     XmVaRADIOBUTTON, on_str, 'n', NULL, NULL,
     XmNradioBehavior, True, 
     XmNradioAlwaysOne, True, 
     XmNuserData, this,
     XtNvisual, vi -> visual, 
     XtNdepth, vi -> depth, 
     XtNcolormap, cmap, 
     XtNborderColor, borcol,
     XtNbackground, bgnd,
     NULL);
  
  if (antialiasing_enabled == false) {
    special_widget = XtNameToWidget(antialias_pullright, "button_0");
    if(special_widget) {
      XtVaSetValues (special_widget, XmNset, True, NULL);
    }
  } else if (antialiasing_enabled == true) {
    special_widget = XtNameToWidget(antialias_pullright, "button_1");
    if(special_widget) {
      XtVaSetValues (special_widget, XmNset, True, NULL);
    }
  } else {
    G4Exception("antialiasing_enabled in G4OpenGLXmViewer is neither true nor false!!");
  }

  //Add Haloing pullright menu to special cascade...
  haloing_pullright = XmVaCreateSimplePulldownMenu 
    (spec_cascade,
     (char*)"haloing",
     2,
     haloing_callback,
     XmVaRADIOBUTTON, off_str, 'f', NULL, NULL,
     XmVaRADIOBUTTON, on_str, 'n', NULL, NULL,
     XmNradioBehavior, True, 
     XmNradioAlwaysOne, True, 
     XmNuserData, this,
     XtNvisual, vi -> visual, 
     XtNdepth, vi -> depth, 
     XtNcolormap, cmap, 
     XtNborderColor, borcol,
     XtNbackground, bgnd,
     NULL);
  
  if (haloing_enabled == false) {
    special_widget = XtNameToWidget(haloing_pullright, "button_0");
    if(special_widget) {
      XtVaSetValues (special_widget, XmNset, True, NULL);
    }
  } else if (haloing_enabled == true) {
    special_widget = XtNameToWidget(haloing_pullright, "button_1");
    if(special_widget) {
      XtVaSetValues (special_widget, XmNset, True, NULL);
    }
  } else {
    G4Exception("haloing_enabled in G4OpenGLXmViewer is neither true nor false!!");
  }

  //Add Aux_Edge pullright menu to special cascade...
  aux_edge_pullright = XmVaCreateSimplePulldownMenu 
    (spec_cascade,
     (char*)"aux_edge",
     3,
     aux_edge_callback,
     XmVaRADIOBUTTON, off_str, 'f', NULL, NULL,
     XmVaRADIOBUTTON, on_str, 'n', NULL, NULL,
     XmNradioBehavior, True, 
     XmNradioAlwaysOne, True, 
     XmNuserData, this,
     XtNvisual, vi -> visual, 
     XtNdepth, vi -> depth, 
     XtNcolormap, cmap, 
     XtNborderColor, borcol,
     XtNbackground, bgnd,
     NULL);
  
  if (!fVP.IsAuxEdgeVisible()) {
    special_widget = XtNameToWidget(aux_edge_pullright, "button_0");
    if(special_widget) {
      XtVaSetValues (special_widget, XmNset, True, NULL);
    }
  } else {
    special_widget = XtNameToWidget(aux_edge_pullright, "button_1");
    if(special_widget) {
      XtVaSetValues (special_widget, XmNset, True, NULL);
    }
  }

  XtManageChild (menubar);
  frame = XtVaCreateManagedWidget ((char*)"frame",
				   xmFrameWidgetClass, main_win,
				   XtNvisual, vi -> visual, 
				   XtNdepth, vi -> depth, 
				   XtNcolormap, cmap, 
				   XtNborderColor, borcol,
				   XtNbackground, bgnd,
				   NULL);

  glxarea = XtVaCreateManagedWidget ((char*)"glxarea", 
				     xmDrawingAreaWidgetClass,
				     frame,
				     XtNvisual, vi -> visual, 
				     XtNdepth, vi -> depth, 
				     XtNcolormap, cmap, 
				     XtNborderColor, borcol,
				     XtNbackground, bgnd,
				     NULL);
  
  XtAddCallback (glxarea, 
		 XmNexposeCallback, 
		 expose_callback, 
		 this);

  XtAddCallback (glxarea, 
		 XmNresizeCallback, 
		 resize_callback, 
		 this);

  XmMainWindowSetAreas (main_win,  // main widget, children are specified 
			menubar,   // widget to use as menu bar
			NULL,      // widget to use as command window
			NULL,      // widget for horizontal scroll bar
			NULL,      // widget for vertical scroll bar
			frame      // widget to be used for work window
			);

  XtRealizeWidget(shell);
  
  // Once widget is realized (ie, associated with a created X window), we
  // can bind the OpenGL rendering context to the window.

  Dimension width, height;
  XtVaGetValues (glxarea,XmNwidth,&width,XmNheight,&height,NULL);
  WinSize_x = (G4int) width;
  WinSize_y = (G4int) height;

  win = XtWindow (glxarea);

  glXMakeCurrent (dpy, win, cx);

}

G4OpenGLXmViewer::G4OpenGLXmViewer (G4OpenGLSceneHandler& scene):
G4VViewer (scene, -1),
G4OpenGLViewer (scene),
G4OpenGLXViewer (scene),
zoom_high (fVP.GetZoomFactor() * 10.0),
zoom_low (fVP.GetZoomFactor() / 10.0),
dolly_low (fVP.GetDolly() - 1000.0),
dolly_high (fVP.GetDolly() + 1000.0),
fov (0.0),
rot_sens_limit (90.),
pan_sens_limit (100.),
rot_sens (1.),
wob_sens (20.),
original_vp(fVP.GetViewpointDirection()),
frameNo (0),
fprotation_top (0),
fprotation_slider (0),
fppanning_top (0),
fppanning_slider (0),
fpzoom_slider (0),
fpdolly_slider (0),
fpsetting_top (0),
fpmiscellany_top (0),
fpprint_top (0)
{
  WinSize_x = 100;
  WinSize_y = 100;
  
  GetXmConnection ();
  if (fViewId < 0) return;
}

G4OpenGLXmViewer::~G4OpenGLXmViewer ()
{
  XtDestroyWidget  (shell);
  win = 0; // ...to avoid XDestroyWindow in G4OpenGLXViewer base class
  // because XtDestroyWidget has already destroyed it.
  G4Xt::getInstance () ->RemoveShell (shell);

/******************************
  if (fprotation_top) {
    delete fprotation_top;
  }

  if (fppanning_top) {
    delete fppanning_top;
  }

  if (fpsetting_top) {
    delete fpsetting_top;
  }

  if (fpmiscellany_top) {
    delete fpmiscellany_top;
  }
******************************/

}

#endif
