//
// ********************************************************************
// * 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.          *
// ********************************************************************
//
//
// 
// GEANT4 Visualization Manager - John Allison 02/Jan/1996.
// Michael Kelsey  31 Jan 2019 -- Add new command for electric field

#include "G4VisManager.hh"

#include "G4VisCommands.hh"
#include "G4VisCommandsCompound.hh"
#include "G4VisCommandsGeometry.hh"
#include "G4VisCommandsGeometrySet.hh"
#include "G4VisCommandsMultithreading.hh"
#include "G4VisCommandsSet.hh"
#include "G4VisCommandsScene.hh"
#include "G4VisCommandsSceneAdd.hh"
#include "G4VisCommandsPlotter.hh"
#include "G4VisCommandsSceneHandler.hh"
#include "G4VisCommandsTouchable.hh"
#include "G4VisCommandsTouchableSet.hh"
#include "G4VisCommandsViewer.hh"
#include "G4VisCommandsViewerDefault.hh"
#include "G4VisCommandsViewerSet.hh"
#include "G4UImanager.hh"
#include "G4VisStateDependent.hh"
#include "G4UIdirectory.hh"
#include "G4VGraphicsSystem.hh"
#include "G4VSceneHandler.hh"
#include "G4VViewer.hh"
#include "G4VPhysicalVolume.hh"
#include "G4LogicalVolume.hh"
#include "G4VSolid.hh"
#include "G4Vector3D.hh"
#include "G4Point3D.hh"
#include "G4RotationMatrix.hh"
#include "G4Polyline.hh"
#include "G4Polyhedron.hh"
#include "G4NullModel.hh"
#include "G4ModelingParameters.hh"
#include "G4TransportationManager.hh"
#include "G4VisCommandModelCreate.hh"
#include "G4VisCommandsListManager.hh"
#include "G4VisModelManager.hh"
#include "G4VModelFactory.hh"
#include "G4VisFilterManager.hh"
#include "G4VTrajectoryModel.hh"
#include "G4TrajectoryDrawByCharge.hh"
#include "Randomize.hh"
#include "G4RunManager.hh"
#include "G4RunManagerFactory.hh"
#include "G4EventManager.hh"
#include "G4Run.hh"
#include "G4Event.hh"
#include <map>
#include <set>
#include <vector>
#include <sstream>

#include "G4Threading.hh"
#include "G4AutoLock.hh"
#include "G4GeometryWorkspace.hh" // no_geant4_module_check(!G4MULTITHREADED)
#include "G4SolidsWorkspace.hh"
#include <deque>
#include <typeinfo>
#include <chrono>
#include <thread>
#include <utility>

#define G4warn G4cout

// Local threading related variables
namespace {
  G4bool mtRunInProgress = false;
  std::deque<const G4Event*> mtVisEventQueue;
  G4Thread* mtVisSubThread = 0;
  [[maybe_unused]] G4Mutex mtVisSubThreadMutex = G4MUTEX_INITIALIZER;
  //  G4Mutex visBeginOfRunMutex = G4MUTEX_INITIALIZER;
  //  G4Mutex visBeginOfEventMutex = G4MUTEX_INITIALIZER;
  G4Mutex visEndOfEventMutex = G4MUTEX_INITIALIZER;
  //  G4Mutex visEndOfRunMutex = G4MUTEX_INITIALIZER;
  G4bool isSubEventRunManagerType = false;
  G4bool isValidViewForRun = false;
  G4bool isFakeRun = false;
}

// Class statics
G4VisManager* G4VisManager::fpInstance = 0;

G4VisManager::Verbosity G4VisManager::fVerbosity = G4VisManager::warnings;

G4VisManager::G4VisManager (const G4String& verbosityString)
: fVerbose         (1)
, fDefaultGraphicsSystemName("TSG")          // Override in G4VisExecutive
, fDefaultXGeometryString   ("600x600-0+0")  // Override in G4VisExecutive
, fDefaultGraphicsSystemBasis ("G4VisManager initialisation")
, fDefaultXGeometryStringBasis("G4VisManager initialisation")
, fInitialised     (false)
, fpGraphicsSystem (0)
, fpScene          (0)
, fpSceneHandler   (0)
, fpViewer         (0)
, fpStateDependent (0)
, fEventRefreshing          (false)
, fTransientsDrawnThisRun   (false)
, fTransientsDrawnThisEvent (false)
, fNoOfEventsDrawnThisRun   (0)
, fNKeepForPostProcessingRequests (0)
, fNKeepTheEventRequests    (0)
, fEventKeepingSuspended    (false)
, fDrawEventOnlyIfToBeKept  (false)
, fpRequestedEvent          (0)
, fReviewingKeptEvents      (false)
, fAbortReviewKeptEvents    (false)
, fReviewingPlots           (false)
, fAbortReviewPlots         (false)
, fIsDrawGroup              (false)
, fDrawGroupNestingDepth    (0)
, fIgnoreStateChanges       (false)
, fMaxEventQueueSize        (100)
, fWaitOnEventQueueFull     (true)
// All other objects use default constructors.
{
  fpTrajDrawModelMgr = new G4VisModelManager<G4VTrajectoryModel>("/vis/modeling/trajectories");
  fpTrajFilterMgr = new G4VisFilterManager<G4VTrajectory>("/vis/filtering/trajectories");
  fpHitFilterMgr = new G4VisFilterManager<G4VHit>("/vis/filtering/hits");
  fpDigiFilterMgr = new G4VisFilterManager<G4VDigi>("/vis/filtering/digi");

  VerbosityGuidanceStrings.push_back
    ("Simple graded message scheme - digit or string (1st character defines):");
  VerbosityGuidanceStrings.push_back
    ("  0) quiet,         // Nothing is printed.");
  VerbosityGuidanceStrings.push_back
    ("  1) startup,       // Startup and endup messages are printed...");
  VerbosityGuidanceStrings.push_back
    ("  2) errors,        // ...and errors...");
  VerbosityGuidanceStrings.push_back
    ("  3) warnings,      // ...and warnings...");
  VerbosityGuidanceStrings.push_back
    ("  4) confirmations, // ...and confirming messages...");
  VerbosityGuidanceStrings.push_back
    ("  5) parameters,    // ...and parameters of scenes and views...");
  VerbosityGuidanceStrings.push_back
    ("  6) all            // ...and everything available.");

  if (fpInstance) {
    G4Exception
      ("G4VisManager::G4VisManager",
       "visman0001", FatalException,
       "Attempt to Construct more than one VisManager");
  }

  fpInstance = this;
  SetConcreteInstance(this);

  fpStateDependent = new G4VisStateDependent (this);
  // No need to delete this; G4StateManager does this.

  fVerbosity = GetVerbosityValue(verbosityString);
  if (fVerbosity >= startup) {
      G4cout
	<< "Visualization Manager instantiating with verbosity \""
	<< VerbosityString(fVerbosity)
	<< "\"..." << G4endl;
  }

  // Note: The specific graphics systems must be instantiated in a
  // higher level library to avoid circular dependencies.  Also,
  // some specifically need additional external libararies that the
  // user must supply.  Therefore we ask the user to implement
  // RegisterGraphicsSystems() and RegisterModelFactories()
  // in a subclass.  We have to wait for the subclass to instantiate
  // so RegisterGraphicsSystems() cannot be called from this
  // constructor; it is called from Initialise().  So we ask the
  // user:
  //   (a) to write a subclass and implement  RegisterGraphicsSystems()
  //       and RegisterModelFactories().  See
  //       visualization/include/G4VisExecutive.hh/icc as an example.
  //   (b) instantiate the subclass.
  //   (c) invoke the Initialise() method of the subclass.
  // For example:
  //   ...
  //   // Instantiate and initialise Visualization Manager.
  //   G4VisManager* visManager = new G4VisExecutive;
  //   visManager -> SetVerboseLevel (Verbose);
  //   visManager -> Initialise ();
  //   // (Don't forget to delete visManager;)
  //   ...

  // Make top level command directory...
  // vis commands should *not* be broadcast to workers
  G4bool propagateToWorkers;
  auto directory = new G4UIdirectory ("/vis/",propagateToWorkers=false);
  directory -> SetGuidance ("Visualization commands.");
  // Request commands in name order
  directory -> Sort();  // Ordering propagates to sub-directories
  fDirectoryList.push_back (directory);

  // Instantiate *basic* top level commands so that they can be used
  // immediately after instantiation of the vis manager.  Other top
  // level and lower level commands are instantiated later in
  // RegisterMessengers.
  G4VVisCommand::SetVisManager (this);  // Sets shared pointer
  RegisterMessenger(new G4VisCommandVerbose);
  RegisterMessenger(new G4VisCommandInitialize);
}

G4VisManager::~G4VisManager()
{
  G4UImanager* UImanager = G4UImanager::GetUIpointer();
  UImanager->SetCoutDestination(nullptr);
  std::size_t i;
  for (i = 0; i < fSceneList.size (); ++i) {
    delete fSceneList[i];
  }
  for (i = 0; i < fAvailableSceneHandlers.size (); ++i) {
  if (fAvailableSceneHandlers[i] != NULL) {
    delete fAvailableSceneHandlers[i];
  }
  }
  for (i = 0; i < fAvailableGraphicsSystems.size (); ++i) {
    if (fAvailableGraphicsSystems[i]) {
      delete fAvailableGraphicsSystems[i];
    }
  }
  if (fVerbosity >= startup) {
    G4cout << "Graphics systems deleted." << G4endl;
    G4cout << "Visualization Manager deleting..." << G4endl;
  }
  for (i = 0; i < fMessengerList.size (); ++i) {
    delete fMessengerList[i];
  }
  for (i = 0; i < fDirectoryList.size (); ++i) {
    delete fDirectoryList[i];
  }

  delete fpDigiFilterMgr;
  delete fpHitFilterMgr;
  delete fpTrajFilterMgr;
  delete fpTrajDrawModelMgr;
  fpInstance = 0;
}

G4VisManager* G4VisManager::GetInstance () {
  if (!fpInstance) {
    G4Exception
      ("G4VisManager::GetInstance",
       "visman0002", FatalException, "VisManager not yet instantiated");
  }
  return fpInstance;
}

void G4VisManager::Initialise () {

  if (fInitialised && fVerbosity >= warnings) {
    G4warn << "WARNING: G4VisManager::Initialise: already initialised."
	   << G4endl;
    return;
  }

  if (fVerbosity >= startup) {
    G4cout << "Visualization Manager initialising..." << G4endl;
  }

  if (fVerbosity >= parameters) {
    G4cout <<
      "\nYou have instantiated your own Visualization Manager, inheriting"
      "\n  G4VisManager and implementing RegisterGraphicsSystems(), in which"
      "\n  you should, normally, instantiate drivers which do not need"
      "\n  external packages or libraries, and, optionally, drivers under"
      "\n  control of environment variables."
      "\n  Also you should implement RegisterModelFactories()."
      "\n  See visualization/management/include/G4VisExecutive.hh/icc, for example."
      "\n  In your main() you will have something like:"
      "\n    G4VisManager* visManager = new G4VisExecutive;"
      "\n    visManager -> SetVerboseLevel (Verbose);"
      "\n    visManager -> Initialize ();"
      "\n  (Don't forget to delete visManager;)"
      "\n"
	 << G4endl;
  }

  if (fVerbosity >= startup) {
    G4cout << "Registering graphics systems..." << G4endl;
  }

  RegisterGraphicsSystems ();

  if (fVerbosity >= startup) {
    G4cout <<
      "\nYou have successfully registered the following graphics systems."
	 << G4endl;
    PrintAvailableGraphicsSystems (fVerbosity);
    G4cout << G4endl;
  }

  // Make command directories for commands instantiated in the
  // modeling subcategory...
  G4UIcommand* directory;
  directory = new G4UIdirectory ("/vis/modeling/");
  directory -> SetGuidance ("Modeling commands.");
  fDirectoryList.push_back (directory);
  directory = new G4UIdirectory ("/vis/modeling/trajectories/");
  directory -> SetGuidance ("Trajectory model commands.");
  fDirectoryList.push_back (directory);
  directory = new G4UIdirectory ("/vis/modeling/trajectories/create/");
  directory -> SetGuidance ("Create trajectory models and messengers.");
  fDirectoryList.push_back (directory);

  // Filtering command directory
  directory = new G4UIdirectory ("/vis/filtering/");
  directory -> SetGuidance ("Filtering commands.");
  fDirectoryList.push_back (directory);
  directory = new G4UIdirectory ("/vis/filtering/trajectories/");
  directory -> SetGuidance ("Trajectory filtering commands.");
  fDirectoryList.push_back (directory);
  directory = new G4UIdirectory ("/vis/filtering/trajectories/create/");
  directory -> SetGuidance ("Create trajectory filters and messengers.");
  fDirectoryList.push_back (directory);
  directory = new G4UIdirectory ("/vis/filtering/hits/");
  directory -> SetGuidance ("Hit filtering commands.");
  fDirectoryList.push_back (directory);
  directory = new G4UIdirectory ("/vis/filtering/hits/create/");
  directory -> SetGuidance ("Create hit filters and messengers.");
  fDirectoryList.push_back (directory);
  directory = new G4UIdirectory ("/vis/filtering/digi/");
  directory -> SetGuidance ("Digi filtering commands.");
  fDirectoryList.push_back (directory);
  directory = new G4UIdirectory ("/vis/filtering/digi/create/");
  directory -> SetGuidance ("Create digi filters and messengers.");
  fDirectoryList.push_back (directory);

  RegisterMessengers ();

  if (fVerbosity >= startup) {
    G4cout << "Registering model factories..." << G4endl;
  }

  RegisterModelFactories();

  if (fVerbosity >= startup) {
    G4cout <<
      "\nYou have successfully registered the following model factories."
	   << G4endl;
    PrintAvailableModels (fVerbosity);
    G4cout << G4endl;
  }

  if (fVerbosity >= startup) {
    PrintAvailableUserVisActions (fVerbosity);
    G4cout << G4endl;
  }

  InitialiseG4ColourMap();

  if (fVerbosity >= startup) {
    G4cout <<
    "Some /vis commands (optionally) take a string to specify colour."
    "\n\"/vis/list\" to see available colours."
    << G4endl;
  }

  fInitialised = true;
}

void G4VisManager::InitialiseG4ColourMap() const
{
  G4Colour::InitialiseColourMap();  // Initialises (if not already initialised)

  // our forever 65 named colors taken long time ago from X11.
  // Extracted from g4tools/include/tools/colors
  // Copyright (C) 2010, Guy Barrand. All rights reserved.
  // See the file tools.license for terms.

#define TOOLS_COLORS_STAT(name,r,g,b) \
G4Colour::AddToMap(#name, G4Colour(r,g,b));

  //0-9
  TOOLS_COLORS_STAT(aquamarine,0.496101F,0.996109F,0.828138F)
  TOOLS_COLORS_STAT(mediumaquamarine,0.398444F,0.800793F,0.664073F)
  //  TOOLS_COLORS_STAT(black,0,0,0) (already defined)
  //  TOOLS_COLORS_STAT(blue,0,0,1) (already defined)
  TOOLS_COLORS_STAT(cadetblue,0.371099F,0.617197F,0.62501F)
  TOOLS_COLORS_STAT(cornflowerblue,0.390631F,0.58204F,0.925795F)
  TOOLS_COLORS_STAT(darkslateblue,0.281254F,0.238285F,0.542977F)
  TOOLS_COLORS_STAT(lightblue,0.675792F,0.843763F,0.898451F)
  TOOLS_COLORS_STAT(lightsteelblue,0.68751F,0.765637F,0.867201F)
  TOOLS_COLORS_STAT(mediumblue,0,0,0.800793F)

  //10-19
  TOOLS_COLORS_STAT(mediumslateblue,0.480476F,0.406256F,0.929702F)
  TOOLS_COLORS_STAT(midnightblue,0.0976577F,0.0976577F,0.437507F)
  TOOLS_COLORS_STAT(navyblue,0,0,0.500008F)
  TOOLS_COLORS_STAT(navy,0,0,0.500008F)
  TOOLS_COLORS_STAT(skyblue,0.527352F,0.8047F,0.917983F)
  TOOLS_COLORS_STAT(slateblue,0.414069F,0.351568F,0.800793F)
  TOOLS_COLORS_STAT(steelblue,0.273442F,0.50782F,0.703136F)
  TOOLS_COLORS_STAT(coral,0.996109F,0.496101F,0.312505F)
  //  TOOLS_COLORS_STAT(cyan,0,1,1) (already defined)
  TOOLS_COLORS_STAT(firebrick,0.695323F,0.132815F,0.132815F)

  //20-29
  //  TOOLS_COLORS_STAT(brown,0.644541F,0.164065F,0.164065F) (already defined)
  TOOLS_COLORS_STAT(gold,0.996109F,0.839857F,0)
  TOOLS_COLORS_STAT(goldenrod,0.851575F,0.644541F,0.125002F)
  //  TOOLS_COLORS_STAT(green,0,1,0) (already defined)
  TOOLS_COLORS_STAT(darkgreen,0,0.390631F,0)
  TOOLS_COLORS_STAT(darkolivegreen,0.332036F,0.417975F,0.183597F)
  TOOLS_COLORS_STAT(forestgreen,0.132815F,0.542977F,0.132815F)
  TOOLS_COLORS_STAT(limegreen,0.195315F,0.800793F,0.195315F)
  TOOLS_COLORS_STAT(mediumseagreen,0.234379F,0.699229F,0.441413F)
  TOOLS_COLORS_STAT(mediumspringgreen,0,0.976577F,0.601572F)

  //30-39
  TOOLS_COLORS_STAT(palegreen,0.593759F,0.980484F,0.593759F)
  TOOLS_COLORS_STAT(seagreen,0.17969F,0.542977F,0.339849F)
  TOOLS_COLORS_STAT(springgreen,0,0.996109F,0.496101F)
  TOOLS_COLORS_STAT(yellowgreen,0.601572F,0.800793F,0.195315F)
  TOOLS_COLORS_STAT(darkslategrey,0.183597F,0.308598F,0.308598F)
  TOOLS_COLORS_STAT(dimgrey,0.410163F,0.410163F,0.410163F)
  TOOLS_COLORS_STAT(lightgrey,0.824231F,0.824231F,0.824231F)
  //  TOOLS_COLORS_STAT(grey,0.750011F,0.750011F,0.750011F) (already defined)
  TOOLS_COLORS_STAT(khaki,0.937514F,0.898451F,0.546883F)
  //  TOOLS_COLORS_STAT(magenta,1,0,1) (already defined)

  //40-49
  TOOLS_COLORS_STAT(maroon,0.68751F,0.187503F,0.375006F)
  TOOLS_COLORS_STAT(orange,0.996109F,0.644541F,0)
  TOOLS_COLORS_STAT(orchid,0.851575F,0.437507F,0.83595F)
  TOOLS_COLORS_STAT(darkorchid,0.597665F,0.195315F,0.796887F)
  TOOLS_COLORS_STAT(mediumorchid,0.726574F,0.332036F,0.824231F)
  TOOLS_COLORS_STAT(pink,0.996109F,0.750011F,0.792981F)
  TOOLS_COLORS_STAT(plum,0.863294F,0.62501F,0.863294F)
  //  TOOLS_COLORS_STAT(red,1,0,0) (already defined)
  TOOLS_COLORS_STAT(indianred,0.800793F,0.35938F,0.35938F)
  TOOLS_COLORS_STAT(mediumvioletred,0.777356F,0.0820325F,0.519539F)

  //50-59
  TOOLS_COLORS_STAT(orangered,0.996109F,0.269535F,0)
  TOOLS_COLORS_STAT(violetred,0.812512F,0.125002F,0.562509F)
  TOOLS_COLORS_STAT(salmon,0.976577F,0.500008F,0.445319F)
  TOOLS_COLORS_STAT(sienna,0.62501F,0.320317F,0.175784F)
  TOOLS_COLORS_STAT(tan,0.820325F,0.703136F,0.546883F)
  TOOLS_COLORS_STAT(thistle,0.843763F,0.746105F,0.843763F)
  TOOLS_COLORS_STAT(turquoise,0.250004F,0.875013F,0.812512F)
  TOOLS_COLORS_STAT(darkturquoise,0,0.8047F,0.816419F)
  TOOLS_COLORS_STAT(mediumturquoise,0.281254F,0.816419F,0.796887F)
  TOOLS_COLORS_STAT(violet,0.929702F,0.50782F,0.929702F)

  //60-64
  TOOLS_COLORS_STAT(blueviolet,0.539071F,0.167971F,0.882826F)
  TOOLS_COLORS_STAT(wheat,0.957046F,0.867201F,0.699229F)
  //  TOOLS_COLORS_STAT(white,1,1,1) (already defined)
  //  TOOLS_COLORS_STAT(yellow,1,1,0) (already defined)
  TOOLS_COLORS_STAT(greenyellow,0.675792F,0.996109F,0.18359F)

#undef TOOLS_COLORS_STAT
}

void G4VisManager::RegisterMessengers () {
  
  // Instantiate individual messengers/commands (often - but not
  // always - one command per messenger).
  
  G4UIcommand* directory;
  
  directory = new G4UIdirectory ("/vis/geometry/");
  directory -> SetGuidance("Operations on vis attributes of Geant4 geometry.");
  fDirectoryList.push_back (directory);
  RegisterMessenger(new G4VisCommandGeometryList);
  RegisterMessenger(new G4VisCommandGeometryRestore);
  
  directory = new G4UIdirectory ("/vis/geometry/set/");
  directory -> SetGuidance("Set vis attributes of Geant4 geometry.");
  fDirectoryList.push_back (directory);
  RegisterMessenger(new G4VisCommandGeometrySetColour);
  RegisterMessenger(new G4VisCommandGeometrySetDaughtersInvisible);
  RegisterMessenger(new G4VisCommandGeometrySetLineStyle);
  RegisterMessenger(new G4VisCommandGeometrySetLineWidth);
  RegisterMessenger(new G4VisCommandGeometrySetForceAuxEdgeVisible);
  RegisterMessenger(new G4VisCommandGeometrySetForceCloud);
  RegisterMessenger(new G4VisCommandGeometrySetForceLineSegmentsPerCircle);
  RegisterMessenger(new G4VisCommandGeometrySetForceSolid);
  RegisterMessenger(new G4VisCommandGeometrySetForceWireframe);
  RegisterMessenger(new G4VisCommandGeometrySetVisibility);
  
  directory = new G4UIdirectory ("/vis/multithreading/");
  directory -> SetGuidance("Commands unique to multithreading mode.");
  fDirectoryList.push_back (directory);
  RegisterMessenger(new G4VisCommandMultithreadingActionOnEventQueueFull);
  RegisterMessenger(new G4VisCommandMultithreadingMaxEventQueueSize);

  directory = new G4UIdirectory ("/vis/set/");
  directory -> SetGuidance
    ("Set quantities for use in future commands where appropriate.");
  fDirectoryList.push_back (directory);
  RegisterMessenger(new G4VisCommandSetArrow3DLineSegmentsPerCircle);
  RegisterMessenger(new G4VisCommandSetColour);
  RegisterMessenger(new G4VisCommandSetExtentForField);
  RegisterMessenger(new G4VisCommandSetLineWidth);
  RegisterMessenger(new G4VisCommandSetTextColour);
  RegisterMessenger(new G4VisCommandSetTextLayout);
  RegisterMessenger(new G4VisCommandSetTextSize);
  RegisterMessenger(new G4VisCommandSetTouchable);
  RegisterMessenger(new G4VisCommandSetVolumeForField);

  directory = new G4UIdirectory ("/vis/scene/");
  directory -> SetGuidance ("Operations on Geant4 scenes.");
  fDirectoryList.push_back (directory);
  RegisterMessenger(new G4VisCommandSceneActivateModel);
  RegisterMessenger(new G4VisCommandSceneCreate);
  RegisterMessenger(new G4VisCommandSceneEndOfEventAction);
  RegisterMessenger(new G4VisCommandSceneEndOfRunAction);
  RegisterMessenger(new G4VisCommandSceneList);
  RegisterMessenger(new G4VisCommandSceneNotifyHandlers);
  RegisterMessenger(new G4VisCommandSceneRemoveModel);
  RegisterMessenger(new G4VisCommandSceneSelect);
  RegisterMessenger(new G4VisCommandSceneShowExtents);

  directory = new G4UIdirectory ("/vis/scene/add/");
  directory -> SetGuidance ("Add model to current scene.");
  fDirectoryList.push_back (directory);
  RegisterMessenger(new G4VisCommandSceneAddArrow);
  RegisterMessenger(new G4VisCommandSceneAddArrow2D);
  RegisterMessenger(new G4VisCommandSceneAddAxes);
  RegisterMessenger(new G4VisCommandSceneAddDate);
  RegisterMessenger(new G4VisCommandSceneAddDigis);
  RegisterMessenger(new G4VisCommandSceneAddEndOfRunMacro);
  RegisterMessenger(new G4VisCommandSceneAddEventID);
  RegisterMessenger(new G4VisCommandSceneAddExtent);
  RegisterMessenger(new G4VisCommandSceneAddElectricField);
  RegisterMessenger(new G4VisCommandSceneAddFrame);
  RegisterMessenger(new G4VisCommandSceneAddGPS);
  RegisterMessenger(new G4VisCommandSceneAddHits);
  RegisterMessenger(new G4VisCommandSceneAddLine);
  RegisterMessenger(new G4VisCommandSceneAddLine2D);
  RegisterMessenger(new G4VisCommandSceneAddLocalAxes);
  RegisterMessenger(new G4VisCommandSceneAddLogicalVolume);
  RegisterMessenger(new G4VisCommandSceneAddLogo);
  RegisterMessenger(new G4VisCommandSceneAddLogo2D);
  RegisterMessenger(new G4VisCommandSceneAddMagneticField);
  RegisterMessenger(new G4VisCommandSceneAddPlotter);
  RegisterMessenger(new G4VisCommandSceneAddPSHits);
  RegisterMessenger(new G4VisCommandSceneAddScale);
  RegisterMessenger(new G4VisCommandSceneAddText);
  RegisterMessenger(new G4VisCommandSceneAddText2D);
  RegisterMessenger(new G4VisCommandSceneAddTrajectories);
  RegisterMessenger(new G4VisCommandSceneAddUserAction);
  RegisterMessenger(new G4VisCommandSceneAddVolume);
  
  RegisterMessenger(new G4VisCommandPlotterCreate);
  RegisterMessenger(new G4VisCommandPlotterSetLayout);
  RegisterMessenger(new G4VisCommandPlotterAddStyle);
  RegisterMessenger(new G4VisCommandPlotterAddRegionStyle);
  RegisterMessenger(new G4VisCommandPlotterAddRegionParameter);
  RegisterMessenger(new G4VisCommandPlotterClear);
  RegisterMessenger(new G4VisCommandPlotterClearRegion);
  RegisterMessenger(new G4VisCommandPlotterList);
  RegisterMessenger(new G4VisCommandPlotterAddRegionH1);
  RegisterMessenger(new G4VisCommandPlotterAddRegionH2);
  
  directory = new G4UIdirectory ("/vis/sceneHandler/");
  directory -> SetGuidance ("Operations on Geant4 scene handlers.");
  fDirectoryList.push_back (directory);
  RegisterMessenger(new G4VisCommandSceneHandlerAttach);
  RegisterMessenger(new G4VisCommandSceneHandlerCreate);
  RegisterMessenger(new G4VisCommandSceneHandlerList);
  RegisterMessenger(new G4VisCommandSceneHandlerSelect);
  
  directory = new G4UIdirectory ("/vis/touchable/");
  directory -> SetGuidance ("Operations on touchables.");
  fDirectoryList.push_back (directory);
  RegisterMessenger(new G4VisCommandsTouchable);
  
  directory = new G4UIdirectory ("/vis/touchable/set/");
  directory -> SetGuidance ("Set vis attributes of current touchable.");
  fDirectoryList.push_back (directory);
  RegisterMessenger(new G4VisCommandsTouchableSet);

  directory = new G4UIdirectory ("/vis/viewer/");
  directory -> SetGuidance ("Operations on Geant4 viewers.");
  fDirectoryList.push_back (directory);
  RegisterMessenger(new G4VisCommandViewerAddCutawayPlane);
  RegisterMessenger(new G4VisCommandViewerCentreOn);
  RegisterMessenger(new G4VisCommandViewerChangeCutawayPlane);
  RegisterMessenger(new G4VisCommandViewerClear);
  RegisterMessenger(new G4VisCommandViewerClearCutawayPlanes);
  RegisterMessenger(new G4VisCommandViewerClearTransients);
  RegisterMessenger(new G4VisCommandViewerClearVisAttributesModifiers);
  RegisterMessenger(new G4VisCommandViewerClone);
  RegisterMessenger(new G4VisCommandViewerColourByDensity);
  RegisterMessenger(new G4VisCommandViewerCopyViewFrom);
  RegisterMessenger(new G4VisCommandViewerCreate);
  RegisterMessenger(new G4VisCommandViewerDolly);
  RegisterMessenger(new G4VisCommandViewerFlush);
  RegisterMessenger(new G4VisCommandViewerInterpolate);
  RegisterMessenger(new G4VisCommandViewerList);
  RegisterMessenger(new G4VisCommandViewerPan);
  RegisterMessenger(new G4VisCommandViewerRebuild);
  RegisterMessenger(new G4VisCommandViewerRefresh);
  RegisterMessenger(new G4VisCommandViewerReset);
  RegisterMessenger(new G4VisCommandViewerResetCameraParameters);
  RegisterMessenger(new G4VisCommandViewerSave);
  RegisterMessenger(new G4VisCommandViewerScale);
  RegisterMessenger(new G4VisCommandViewerSelect);
  RegisterMessenger(new G4VisCommandViewerUpdate);
  RegisterMessenger(new G4VisCommandViewerZoom);
  
  directory = new G4UIdirectory ("/vis/viewer/default/");
  directory -> SetGuidance("Set default values for future viewers.");
  fDirectoryList.push_back (directory);
  RegisterMessenger(new G4VisCommandViewerDefaultHiddenEdge);
  RegisterMessenger(new G4VisCommandViewerDefaultStyle);
  
  directory = new G4UIdirectory ("/vis/viewer/set/");
  directory -> SetGuidance ("Set view parameters of current viewer.");
  fDirectoryList.push_back (directory);
  RegisterMessenger(new G4VisCommandsViewerSet);
  
  // *Basic* top level commands were instantiated in the constructor
  // so that they can be used immediately after instantiation of the
  // vis manager.  Other top level commands, including "compound commands"
  // (i.e., commands that invoke other commands) are instantiated here.

  RegisterMessenger(new G4VisCommandAbortReviewKeptEvents);
  RegisterMessenger(new G4VisCommandAbortReviewPlots);
  RegisterMessenger(new G4VisCommandDrawOnlyToBeKeptEvents);
  RegisterMessenger(new G4VisCommandDrawTree);
  RegisterMessenger(new G4VisCommandDrawView);
  RegisterMessenger(new G4VisCommandDrawLogicalVolume);
  RegisterMessenger(new G4VisCommandDrawVolume);
  RegisterMessenger(new G4VisCommandEnable);
  RegisterMessenger(new G4VisCommandList);
  RegisterMessenger(new G4VisCommandOpen);
  RegisterMessenger(new G4VisCommandPlot);
  RegisterMessenger(new G4VisCommandReviewKeptEvents);
  RegisterMessenger(new G4VisCommandReviewPlots);
  RegisterMessenger(new G4VisCommandSpecify);

  // List manager commands
  RegisterMessenger(new G4VisCommandListManagerList< G4VisModelManager<G4VTrajectoryModel> >
		    (fpTrajDrawModelMgr, fpTrajDrawModelMgr->Placement()));
  RegisterMessenger(new G4VisCommandListManagerSelect< G4VisModelManager<G4VTrajectoryModel> >
		    (fpTrajDrawModelMgr, fpTrajDrawModelMgr->Placement()));
  
  // Trajectory filter manager commands
  RegisterMessenger(new G4VisCommandListManagerList< G4VisFilterManager<G4VTrajectory> >
                    (fpTrajFilterMgr, fpTrajFilterMgr->Placement()));
  RegisterMessenger(new G4VisCommandManagerMode< G4VisFilterManager<G4VTrajectory> >
                    (fpTrajFilterMgr, fpTrajFilterMgr->Placement()));
  
  // Hit filter manager commands
  RegisterMessenger(new G4VisCommandListManagerList< G4VisFilterManager<G4VHit> >
                    (fpHitFilterMgr, fpHitFilterMgr->Placement()));
  RegisterMessenger(new G4VisCommandManagerMode< G4VisFilterManager<G4VHit> >
                    (fpHitFilterMgr, fpHitFilterMgr->Placement()));
  
  // Digi filter manager commands
  RegisterMessenger(new G4VisCommandListManagerList< G4VisFilterManager<G4VDigi> >
                    (fpDigiFilterMgr, fpDigiFilterMgr->Placement()));
  RegisterMessenger(new G4VisCommandManagerMode< G4VisFilterManager<G4VDigi> >
                    (fpDigiFilterMgr, fpDigiFilterMgr->Placement()));
}

#include <tools/histo/h1d>
#include <tools/histo/h2d>

namespace {
  struct PlotResults {
    std::size_t fNumberOfPlots = 0;
    std::size_t fTotalEntries = 0;
  };
  template <typename HT>  // tools::histo::h1d, etc
  PlotResults ResultsOfHnPlots(const G4String& plotType) {  // h1, etc.
    PlotResults plotResults;
    auto ui = G4UImanager::GetUIpointer();
    auto keepControlVerbose = ui->GetVerboseLevel();
    ui->SetVerboseLevel(0);
    auto status = ui->ApplyCommand("/analysis/" + plotType + "/getVector");
    ui->SetVerboseLevel(keepControlVerbose);
    if(status==G4UIcommandStatus::fCommandSucceeded) {
      G4String hexString = ui->GetCurrentValues(G4String("/analysis/" + plotType + "/getVector"));
      if(hexString.size()) {
        void* ptr; std::istringstream is(hexString); is >> ptr;
        auto vectorOfPlots = (const std::vector<HT*>*)ptr;
        for (std::size_t i = 0; i < vectorOfPlots->size(); ++i) {
          auto plot = (*vectorOfPlots)[i];
          if (plot == nullptr) continue;  // Ignore deleted plots
          ++plotResults.fNumberOfPlots;
          plotResults.fTotalEntries += plot->entries();
        }
      }
    }
    return plotResults;
  }
  void PrintListOfPlots() {
    std::size_t numberOfPlots = 0;
    std::size_t numberOfEntries = 0;
    PlotResults h1results = ResultsOfHnPlots<tools::histo::h1d>("h1");
    numberOfPlots += h1results.fNumberOfPlots;
    numberOfEntries += h1results.fTotalEntries;
    PlotResults h2results = ResultsOfHnPlots<tools::histo::h2d>("h2");
    numberOfPlots += h2results.fNumberOfPlots;
    numberOfEntries += h2results.fTotalEntries;
    if (numberOfPlots > 0) {
      G4warn << "There are histograms that can be viewed with visualization:";
      if (h1results.fNumberOfPlots > 0) {
        G4warn << "\n  " << h1results.fNumberOfPlots << " h1 histograms(s)";
      }
      if (h2results.fNumberOfPlots > 0) {
        G4warn << "\n  " << h2results.fNumberOfPlots << " h2 histograms(s)";
      }
      G4warn
      << "\n  List them with \"/analysis/list\"."
      << "\n  View them immediately with \"/vis/plot\" or \"/vis/reviewPlots\"."
      << G4endl;
      if (numberOfEntries == 0) {
        G4warn <<
        "  But...there are no entries. To make your histograms available for"
        "\n  plotting in this UI session, use CloseFile(false) in your"
        "\n  EndOfRunAction and Reset() in your BeginOfRunAction."
        << G4endl;
      }
    }
  }
}

void G4VisManager::Enable() {
  if (IsValidView ()) {
    SetConcreteInstance(this);
    if (fVerbosity >= confirmations) {
      G4cout << "G4VisManager::Enable: visualization enabled." << G4endl;
    }
    if (fVerbosity >= warnings) {
      std::size_t nKeptEvents = 0;
      const G4Run* run = G4RunManager::GetRunManager()->GetCurrentRun();
      if (run) nKeptEvents = run->GetNumberOfKeptEvents();
      G4String isare("are"),plural("s");
      if (nKeptEvents == 1) {isare = "is"; plural = "";}
      G4cout <<
      "There " << isare << ' ' << nKeptEvents << " kept event" << plural << '.'
      << G4endl;
      if (nKeptEvents > 0) {
        G4cout <<
  "  \"/vis/reviewKeptEvents\" to review one by one."
  "\n  To see accumulated, \"/vis/enable\", then \"/vis/viewer/flush\" or \"/vis/viewer/rebuild\"."
        << G4endl;
      }
      PrintListOfPlots();
    }
  }
  else {
    if (fVerbosity >= warnings) {
      G4warn <<
	"G4VisManager::Enable: WARNING: visualization remains disabled for"
	"\n  above reasons.  Rectifying with valid vis commands will"
	"\n  automatically enable."
	     << G4endl;
    }
  }
}

void G4VisManager::Disable() {
  SetConcreteInstance(0);
  if (fVerbosity >= confirmations) {
    G4cout <<
    "G4VisManager::Disable: visualization disabled."
    "\n  The pointer returned by GetConcreteInstance will be zero."
    "\n  Note that it will become enabled after some valid vis commands."
	   << G4endl;
  }
  if (fVerbosity >= warnings) {
    G4int currentTrajectoryType =
    G4RunManagerKernel::GetRunManagerKernel()->GetTrackingManager()->GetStoreTrajectory();
    if (currentTrajectoryType > 0) {
    G4warn <<
      "You may wish to disable trajectory production too:"
      "\n  \"/tracking/storeTrajectory 0\""
      "\nbut don't forget to re-enable with"
      "\n  \"/vis/enable\""
      "\n  \"/tracking/storeTrajectory " << currentTrajectoryType
      << "\"\n  and maybe \"/vis/viewer/rebuild\""
      << G4endl;
    }
  }
}

const G4GraphicsSystemList& G4VisManager::GetAvailableGraphicsSystems () {
  std::size_t nSystems = fAvailableGraphicsSystems.size ();
  if (nSystems == 0) {
    if (fVerbosity >= warnings) {
      G4warn << "G4VisManager::GetAvailableGraphicsSystems: WARNING: no"
	"\n graphics system available!"
	"\n  1) Did you have environment variables G4VIS_BUILD_xxxx_DRIVER set"
	"\n     when you compiled/built the visualization code?"
	"\n  2) Did you instantiate your own Visualization Manager and forget"
	"\n     to implement RegisterGraphicsSystems correctly?"
	"\n  3) You can register your own graphics system, e.g.,"
	"\n     visManager->RegisterGraphicsSystem(new MyGraphicsSystem);)"
	"\n     after instantiating your vis manager and before"
	"\n     visManager->Initialize()."
	     << G4endl;
    }
  }
  return fAvailableGraphicsSystems;
}

G4bool G4VisManager::RegisterGraphicsSystem (G4VGraphicsSystem* pSystem) {
  G4bool happy = true;
  if (pSystem) {
    fAvailableGraphicsSystems.push_back (pSystem);
    if (fVerbosity >= confirmations) {
      G4cout << "G4VisManager::RegisterGraphicsSystem: "
	     << pSystem -> GetName ();
      if (pSystem -> GetNickname () != "") {
	G4cout << " (" << pSystem -> GetNickname () << ")";
      }
      G4cout << " registered." << G4endl;
    }
  }
  else {
    if (fVerbosity >= errors) {
      G4warn << "G4VisManager::RegisterGraphicsSystem: null pointer!"
	     << G4endl;
    }
    happy=false;
  }
  return happy;
}

const G4VTrajectoryModel*
G4VisManager::CurrentTrajDrawModel() const
{
  assert (0 != fpTrajDrawModelMgr);

  const G4VTrajectoryModel* model = fpTrajDrawModelMgr->Current();

  if (0 == model) {
    // No model was registered with the trajectory model manager.
    // Use G4TrajectoryDrawByCharge as a fallback.
    fpTrajDrawModelMgr->Register(new G4TrajectoryDrawByCharge("DefaultModel"));
    if (fVerbosity >= warnings) {
      G4warn<<"G4VisManager: Using G4TrajectoryDrawByCharge as fallback trajectory model."<<G4endl;
      G4warn<<"See commands in /vis/modeling/trajectories/ for other options."<<G4endl;
    }
  }

  model = fpTrajDrawModelMgr->Current();
  assert (0 != model); // Should definitely exist now

  return model;
}

void G4VisManager::RegisterModel(G4VTrajectoryModel* model)
{
  fpTrajDrawModelMgr->Register(model);
}

void
G4VisManager::RegisterModelFactory(G4TrajDrawModelFactory* factory) 
{
  fpTrajDrawModelMgr->Register(factory);
}

void G4VisManager::RegisterModel(G4VFilter<G4VTrajectory>* model)
{
  fpTrajFilterMgr->Register(model);
}

void
G4VisManager::RegisterModelFactory(G4TrajFilterFactory* factory)
{
  fpTrajFilterMgr->Register(factory);
}

void G4VisManager::RegisterModel(G4VFilter<G4VHit>* model)
{
  fpHitFilterMgr->Register(model);
}

void
G4VisManager::RegisterModelFactory(G4HitFilterFactory* factory)
{
  fpHitFilterMgr->Register(factory);
}

void G4VisManager::RegisterModel(G4VFilter<G4VDigi>* model)
{
  fpDigiFilterMgr->Register(model);
}

void
G4VisManager::RegisterModelFactory(G4DigiFilterFactory* factory)
{
  fpDigiFilterMgr->Register(factory);
}

void G4VisManager::SelectTrajectoryModel(const G4String& model) 
{
   fpTrajDrawModelMgr->SetCurrent(model);
}

void G4VisManager::BeginDraw (const G4Transform3D& objectTransform)
{
  if (G4Threading::IsWorkerThread()) return;
  
  fDrawGroupNestingDepth++;
  if (fDrawGroupNestingDepth > 1) {
    G4Exception
      ("G4VisManager::BeginDraw",
       "visman0008", JustWarning,
       "Nesting detected. It is illegal to nest Begin/EndDraw."
       "\n Ignored");
    return;
  }
  if (IsValidView ()) {
    ClearTransientStoreIfMarked();
    fpSceneHandler -> BeginPrimitives (objectTransform);
    fIsDrawGroup = true;
  }
}

void G4VisManager::EndDraw ()
{
  if (G4Threading::IsWorkerThread()) return;
  
  fDrawGroupNestingDepth--;
  if (fDrawGroupNestingDepth != 0) {
    if (fDrawGroupNestingDepth < 0) fDrawGroupNestingDepth = 0;
    return;
  }
  if (IsValidView ()) {
    fpSceneHandler -> EndPrimitives ();
  }
  fIsDrawGroup = false;
}

void G4VisManager::BeginDraw2D (const G4Transform3D& objectTransform)
{
  if (G4Threading::IsWorkerThread()) return;
  
  fDrawGroupNestingDepth++;
  if (fDrawGroupNestingDepth > 1) {
    G4Exception
      ("G4VisManager::BeginDraw2D",
       "visman0009", JustWarning,
       "Nesting detected. It is illegal to nest Begin/EndDraw2D."
       "\n Ignored");
    return;
  }
  if (IsValidView ()) {
    ClearTransientStoreIfMarked();
    fpSceneHandler -> BeginPrimitives2D (objectTransform);
    fIsDrawGroup = true;
  }
}

void G4VisManager::EndDraw2D ()
{
  if (G4Threading::IsWorkerThread()) return;
  
  fDrawGroupNestingDepth--;
  if (fDrawGroupNestingDepth != 0) {
    if (fDrawGroupNestingDepth < 0) fDrawGroupNestingDepth = 0;
    return;
  }
  if (IsValidView ()) {
    fpSceneHandler -> EndPrimitives2D ();
  }
  fIsDrawGroup = false;
}

template <class T> void G4VisManager::DrawT
(const T& graphics_primitive, const G4Transform3D& objectTransform) {
  if (G4Threading::IsWorkerThread()) return;
  
  if (fIsDrawGroup) {
    if (objectTransform != fpSceneHandler->GetObjectTransformation()) {
      G4Exception
	("G4VSceneHandler::DrawT",
	 "visman0010", FatalException,
	 "Different transform detected in Begin/EndDraw group.");
    }
    fpSceneHandler -> AddPrimitive (graphics_primitive);
  } else {
    if (IsValidView ()) {
      ClearTransientStoreIfMarked();
      fpSceneHandler -> BeginPrimitives (objectTransform);
      fpSceneHandler -> AddPrimitive (graphics_primitive);
      fpSceneHandler -> EndPrimitives ();
    }
  }
}

template <class T> void G4VisManager::DrawT2D
(const T& graphics_primitive, const G4Transform3D& objectTransform) {
  if (G4Threading::IsWorkerThread()) return;
  
  if (fIsDrawGroup) {
    if (objectTransform != fpSceneHandler->GetObjectTransformation()) {
      G4Exception
	("G4VSceneHandler::DrawT",
	 "visman0011", FatalException,
	 "Different transform detected in Begin/EndDraw2D group.");
    }
    fpSceneHandler -> AddPrimitive (graphics_primitive);
  } else {
    if (IsValidView ()) {
      ClearTransientStoreIfMarked();
      fpSceneHandler -> BeginPrimitives2D (objectTransform);
      fpSceneHandler -> AddPrimitive (graphics_primitive);
      fpSceneHandler -> EndPrimitives2D ();
    }
  }
}

void G4VisManager::Draw (const G4Circle& circle,
			 const G4Transform3D& objectTransform)
{
  DrawT (circle, objectTransform);
}

void G4VisManager::Draw (const G4Polyhedron& polyhedron,
			 const G4Transform3D& objectTransform)
{
  DrawT (polyhedron, objectTransform);
}

void G4VisManager::Draw (const G4Polyline& line,
			 const G4Transform3D& objectTransform)
{
  DrawT (line, objectTransform);
}

void G4VisManager::Draw (const G4Polymarker& polymarker,
			 const G4Transform3D& objectTransform)
{
  DrawT (polymarker, objectTransform);
}

void G4VisManager::Draw (const G4Square& square,
			 const G4Transform3D& objectTransform)
{
  DrawT (square, objectTransform);
}

void G4VisManager::Draw (const G4Text& text,
			 const G4Transform3D& objectTransform)
{
  DrawT (text, objectTransform);
}

void G4VisManager::Draw2D (const G4Circle& circle,
			   const G4Transform3D& objectTransform)
{
  DrawT2D (circle, objectTransform);
}

void G4VisManager::Draw2D (const G4Polyhedron& polyhedron,
			   const G4Transform3D& objectTransform)
{
  DrawT2D (polyhedron, objectTransform);
}

void G4VisManager::Draw2D (const G4Polyline& line,
			   const G4Transform3D& objectTransform)
{
  DrawT2D (line, objectTransform);
}

void G4VisManager::Draw2D (const G4Polymarker& polymarker,
			   const G4Transform3D& objectTransform)
{
  DrawT2D (polymarker, objectTransform);
}

void G4VisManager::Draw2D (const G4Square& square,
			   const G4Transform3D& objectTransform)
{
  DrawT2D (square, objectTransform);
}

void G4VisManager::Draw2D (const G4Text& text,
			   const G4Transform3D& objectTransform)
{
  DrawT2D (text, objectTransform);
}

void G4VisManager::Draw (const G4VHit& hit) {
  if (G4Threading::IsWorkerThread()) return;
  
  if (fIsDrawGroup) {
    fpSceneHandler -> AddCompound (hit);
  } else {
    if (IsValidView ()) {
      ClearTransientStoreIfMarked();
      fpSceneHandler -> AddCompound (hit);
    }
  }
}

void G4VisManager::Draw (const G4VDigi& digi) {
  if (G4Threading::IsWorkerThread()) return;
  
  if (fIsDrawGroup) {
    fpSceneHandler -> AddCompound (digi);
  } else {
    if (IsValidView ()) {
      ClearTransientStoreIfMarked();
      fpSceneHandler -> AddCompound (digi);
    }
  }
}

void G4VisManager::Draw (const G4VTrajectory& traj) {
  if (G4Threading::IsWorkerThread()) return;
  
  // A trajectory needs a trajectories model to provide G4Atts, etc.
  static G4TrajectoriesModel trajectoriesModel;
  trajectoriesModel.SetCurrentTrajectory(&traj);
  G4RunManager* runManager = G4RunManagerFactory::GetMasterRunManager();
  const G4Run* currentRun  = runManager->GetCurrentRun();
  if (currentRun) {
    trajectoriesModel.SetRunID(currentRun->GetRunID());
  }
  const G4Event* currentEvent =
  G4EventManager::GetEventManager()->GetConstCurrentEvent();
  if (currentEvent) {
    trajectoriesModel.SetEventID(currentEvent->GetEventID());
  }
  if (fIsDrawGroup) {
    fpSceneHandler -> SetModel (&trajectoriesModel);
    fpSceneHandler -> AddCompound (traj);
    fpSceneHandler -> SetModel (0);
  } else {
    if (IsValidView ()) {
      ClearTransientStoreIfMarked();
      fpSceneHandler -> SetModel (&trajectoriesModel);
      fpSceneHandler -> AddCompound (traj);
      fpSceneHandler -> SetModel (0);
    }
  }
}

void G4VisManager::Draw (const G4LogicalVolume& logicalVol,
			 const G4VisAttributes& attribs,
			 const G4Transform3D& objectTransform) {
  if (G4Threading::IsWorkerThread()) return;
  
  // Find corresponding solid.
  G4VSolid* pSol = logicalVol.GetSolid ();
  Draw (*pSol, attribs, objectTransform);
}

void G4VisManager::Draw (const G4VSolid& solid,
			 const G4VisAttributes& attribs,
			 const G4Transform3D& objectTransform) {
  if (G4Threading::IsWorkerThread()) return;
  
  if (fIsDrawGroup) {
    fpSceneHandler -> PreAddSolid (objectTransform, attribs);
    solid.DescribeYourselfTo (*fpSceneHandler);
    fpSceneHandler -> PostAddSolid ();
  } else {
    if (IsValidView ()) {
      ClearTransientStoreIfMarked();
      fpSceneHandler -> PreAddSolid (objectTransform, attribs);
      solid.DescribeYourselfTo (*fpSceneHandler);
      fpSceneHandler -> PostAddSolid ();
    }
  }
}

void G4VisManager::Draw (const G4VPhysicalVolume& physicalVol,
			 const G4VisAttributes& attribs,
			 const G4Transform3D& objectTransform) {
  if (G4Threading::IsWorkerThread()) return;
  
  // Note: It is tempting to use a temporary model here, as for
  // trajectories, in order to get at the G4Atts of the physical
  // volume.  I tried it (JA).  But it's not easy to pass the
  // vis attributes.  Also other aspects of the model seem not to
  // be properly set up.  So, the idea has been abandoned for the time
  // being.  The model pointer will be null.  So when picking there
  // will be no G4Atts from this physical volume.
  //
  // If this is called from DrawHit, for example, the user may G4Atts to the
  // hit and these will be available with "/vis/scene/add/hits".
  //
  // Find corresponding logical volume and solid.
  G4LogicalVolume* pLV  = physicalVol.GetLogicalVolume ();
  G4VSolid*        pSol = pLV -> GetSolid ();
  Draw (*pSol, attribs, objectTransform);
}

void G4VisManager::DrawGeometry
(G4VPhysicalVolume* v, const G4Transform3D& t)
// Draws a geometry tree starting at the specified physical volume.
{
  auto modelingParameters = fpSceneHandler->CreateModelingParameters();
  auto depth = G4PhysicalVolumeModel::UNLIMITED;
  const G4bool useFullExtent = true;
  G4PhysicalVolumeModel aPVModel(v,depth,t,modelingParameters,useFullExtent);
  aPVModel.DescribeYourselfTo(*fpSceneHandler);
  delete modelingParameters;
}

void G4VisManager::CreateSceneHandler (const G4String& name) {
  if (!fInitialised) Initialise ();
  if (fpGraphicsSystem) {
    G4VSceneHandler* pSceneHandler =
      fpGraphicsSystem -> CreateSceneHandler (name);
    if (pSceneHandler) {
      fAvailableSceneHandlers.push_back (pSceneHandler);
      fpSceneHandler = pSceneHandler;                         // Make current.
    }
    else {
      if (fVerbosity >= errors) {
	G4warn << "ERROR in G4VisManager::CreateSceneHandler during "
	       << fpGraphicsSystem -> GetName ()
	       << " scene handler creation.\n  No action taken."
	       << G4endl;
      }
    }
  }
  else PrintInvalidPointers ();
}

void G4VisManager::CreateViewer
(const G4String& name, const G4String& XGeometry)
{

  if (!fInitialised) Initialise ();

  if (!fpSceneHandler) {
    PrintInvalidPointers ();
    return;
  }

  G4VViewer* p = fpGraphicsSystem -> CreateViewer (*fpSceneHandler, name);

  if (!p) {
    if (fVerbosity >= errors) {
      G4warn << "ERROR in G4VisManager::CreateViewer: null pointer during "
	     << fpGraphicsSystem -> GetName ()
	     << " viewer creation.\n  No action taken."
	     << G4endl;
    }
    return;
  }

  if (p -> GetViewId() < 0) {
    if (fVerbosity >= errors) {
      G4warn << "ERROR in G4VisManager::CreateViewer during "
	     << fpGraphicsSystem -> GetName ()
	     << " viewer instantiation.\n  No action taken."
	     << G4endl;
    }
    return;
  }

  // Viewer is created, now we can set geometry parameters
  // Before 12/2008, it was done in G4VViewer.cc but it did not have to be there!
    
  G4ViewParameters initialvp = p -> GetViewParameters();
  initialvp.SetXGeometryString(XGeometry); //parse string and store parameters
  p -> SetViewParameters(initialvp);
  p -> Initialise ();  // (Viewer itself may change view parameters further.)
  if (p -> GetViewId() < 0) {
    if (fVerbosity >= errors) {
      G4warn << "ERROR in G4VisManager::CreateViewer during "
	     << fpGraphicsSystem -> GetName ()
	     << " viewer initialisation.\n  No action taken."
	     << G4endl;
    }
    return;
  }

  fpViewer = p;                             // Make current.
  fpSceneHandler -> AddViewerToList (fpViewer);
  fpSceneHandler -> SetCurrentViewer (fpViewer);
  if (fVerbosity >= confirmations) {
    G4cout << "G4VisManager::CreateViewer: new viewer created."
	   << G4endl;
  }

  const G4ViewParameters& vp = fpViewer->GetViewParameters();
  if (fVerbosity >= parameters) {
    G4cout << " view parameters are:\n  " << vp << G4endl;
  }

  if (vp.IsCulling () && vp.IsCullingInvisible ()) {
    static G4bool warned = false;
    if (fVerbosity >= confirmations) {
      if (!warned) {
	G4cout <<
  "NOTE: objects with visibility flag set to \"false\""
  " will not be drawn!"
  "\n  \"/vis/viewer/set/culling global false\" to Draw such objects."
  "\n  Also see other \"/vis/viewer/set\" commands."
	       << G4endl;
	warned = true;
      }
    }
  }
  if (vp.IsCullingCovered ()) {
    static G4bool warned = false;
    if (fVerbosity >= warnings) {
      if (!warned) {
	G4warn <<
  "WARNING: covered objects in solid mode will not be rendered!"
  "\n  \"/vis/viewer/set/culling coveredDaughters false\" to reverse this."
  "\n  Also see other \"/vis/viewer/set\" commands."
	       << G4endl;
	warned = true;
      }
    }
  }
}

void G4VisManager::GeometryHasChanged () {
  if (fVerbosity >= confirmations) {
    G4cout << "G4VisManager::GeometryHasChanged() called." << G4endl;
  }

  // Change the world...
  G4VPhysicalVolume* pWorld =
  G4TransportationManager::GetTransportationManager ()
  -> GetNavigatorForTracking () -> GetWorldVolume ();
  if (!pWorld) {
    if (fVerbosity >= warnings) {
      G4warn << "WARNING: There is no world volume!" << G4endl;
    }
  }

  // Check scenes.
  G4SceneList& sceneList = fSceneList;
  std::size_t iScene, nScenes = sceneList.size ();
  for (iScene = 0; iScene < nScenes; ++iScene) {
    G4Scene* pScene = sceneList [iScene];
    std::vector<G4Scene::Model>& modelList = pScene -> SetRunDurationModelList ();
    if (modelList.size ()) {
      G4bool modelInvalid;
      do {  // Remove, if required, one at a time.
        modelInvalid = false;
        std::vector<G4Scene::Model>::iterator iterModel;
        for (iterModel = modelList.begin();
             iterModel != modelList.end();
             ++iterModel) {
          modelInvalid = !(iterModel->fpModel->Validate(fVerbosity>=warnings));
          if (modelInvalid) {
            // Model invalid - remove and break.
            if (fVerbosity >= warnings) {
              G4warn << "WARNING: Model \""
              << iterModel->fpModel->GetGlobalDescription ()
              << "\" is no longer valid - being removed\n  from scene \""
              << pScene -> GetName ()
              << "\". You may have to re-establish the scene."
              << G4endl;
            }
            modelList.erase (iterModel);
            break;
          }
        }
      } while (modelInvalid);

      if (modelList.size () == 0) {
        if (fVerbosity >= warnings) {
          G4warn << "WARNING: No run-duration models left in this scene \""
          << pScene -> GetName ()
          << "\"."
          << G4endl;
        }
        if (pWorld) {
          if (fVerbosity >= warnings) {
            G4warn << "  Adding current world to \""
            << pScene -> GetName ()
            << "\"."
            << G4endl;
          }
          pScene->AddRunDurationModel(new G4PhysicalVolumeModel(pWorld),fVerbosity>=warnings);
          // (The above includes a re-calculation of the extent.)
          G4UImanager::GetUIpointer () ->
          ApplyCommand (G4String("/vis/scene/notifyHandlers " + pScene->GetName()));
        }
      }
      else {
        pScene->CalculateExtent();  // Recalculate extent
        G4UImanager::GetUIpointer () ->
        ApplyCommand (G4String("/vis/scene/notifyHandlers " + pScene->GetName()));
      }
    }
  }

  // Check the manager's current scene...
  if (fpScene && fpScene -> GetRunDurationModelList ().size () == 0) {
    if (fVerbosity >= warnings) {
      G4warn << "WARNING: The current scene \""
      << fpScene -> GetName ()
      << "\" has no run duration models."
      << "\n  Use \"/vis/scene/add/volume\" or create a new scene."
      << G4endl;
    }
    // Clean up
    if (fpSceneHandler) {
      fpSceneHandler->ClearTransientStore();
      fpSceneHandler->ClearStore();
      if (fpViewer) {
        fpViewer->NeedKernelVisit();
        fpViewer->SetView();
        fpViewer->ClearView();
        fpViewer->FinishView();
      }
    }
  }
}

void G4VisManager::NotifyHandlers () {

  if (fVerbosity >= confirmations) {
    G4cout << "G4VisManager::NotifyHandler() called." << G4endl;
  }

  if (IsValidView()) {

    // Check scenes.
    G4SceneList& sceneList = fSceneList;
    std::size_t iScene, nScenes = sceneList.size ();
    for (iScene = 0; iScene < nScenes; ++iScene) {
      G4Scene* pScene = sceneList [iScene];
      std::vector<G4Scene::Model>& modelList = pScene -> SetRunDurationModelList ();

      if (modelList.size ()) {
        pScene->CalculateExtent();
        G4UImanager::GetUIpointer () ->
        ApplyCommand (G4String("/vis/scene/notifyHandlers " + pScene->GetName()));
      }
    }

    // Check the manager's current scene...
    if (fpScene && fpScene -> GetRunDurationModelList ().size () == 0) {
      if (fVerbosity >= warnings) {
        G4warn << "WARNING: The current scene \""
        << fpScene -> GetName ()
        << "\" has no run duration models."
        << "\n  Use \"/vis/scene/add/volume\" or create a new scene."
        << G4endl;
      }
      fpSceneHandler->ClearTransientStore();
      fpSceneHandler->ClearStore();
      fpViewer->NeedKernelVisit();
      fpViewer->SetView();
      fpViewer->ClearView();
      fpViewer->FinishView();
    }
  }
}

G4bool G4VisManager::FilterTrajectory(const G4VTrajectory& trajectory)
{
  return fpTrajFilterMgr->Accept(trajectory);
}   

G4bool G4VisManager::FilterHit(const G4VHit& hit)
{
  return fpHitFilterMgr->Accept(hit);
}   

G4bool G4VisManager::FilterDigi(const G4VDigi& digi)
{
  return fpDigiFilterMgr->Accept(digi);
}   

void G4VisManager::DispatchToModel(const G4VTrajectory& trajectory)
{
  G4bool visible(true);

  // See if trajectory passes filter
  G4bool passed = FilterTrajectory(trajectory);

  if (!passed) {
    // Draw invisible trajectory if trajectory failed filter and
    // are filtering in soft mode
    if (fpTrajFilterMgr->GetMode() == FilterMode::Soft) visible = false;
    else {return;}
  }

  // Go on to draw trajectory
  assert (0 != fpTrajDrawModelMgr);

  const G4VTrajectoryModel* trajectoryModel = CurrentTrajDrawModel();

  assert (0 != trajectoryModel); // Should exist

  if (IsValidView()) {
      trajectoryModel->Draw(trajectory, visible);
  }
}

void G4VisManager::RegisterRunDurationUserVisAction
(const G4String& name,
 G4VUserVisAction* pVisAction,
 const G4VisExtent& extent) {
  fRunDurationUserVisActions.push_back(UserVisAction(name,pVisAction));
  if (extent.GetExtentRadius() > 0.) {
    fUserVisActionExtents[pVisAction] = extent;
  } else {
    if (fVerbosity >= warnings) {
      G4warn <<
	"WARNING: No extent set for user vis action \"" << name << "\"."
	     << G4endl;
    }
  }
  if (fVerbosity >= confirmations) {
    G4cout
    << "Run duration user vis action \"" << name << "\" registered"
    << G4endl;
  }
}

void G4VisManager::RegisterEndOfEventUserVisAction
(const G4String& name,
 G4VUserVisAction* pVisAction,
 const G4VisExtent& extent) {
  fEndOfEventUserVisActions.push_back(UserVisAction(name,pVisAction));
  if (extent.GetExtentRadius() > 0.) {
    fUserVisActionExtents[pVisAction] = extent;
  } else {
    if (fVerbosity >= warnings) {
      G4warn <<
	"WARNING: No extent set for user vis action \"" << name << "\"."
	     << G4endl;
    }
  }
  if (fVerbosity >= confirmations) {
    G4cout
    << "End of event user vis action \"" << name << "\" registered"
    << G4endl;
  }
}

void G4VisManager::RegisterEndOfRunUserVisAction
(const G4String& name,
 G4VUserVisAction* pVisAction,
 const G4VisExtent& extent) {
  fEndOfRunUserVisActions.push_back(UserVisAction(name,pVisAction));
  if (extent.GetExtentRadius() > 0.) {
    fUserVisActionExtents[pVisAction] = extent;
  } else {
    if (fVerbosity >= warnings) {
      G4warn <<
	"WARNING: No extent set for user vis action \"" << name << "\"."
	     << G4endl;
    }
  }
  if (fVerbosity >= confirmations) {
    G4cout
    << "End of run user vis action \"" << name << "\" registered"
    << G4endl;
  }
}

void G4VisManager::SetCurrentScene (G4Scene* pScene) {
  if (pScene != fpScene) {
    // A change of scene.  Therefore reset transients drawn flags.  All
    // memory of previous transient proceessing thereby erased...
    ResetTransientsDrawnFlags();
  }
  fpScene = pScene;
}

void G4VisManager::SetCurrentGraphicsSystem (G4VGraphicsSystem* pSystem) {
  fpGraphicsSystem = pSystem;
  if (fVerbosity >= confirmations) {
    G4cout << "G4VisManager::SetCurrentGraphicsSystem: system now "
	   << pSystem -> GetName () << G4endl;
  }
  // If current scene handler is of same graphics system, leave unchanged.
  // Else find the most recent scene handler of same graphics system.
  // Or clear pointers.
  if (!(fpSceneHandler && fpSceneHandler -> GetGraphicsSystem () == pSystem)) {
    const G4SceneHandlerList& sceneHandlerList = fAvailableSceneHandlers;
    G4int nSH = (G4int)sceneHandlerList.size ();  // No. of scene handlers.
    G4int iSH;
    for (iSH = nSH - 1; iSH >= 0; iSH--) {
      if (sceneHandlerList [iSH] -> GetGraphicsSystem () == pSystem) break;
    }
    if (iSH >= 0) {
      fpSceneHandler = sceneHandlerList [iSH];
      if (fVerbosity >= confirmations) {
	G4cout << "  Scene Handler now "
	       << fpSceneHandler -> GetName () << G4endl;
      }
      if (fpScene != fpSceneHandler -> GetScene ()) {
	fpScene = fpSceneHandler -> GetScene ();
	if (fVerbosity >= confirmations) {
	  G4cout << "  Scene now \""
		 << fpScene -> GetName () << "\"" << G4endl;
	}
      }
      const G4ViewerList& viewerList = fpSceneHandler -> GetViewerList ();
      if (viewerList.size ()) {
	fpViewer = viewerList [0];
	if (fVerbosity >= confirmations) {
	  G4cout << "  Viewer now " << fpViewer -> GetName () << G4endl;
	}
      }
      else {
	fpViewer = 0;
      }
    }
    else {
      fpSceneHandler = 0;
      fpViewer = 0;
    }
  }
}

void G4VisManager::SetCurrentSceneHandler (G4VSceneHandler* pSceneHandler) {
  fpSceneHandler = pSceneHandler;
  if (fVerbosity >= confirmations) {
    G4cout << "G4VisManager::SetCurrentSceneHandler: scene handler now \""
	   << pSceneHandler -> GetName () << "\"" << G4endl;
  }
  if (fpScene != fpSceneHandler -> GetScene ()) {
    fpScene = fpSceneHandler -> GetScene ();
    if (fVerbosity >= confirmations) {
      G4cout << "  Scene now \""
	     << fpScene -> GetName () << "\"" << G4endl;
    }
  }
  if (fpGraphicsSystem != pSceneHandler -> GetGraphicsSystem ()) {
    fpGraphicsSystem = pSceneHandler -> GetGraphicsSystem ();
    if (fVerbosity >= confirmations) {
      G4cout << "  Graphics system now \""
	     << fpGraphicsSystem -> GetName () << "\"" << G4endl;
    }
  }
  const G4ViewerList& viewerList = fpSceneHandler -> GetViewerList ();
  std::size_t nViewers = viewerList.size ();
  if (nViewers) {
    std::size_t iViewer;
    for (iViewer = 0; iViewer < nViewers; ++iViewer) {
      if (fpViewer == viewerList [iViewer]) break;
    }
    if (iViewer >= nViewers) {
      fpViewer = viewerList [0];
      if (fVerbosity >= confirmations) {
	G4cout << "  Viewer now \"" << fpViewer -> GetName () << "\""
	       << G4endl;
      }
    }
    if (!IsValidView ()) {
      if (fVerbosity >= warnings) {
	G4warn <<
  "WARNING: Problem setting scene handler - please report circumstances."
	       << G4endl;
      }
    }
  }
  else {
    fpViewer = 0;
    if (fVerbosity >= warnings) {
      G4warn <<
	"WARNING: No viewers for this scene handler - please create one."
	     << G4endl;
    }
  }
}

void G4VisManager::SetCurrentViewer (G4VViewer* pViewer) {
  fpViewer  = pViewer;
  if (fpViewer == nullptr) {
    if (fVerbosity >= confirmations) {
      G4cout << "G4VisManager::SetCurrentViewer: current viewer pointer zeroed "
      << G4endl;
    }
    return;
  }
  if (fVerbosity >= confirmations) {
    G4cout << "G4VisManager::SetCurrentViewer: viewer now "
	   << pViewer -> GetName ()
	   << G4endl;
  }
  fpSceneHandler = fpViewer -> GetSceneHandler ();
  if (!fpSceneHandler) {
    if (fVerbosity >= warnings) {
      G4warn <<
      "WARNING: No scene handler for this viewer - please create one."
      << G4endl;
    }
    return;
  }
  // JA: I don't think we need this. Setview will be called when needed.
  // fpViewer->SetView();
  fpSceneHandler -> SetCurrentViewer (pViewer);
  fpScene = fpSceneHandler -> GetScene ();
  fpGraphicsSystem = fpSceneHandler -> GetGraphicsSystem ();
  if (!IsValidView ()) {
    if (fVerbosity >= warnings) {
      G4warn <<
	"WARNING: Problem setting viewer - please report circumstances."
	     << G4endl;
    }
  }
}

void G4VisManager::PrintAvailableGraphicsSystems
(Verbosity verbosity, std::ostream& out) const
{
  out << "Registered graphics systems are:\n";
  if (fAvailableGraphicsSystems.size ()) {
    for (const auto& gs: fAvailableGraphicsSystems) {
      const G4String& name = gs->GetName();
      const std::vector<G4String>& nicknames = gs->GetNicknames();
      if (verbosity >= warnings) {
        // Brief output
        out << "  " << name << " (";
        for (std::size_t i = 0; i < nicknames.size(); ++i) {
          if (i != 0) {
            out << ", ";
          }
          out << nicknames[i];
        }
        out << ')';
      } else {
        // Full output
        out << *gs;
      }
      out << std::endl;
    }
    out <<
    "You may choose a graphics system (driver) with a parameter of"
    "\nthe command \"/vis/open\" or \"/vis/sceneHandler/create\","
    "\nor you may omit the driver parameter and choose at run time:"
    "\n- by argument in the construction of G4VisExecutive;"
    "\n- by environment variable \"G4VIS_DEFAULT_DRIVER\";"
    "\n- by entry in \"~/.g4session\";"
    "\n- by build flags."
    "\n- Note: This feature is not allowed in batch mode."
    "\nFor further information see \"examples/basic/B1/exampleB1.cc\""
    "\nand \"vis.mac\"."
    << std::endl;
  } else {
    out << "  NONE!!!  None registered - yet!  Mmmmm!" << std::endl;
  }
}

void G4VisManager::PrintAvailableModels (Verbosity verbosity) const
{
  {
    //fpTrajDrawModelMgr->Print(G4cout);
    G4cout << "Registered model factories:" << G4endl;
    const std::vector<G4VModelFactory<G4VTrajectoryModel>*>& factoryList =
      fpTrajDrawModelMgr->FactoryList();
    if (factoryList.empty()) G4cout << "  None" << G4endl;
    else {
      std::vector<G4VModelFactory<G4VTrajectoryModel>*>::const_iterator i;
      for (i = factoryList.begin(); i != factoryList.end(); ++i) {
        (*i)->Print(G4cout);
      }
    }
    G4cout << "\nRegistered models:" << G4endl;
    const G4VisListManager<G4VTrajectoryModel>* listManager =
      fpTrajDrawModelMgr->ListManager();
    const std::map<G4String, G4VTrajectoryModel*>& modelMap =
      listManager->Map();
    if (modelMap.empty()) G4cout << "  None" << G4endl;
    else {
      std::map<G4String, G4VTrajectoryModel*>::const_iterator i;
      for (i = modelMap.begin(); i != modelMap.end(); ++i) {
	G4cout << "  " << i->second->Name();
	if (i->second == listManager->Current()) G4cout << " (Current)";
	G4cout << G4endl;
	if (verbosity >= parameters) i->second->Print(G4cout);
      }
    }
  }

  G4cout << G4endl;

  {
    //fpTrajFilterMgr->Print(G4cout);
    G4cout << "Registered filter factories:" << G4endl;
    const std::vector<G4VModelFactory<G4VFilter<G4VTrajectory> >*>&
      factoryList = fpTrajFilterMgr->FactoryList();
    if (factoryList.empty()) G4cout << "  None" << G4endl;
    else {
      std::vector<G4VModelFactory<G4VFilter<G4VTrajectory> >*>::const_iterator i;
      for (i = factoryList.begin(); i != factoryList.end(); ++i) {
        (*i)->Print(G4cout);
      }
    }

    G4cout << "\nRegistered filters:" << G4endl;
    const std::vector<G4VFilter<G4VTrajectory>*>&
      filterList = fpTrajFilterMgr->FilterList();
    if (filterList.empty()) G4cout << "  None" << G4endl;
    else {
      std::vector<G4VFilter<G4VTrajectory>*>::const_iterator i;
      for (i = filterList.begin(); i != filterList.end(); ++i) {
	G4cout << "  " << (*i)->GetName() << G4endl;
	if (verbosity >= parameters) (*i)->PrintAll(G4cout);
      }
    }
  }
}

void G4VisManager::PrintAvailableUserVisActions (Verbosity) const
{
  G4cout <<
    "You have successfully registered the following user vis actions."
	 << G4endl;
  G4cout << "Run Duration User Vis Actions:";
  if (fRunDurationUserVisActions.empty()) G4cout << " none" << G4endl;
  else {
    G4cout << G4endl;
    for (const auto& fRunDurationUserVisAction : fRunDurationUserVisActions) {
      const G4String& name = fRunDurationUserVisAction.fName;
      G4cout << "  " << name << G4endl;
    }
  }

  G4cout << "End of Event User Vis Actions:";
  if (fEndOfEventUserVisActions.empty()) G4cout << " none" << G4endl;
  else {
    G4cout << G4endl;
    for (const auto& fEndOfEventUserVisAction : fEndOfEventUserVisActions) {
      const G4String& name = fEndOfEventUserVisAction.fName;
      G4cout << "  " << name << G4endl;
    }
  }

  G4cout << "End of Run User Vis Actions:";
  if (fEndOfRunUserVisActions.empty()) G4cout << " none" << G4endl;
  else {
    G4cout << G4endl;
    for (const auto& fEndOfRunUserVisAction : fEndOfRunUserVisActions) {
      const G4String& name = fEndOfRunUserVisAction.fName;
      G4cout << "  " << name << G4endl;
    }
  }
}

void G4VisManager::PrintAvailableColours (Verbosity) const {
  G4cout <<
  "Some /vis commands (optionally) take a string to specify colour."
  "\nThey are also available in your C++ code, e.g:"
  "\n  G4Colour niceColour;  // Default - white"
  "\n  G4Colour::GetColour(\"pink\", niceColour);"
  "\n  logical->SetVisAttributes(niceColour);"
  "\nSee G4Colour.hh."
  "\nAvailable colours";
  for (const auto& i: G4Colour::GetMap()) {
    G4cout << ", " << i.first;
  }
  G4cout << G4endl;
}

void G4VisManager::PrintInvalidPointers () const {
  if (fVerbosity >= errors) {
    G4warn << "ERROR: G4VisManager::PrintInvalidPointers:";
    if (!fpGraphicsSystem) {
      G4warn << "\n null graphics system pointer.";
    }
    else {
      G4warn << "\n  Graphics system is " << fpGraphicsSystem -> GetName ()
	     << " but:";
      if (!fpScene)
	G4warn <<
	  "\n  Null scene pointer. Use \"/vis/drawVolume\" or"
	  " \"/vis/scene/create\".";
      if (!fpSceneHandler)
	G4warn <<
	  "\n  Null scene handler pointer. Use \"/vis/open\" or"
	  " \"/vis/sceneHandler/create\".";
      if (!fpViewer )
	G4warn <<
	  "\n  Null viewer pointer. Use \"/vis/viewer/create\".";
    }
    G4warn << G4endl;
  }
}


G4ThreadFunReturnType G4VisManager::G4VisSubThread(G4ThreadFunArgType p)
{
#ifdef G4MULTITHREADED
  G4VisManager* pVisManager = (G4VisManager*)p;
  G4VSceneHandler* pSceneHandler = pVisManager->GetCurrentSceneHandler();
  if (!pSceneHandler) return 0;
  G4Scene* pScene = pSceneHandler->GetScene();
  if (!pScene) return 0;
  G4VViewer* pViewer = pVisManager->GetCurrentViewer();
  if (!pViewer) return 0;

  G4UImanager::GetUIpointer()->SetUpForSpecialThread("G4VIS");

  // Set up geometry and navigation for a thread
  G4GeometryWorkspace::GetPool()->CreateAndUseWorkspace();
  G4SolidsWorkspace::GetPool()->CreateAndUseWorkspace();
  G4Navigator* navigator = G4TransportationManager::GetTransportationManager()
                             ->GetNavigatorForTracking();
  navigator->SetWorldVolume(
    G4RunManagerFactory::GetMasterRunManagerKernel()->GetCurrentWorld());

  pViewer->SwitchToVisSubThread();

  while (true) {

    G4MUTEXLOCK(&mtVisSubThreadMutex);
    std::size_t eventQueueSize = mtVisEventQueue.size();
    G4MUTEXUNLOCK(&mtVisSubThreadMutex);
    // G4cout << "Event queue size (A): " << eventQueueSize << G4endl;

    while (eventQueueSize) {

      G4MUTEXLOCK(&mtVisSubThreadMutex);
      const G4Event* event = mtVisEventQueue.front();
      G4MUTEXUNLOCK(&mtVisSubThreadMutex);

      // Here comes the event drawing
      pVisManager->SetTransientsDrawnThisEvent(false);
      pSceneHandler->SetTransientsDrawnThisEvent(false);

      // We are about to draw the event (trajectories, etc.), but first we
      // have to clear the previous event(s) if necessary.  If this event
      // needs to be drawn afresh, e.g., the first event or any event when
      // "accumulate" is not requested, the old event has to be cleared.
      // We have postponed this so that, for normal viewers like OGL, the
      // previous event(s) stay on screen until this new event comes
      // along.  For a file-writing viewer the geometry has to be drawn.
      // See, for example, G4FRSCENEHANDLER::ClearTransientStore.
      pVisManager->ClearTransientStoreIfMarked();

      // Now draw the event...
      pSceneHandler->DrawEvent(event);
      ++pVisManager->fNoOfEventsDrawnThisRun;

      // Then pop and release event
      G4MUTEXLOCK(&mtVisSubThreadMutex);
      mtVisEventQueue.pop_front();
      pVisManager->EndOfEventCleanup(event);
      eventQueueSize = mtVisEventQueue.size();
      G4MUTEXUNLOCK(&mtVisSubThreadMutex);
    }

    G4MUTEXLOCK(&mtVisSubThreadMutex);
    G4bool runInProgress = mtRunInProgress;
    G4MUTEXUNLOCK(&mtVisSubThreadMutex);
    if (!runInProgress) {
      // EndOfRun on master thread has signalled end of run.  There is
      // nothing to draw so...
      break;
    }

    // Run still in progress but nothing to draw, so wait a while.
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
  }

  // Inform viewer that we have finished all sub-thread drawing
  pViewer->DoneWithVisSubThread();
  pViewer->MovingToMasterThread();
#else
  G4ConsumeParameters(p);
#endif
  return nullptr;
}

void G4VisManager::BeginOfRun ()
{
  if (fIgnoreStateChanges) return;
  if (G4Threading::IsWorkerThread()) return;
  if (!GetConcreteInstance()) return;

  // Check if view is valid
  // Protect IsValidView() with fpSceneHandler to prevent warning messages in batch
  isValidViewForRun = fpSceneHandler && IsValidView();
  if (!isValidViewForRun) return;

  // For a fake run...
  G4RunManager* runManager = G4RunManagerFactory::GetMasterRunManager();
  G4int nEventsToBeProcessed = runManager->GetNumberOfEventsToBeProcessed();
  isFakeRun = (nEventsToBeProcessed == 0);
  if (isFakeRun) return;

  // Is the run manager a sub-event type?
  G4RunManager::RMType rmType = runManager->GetRunManagerType();
  isSubEventRunManagerType =
  (rmType == G4RunManager::subEventMasterRM) ||
  (rmType == G4RunManager::subEventWorkerRM);

  fNKeepForPostProcessingRequests = 0;
  fNKeepTheEventRequests = 0;
  fEventKeepingSuspended = false;
  fTransientsDrawnThisRun = false;
  if (fpSceneHandler) fpSceneHandler->SetTransientsDrawnThisRun(false);
  fNoOfEventsDrawnThisRun = 0;

  // Check to see if the user has created a trajectory model. If not, create
  // a default one. To avoid code duplication the following function is used
  // and its result (a const G4VTrajectoryModel*) is thrown away at this point.
  // The function is called again later when needed.
  CurrentTrajDrawModel();

  // Actions only in MT mode
  if (G4Threading::IsMultithreadedApplication()) {

    // Inform viewer that we have finished all master thread drawing for now...
    if (fpViewer) fpViewer->DoneWithMasterThread();

    // Start vis sub-thread
    G4MUTEXLOCK(&mtVisSubThreadMutex);
    mtRunInProgress = true;
    G4MUTEXUNLOCK(&mtVisSubThreadMutex);
    mtVisSubThread = new G4Thread;
    // Launch vis thread
    G4THREADCREATE(mtVisSubThread,G4VisSubThread,this);

    // Tricky things for some viewers (e.g., Qt):
    // - Launch the vis thread
    // - Wait for the vis thread to set its QThread
    // - Then move current QOpenGL context (if Qt) to this Qthread
    // - Go ahead
    if (fpViewer) fpViewer->MovingToVisSubThread();
  }
}

void G4VisManager::BeginOfEvent ()
{
  if (fIgnoreStateChanges) return;
  if (!GetConcreteInstance()) return;
  if (!isValidViewForRun) return;
  if (isFakeRun) return;

  // Some instructions that should NOT be in multithreaded version.
  // TODO: protect with G4Threading::IsMultithreadedApplication() instead?
#ifndef G4MULTITHREADED
  // These instructions are in G4VisSubThread for multithreading.
  fTransientsDrawnThisEvent = false;
  if (fpSceneHandler) fpSceneHandler->SetTransientsDrawnThisEvent(false);
#endif
}

// Here begins a sequence of functions that deal with end-of-event.
// Sequential/Serial mode:
//   EndOfEvent is invoked by a state change on the master thread:
//     EndOfEvent pulls the event from the Event Manager and calls EndOfEventKernel.
//     EndOfEventKernel draws the event and calls EndOfEventCleanup.
//     (Unless the user him/herself has requested keeping, EndOfEventKernel
//      also sets KeepTheEvent for a certain number (adjustable, default 100)
//      of events. The run manager keeps these events until the beginning of the next
//      run, so, for example, the vis manager can redraw/rebuild if required.)
// Multithreading/Tasking:
//   EndOfEvent is invoked by a state change on each worker thread:
//     EndOfEvent pulls the event from the (worker) Event Manager and calls EndOfEventKernel.
//     EndOfEventKernel pushes it to the vis queue, and returns. The events
//       are marked KeepForPostProcessing so that the worker does not delete them until
//       PostProcessingFinished in EndOfEventCleanup.
//     EndOfEventKernel also sets KeepTheEvent as above.
//     The vis sub thread pulls events from the vis queue, draws them and
//       calls EndOfEventCleanup, which calls PostProcessingFinished.
// Sub-event parallelism:
//   EndOfEvent is invoked by a state change on each worker thread, as for
//     multithreading, but actually, the event is not complete. Workers are
//     employed on sub-events, so we ignore such state changes. Only
//     G4SubEvtRunManager knows when the event is complete.
//   Events are pushed to the Vis Manager by G4SubEvtRunManager via EventReadyForVis
//      when the event is ready. EventReadyForVis calls EndOfEventKernel and
//      processsing continues as for Multithreading.

void G4VisManager::EndOfEvent ()
{
  if (fIgnoreStateChanges) return;
  if (!GetConcreteInstance()) return;
  if (!isValidViewForRun) return;
  if (isFakeRun) return;

  // For sub-event parallelism, this is not the true end of event
  if (isSubEventRunManagerType) return;

  G4AutoLock al(&visEndOfEventMutex);

  G4RunManager* runManager = G4RunManagerFactory::GetMasterRunManager();

  const G4Run* currentRun = runManager->GetCurrentRun();
  if (!currentRun) return;

  // This gets the appropriate event manager
  G4EventManager* eventManager = G4EventManager::GetEventManager();
  const G4Event* currentEvent = eventManager->GetConstCurrentEvent();
  if (!currentEvent) return;

  // Call kernel
  EndOfEventKernel(currentEvent);
}

void G4VisManager::EventReadyForVis(const G4Event* event)
// This is invoked by G4SubEvtRunManager.
// The event is passed to EndOfEventKernel.
{
  if (fIgnoreStateChanges) return;
  if (!GetConcreteInstance()) return;
  if (!isValidViewForRun) return;
  if (isFakeRun) return;

  G4AutoLock al(&visEndOfEventMutex);
  EndOfEventKernel(event);
}

void G4VisManager::EndOfEventKernel (const G4Event* currentEvent)
{
  // Note: we are still subject to the mutex lock with visEndOfEventMutex

  // Discard event if fDrawEventOnlyIfToBeKept flag is set unless the
  // user has requested the event to be kept.
  if (fDrawEventOnlyIfToBeKept) {
    if (!currentEvent->KeepTheEventFlag()) return;
  }

  if (G4Threading::IsMultithreadedApplication()) {

    // Wait if too many events in the queue.
    G4MUTEXLOCK(&mtVisSubThreadMutex);
    std::size_t eventQueueSize = mtVisEventQueue.size();
    G4MUTEXUNLOCK(&mtVisSubThreadMutex);

    G4bool eventQueueFull = false;
    while (fMaxEventQueueSize > 0 && (G4int)eventQueueSize >= fMaxEventQueueSize) {

      if (fWaitOnEventQueueFull) {

        static G4bool warned = false;
        if (!warned) {
          G4warn <<
          "WARNING: The number of events in the visualisation queue has exceeded"
          "\n  the maximum, "
          << fMaxEventQueueSize <<
          ".\n  If, during a multithreaded run, the simulation gets ahead of the"
          "\n  visualisation by more than this maximum, the simulation is delayed"
          "\n  until the vis sub-thread has drawn a few more events and removed them"
          "\n  from the queue.  You may change this maximum number of events with"
          "\n  \"/vis/multithreading/maxEventQueueSize <N>\", where N is the maximum"
          "\n  number you wish to allow.  N <= 0 means \"unlimited\"."
          "\n  Alternatively you may choose to discard events for drawing by setting"
          "\n  \"/vis/multithreading/actionOnEventQueueFull discard\"."
          "\n  To avoid visualisation altogether: \"/vis/disable\"."
          "\n  And maybe \"/tracking/storeTrajectories 0\"."
          << G4endl;
          warned = true;
        }

        // Wait a while to give event drawing time to reduce the queue...
        std::this_thread::sleep_for(std::chrono::milliseconds(100));

      } else {

        static G4bool warned = false;
        if (!warned) {
          G4warn <<
          "WARNING: The number of events in the visualisation queue has exceeded"
          "\n  the maximum, "
          << fMaxEventQueueSize <<
          ".\n  Some events have been discarded for drawing.  You may change this"
          "\n  behaviour with \"/vis/multithreading/actionOnEventQueueFull wait\"."
          "\n  To avoid visualisation altogether: \"/vis/disable\"."
          "\n  And maybe \"/tracking/storeTrajectories 0\"."
          << G4endl;
          warned = true;
        }

        eventQueueFull = true;  // Causes event to be discarded for drawing.
        break;
      }

      G4MUTEXLOCK(&mtVisSubThreadMutex);
      eventQueueSize = mtVisEventQueue.size();
      G4MUTEXUNLOCK(&mtVisSubThreadMutex);
    }

    if (!eventQueueFull) {

      if (RequiredToBeKeptForVis(currentEvent->GetEventID())) {
        currentEvent->KeepTheEvent();
        fNKeepTheEventRequests++;  // Counts number of calls to KeepTheEvent
      }

      G4MUTEXLOCK(&mtVisSubThreadMutex);

      // Keep while post processing (i.e., drawing), relinquished in EndOfEventCleanup
      // Make sure the event is not deleted by the run manager while in the vis queue
      currentEvent->KeepForPostProcessing();
      fNKeepForPostProcessingRequests++;

      // Put event on vis queue
      mtVisEventQueue.push_back(currentEvent);

      G4MUTEXUNLOCK(&mtVisSubThreadMutex);
    }

  } else {

    // Sequential mode

    // We are about to draw the event (trajectories, etc.), but first we
    // have to clear the previous event(s) if necessary.  If this event
    // needs to be drawn afresh, e.g., the first event or any event when
    // "accumulate" is not requested, the old event has to be cleared.
    // We have postponed this so that, for normal viewers like OGL, the
    // previous event(s) stay on screen until this new event comes
    // along.  For a file-writing viewer the geometry has to be drawn.
    // See, for example, G4FRSCENEHANDLER::ClearTransientStore.
    ClearTransientStoreIfMarked();

    // Keep while post processing (i.e., drawing), relinquished in EndOfEventCleanup
    currentEvent->KeepForPostProcessing();
    fNKeepForPostProcessingRequests++;

    if (RequiredToBeKeptForVis(currentEvent->GetEventID())) {
      currentEvent->KeepTheEvent();
      fNKeepTheEventRequests++;  // Counts number of calls to KeepTheEvent
    }

    // Now draw the event...
    fpSceneHandler->DrawEvent(currentEvent);
    ++fNoOfEventsDrawnThisRun;

    EndOfEventCleanup(currentEvent);
  }
}

void G4VisManager::EndOfEventCleanup(const G4Event* currentEvent)
{
  if (fpScene->GetRefreshAtEndOfEvent()) {

    fpSceneHandler->SetMarkForClearingTransientStore(true);

    // ShowView guarantees the view is flushed to the screen.  It also
    // triggers other features such as picking (if enabled) and allows
    // file-writing viewers to close the file.
    fpViewer->ShowView();
  }

  currentEvent->PostProcessingFinished();
}

G4bool G4VisManager::RequiredToBeKeptForVis (G4int eventID)
// i.e., kept by the run manager at least to the beginning of the next run.
{
  G4bool requiredToBeKept = false;

  G4RunManager* runManager = G4RunManagerFactory::GetMasterRunManager();
  G4int nEventsToBeProcessed = runManager->GetNumberOfEventsToBeProcessed();

  if (fpScene->GetRefreshAtEndOfEvent()) {  // Refreshing every event

    // Keep last event only
    if (eventID == nEventsToBeProcessed - 1) {
      requiredToBeKept = true;
    }

  } else {  //  Accumulating events

    G4int maxNumberOfKeptEvents = fpScene->GetMaxNumberOfKeptEvents();

    if (maxNumberOfKeptEvents >= 0) {  // Event keeping requested

      if (fNKeepTheEventRequests < maxNumberOfKeptEvents) {

        // Not yet reached the limit
        requiredToBeKept = true;

      } else {

        // We have already reached the limit
        fEventKeepingSuspended = true;
        static G4bool warned = false;
        if (!warned) {
          if (fVerbosity >= warnings) {
            G4warn <<
            "WARNING: G4VisManager::EndOfEvent: Automatic event keeping suspended."
            << G4endl;
            if (maxNumberOfKeptEvents > 0) {
              G4warn <<
              "\n  The number of events exceeds the maximum, "
              << maxNumberOfKeptEvents <<
              ", that may be kept by\n  the vis manager."
              << G4endl;
            }
          }
          warned = true;
        }
      }

    } else {  // Indefinite event keeping (maxNumberOfKeptEvents < 0)

      requiredToBeKept = true;

    }
  }

  return requiredToBeKept;
}

void G4VisManager::EndOfRun ()
{
  if (fIgnoreStateChanges) return;
  if (G4Threading::IsWorkerThread()) return;
  if (!isValidViewForRun) return;
  if (isFakeRun) return;
  if (fVerbosity >= warnings) PrintListOfPlots();  // Print even if disabled
  if (!GetConcreteInstance()) return;

  G4RunManager* runManager = G4RunManagerFactory::GetMasterRunManager();
  const G4Run* currentRun = runManager->GetCurrentRun();
  if (!currentRun) return;

  //  G4AutoLock al(&visEndOfRunMutex);  ???
  if (G4Threading::IsMultithreadedApplication()) {
    // Reset flag so that sub-thread exits when it has finished processing.
    G4MUTEXLOCK(&mtVisSubThreadMutex);
    mtRunInProgress = false;
    G4MUTEXUNLOCK(&mtVisSubThreadMutex);
    // Wait for sub-thread to finish.
    G4THREADJOIN(*mtVisSubThread);
    delete mtVisSubThread;
    fpViewer->SwitchToMasterThread();
  }

  if (G4Threading::IsMultithreadedApplication()) {
    G4int noOfEventsRequested = runManager->GetNumberOfEventsToBeProcessed();
    if (fNoOfEventsDrawnThisRun != noOfEventsRequested) {
      if (!fWaitOnEventQueueFull && fVerbosity >= warnings) {
        G4warn
        << "WARNING: Number of events drawn this run, "
        << fNoOfEventsDrawnThisRun << ", is different to number requested, "
        << noOfEventsRequested <<
        ".\n  (This is because you requested \"/vis/multithreading/actionOnEventQueueFull discard\".)"
        << G4endl;
      }
    }
  }

  G4int nKeptEvents = currentRun->GetNumberOfKeptEvents();
  if (fVerbosity >= warnings && nKeptEvents > 0) {
    G4warn << nKeptEvents;
    if (nKeptEvents == 1) G4warn << " event has";
    else G4warn << " events have";
    G4warn << " been kept for refreshing and/or reviewing." << G4endl;
    if (nKeptEvents != fNKeepTheEventRequests) {
      if (fNKeepTheEventRequests == 0) {
        G4warn << "No keep requests were";
      } else if (fNKeepTheEventRequests == 1) {
        G4warn << "1 keep request was";
      } else {
        G4warn << fNKeepTheEventRequests << " keep requests were";
      }
      G4warn << " made by the vis manager.";
      if (fNKeepTheEventRequests == 0) {
        G4warn <<
        "\n  The kept events are those you have asked to be kept in your user action(s).";
      } else {
        G4warn <<
        "\n  The same or further events may have been kept by you in your user action(s)."
        "\n  To turn off event keeping by the vis manager: /vis/drawOnlyToBeKeptEvents"
        "\n  or use /vis/scene/endOfEventAction <refresh|accumulate> 0";
      }
      G4warn << G4endl;
    }
    G4warn <<
  "  \"/vis/reviewKeptEvents\" to review one by one."
  "\n  To see accumulated, \"/vis/enable\", then \"/vis/viewer/flush\" or \"/vis/viewer/rebuild\"."
    << G4endl;
  }

  if (fEventKeepingSuspended && fVerbosity >= warnings) {
    G4warn <<
    "WARNING: G4VisManager::EndOfRun: Automatic event keeping was suspended."
    << G4endl;
    if (fpScene->GetMaxNumberOfKeptEvents() > 0) {
      G4warn <<
      "  The number of events in the run exceeded the maximum, "
      << fpScene->GetMaxNumberOfKeptEvents() <<
      ", that may be\n  kept by the vis manager." <<
      "\n  The number of events kept by the vis manager can be changed with"
      "\n  \"/vis/scene/endOfEventAction accumulate <N>\", where N is the"
      "\n  maximum number you wish to allow.  N < 0 means \"unlimited\"."
      << G4endl;
    }
  }

//    // if (!fpSceneHandler->GetMarkForClearingTransientStore()) {
//    // is here.  It prevents ShowView at end of run, which seems to be OK
//    // for sequential mode, but MT mode seems to need it (I have not
//    // figured out why). ???? JA ????
//    if (!fpSceneHandler->GetMarkForClearingTransientStore()) {
  if (fpScene->GetRefreshAtEndOfRun()) {
    fpSceneHandler->DrawEndOfRunModels();
    // An extra refresh for auto-refresh viewers.
    // ???? I DON'T WHY THIS IS NECESSARY ???? JA ????
    if (fpViewer->GetViewParameters().IsAutoRefresh()) {
      fpViewer->RefreshView();
    }
    // ShowView guarantees the view is flushed to the screen.  It also
    // triggers other features such picking (if enabled) and allows
    // file-writing viewers to close the file.
    fpViewer->ShowView();
    fpSceneHandler->SetMarkForClearingTransientStore(true);
  } else {
    if (fpGraphicsSystem->GetFunctionality() ==
        G4VGraphicsSystem::fileWriter) {
      if (fVerbosity >= warnings) {
        G4warn << "\"/vis/viewer/update\" to close file." << G4endl;
      }
    }
  }
//}
  fEventRefreshing = false;
}

void G4VisManager::ClearTransientStoreIfMarked(){
  // Assumes valid view.
  if (fpSceneHandler->GetMarkForClearingTransientStore()) {
    fpSceneHandler->SetMarkForClearingTransientStore(false);
    fpSceneHandler->ClearTransientStore();
  }
  // Record if transients drawn.  These local flags are only set
  // *after* ClearTransientStore.  In the code in G4VSceneHandler
  // triggered by ClearTransientStore, use these flags so that
  // event refreshing is not done too early.
  fTransientsDrawnThisEvent = fpSceneHandler->GetTransientsDrawnThisEvent();
  fTransientsDrawnThisRun = fpSceneHandler->GetTransientsDrawnThisRun();
}

void G4VisManager::ResetTransientsDrawnFlags()
{
  fTransientsDrawnThisRun = false;
  fTransientsDrawnThisEvent = false;
  G4SceneHandlerListConstIterator i;
  for (i = fAvailableSceneHandlers.begin();
       i != fAvailableSceneHandlers.end(); ++i) {
    (*i)->SetTransientsDrawnThisEvent(false);
    (*i)->SetTransientsDrawnThisRun(false);
  }
}

G4String G4VisManager::ViewerShortName (const G4String& viewerName) const {
  const G4String& viewerShortName = viewerName.substr(0, viewerName.find (' '));
  return G4StrUtil::strip_copy(viewerShortName);
}

G4VViewer* G4VisManager::GetViewer (const G4String& viewerName) const {
  G4String viewerShortName = ViewerShortName (viewerName);
  std::size_t nHandlers = fAvailableSceneHandlers.size ();
  std::size_t iHandler, iViewer;
  G4VViewer* viewer = 0;
  G4bool found = false;
  for (iHandler = 0; iHandler < nHandlers; iHandler++) {
    G4VSceneHandler* sceneHandler = fAvailableSceneHandlers [iHandler];
    const G4ViewerList& viewerList = sceneHandler -> GetViewerList ();
    for (iViewer = 0; iViewer < viewerList.size (); iViewer++) {
      viewer = viewerList [iViewer];
      if (viewerShortName == viewer -> GetShortName ()) {
	found = true;
	break;
      }
    }
    if (found) break;
  }
  if (found) return viewer;
  else return 0;
}

std::vector<G4String> G4VisManager::VerbosityGuidanceStrings;

G4String G4VisManager::VerbosityString(Verbosity verbosity) {
  G4String rs;
  switch (verbosity) {
  case         quiet: rs = "quiet (0)"; break;
  case       startup: rs = "startup (1)"; break;
  case        errors: rs = "errors (2)"; break;
  case      warnings: rs = "warnings (3)"; break;
  case confirmations: rs = "confirmations (4)"; break;
  case    parameters: rs = "parameters (5)"; break;
  case           all: rs = "all (6)"; break;
  }
  return rs;
}

void G4VisManager::PrintAvailableVerbosity(std::ostream& os)
{
  os << "Available verbosity options:";
  for (const auto& VerbosityGuidanceString : VerbosityGuidanceStrings) {
    os << '\n' << VerbosityGuidanceString;
  }
  os << "\nCurrent verbosity: " << G4VisManager::VerbosityString(fVerbosity);
  os << std::endl;
}

G4VisManager::Verbosity
G4VisManager::GetVerbosityValue(const G4String& verbosityString) {
  G4String ss = G4StrUtil::to_lower_copy(verbosityString);
  Verbosity verbosity;
  if      (ss[0] == 'q') verbosity = quiet;
  else if (ss[0] == 's') verbosity = startup;
  else if (ss[0] == 'e') verbosity = errors;
  else if (ss[0] == 'w') verbosity = warnings;
  else if (ss[0] == 'c') verbosity = confirmations;
  else if (ss[0] == 'p') verbosity = parameters;
  else if (ss[0] == 'a') verbosity = all;
  else {
    // Could be an integer
    G4int intVerbosity;
    std::istringstream is(ss);
    is >> intVerbosity;
    if (!is) {
      G4warn << "ERROR: G4VisManager::GetVerbosityValue: invalid verbosity \""
	     << verbosityString << "\"\n";
      PrintAvailableVerbosity(G4warn);
      // Return existing verbosity
      return fVerbosity;
    }
    else {
      verbosity = GetVerbosityValue(intVerbosity);
    }
  }
  return verbosity;
}

G4VisManager::Verbosity G4VisManager::GetVerbosityValue(G4int intVerbosity) {
  Verbosity verbosity;
  if      (intVerbosity < quiet) verbosity = quiet;
  else if (intVerbosity > all)   verbosity = all;
  else                           verbosity = Verbosity(intVerbosity);
  return verbosity;
}

G4VisManager::Verbosity G4VisManager::GetVerbosity () {
  return fVerbosity;
}

void G4VisManager::SetVerboseLevel (G4int intVerbosity) {
  fVerbosity = GetVerbosityValue(intVerbosity);
}

void G4VisManager::SetVerboseLevel (const G4String& verbosityString) {
  fVerbosity = GetVerbosityValue(verbosityString);
}

G4bool G4VisManager::IsValidView () {

  if (!fInitialised) Initialise ();

  static G4bool noGSPrinting = true;
  if (!fpGraphicsSystem) {
    // Limit printing - we do not want printing if the user simply does
    // not want to use graphics, e.g., in batch mode.
    if (noGSPrinting) {
      noGSPrinting = false;
      if (fVerbosity >= warnings) {
	G4warn <<
  "WARNING: G4VisManager::IsValidView(): Attempt to draw when no graphics system"
  "\n  has been instantiated.  Use \"/vis/open\" or \"/vis/sceneHandler/create\"."
  "\n  Alternatively, to avoid this message, suppress instantiation of vis"
  "\n  manager (G4VisExecutive) and ensure drawing code is executed only if"
  "\n  G4VVisManager::GetConcreteInstance() is non-zero."
        << G4endl;
      }
    }
    return false;
  }

  if ((!fpScene) || (!fpSceneHandler) || (!fpViewer)) {
    if (fVerbosity >= errors) {
      G4warn <<
	"ERROR: G4VisManager::IsValidView(): Current view is not valid."
	     << G4endl;
      PrintInvalidPointers ();
    }
    return false;
  }

  if (fpScene != fpSceneHandler -> GetScene ()) {
    if (fVerbosity >= errors) {
      G4warn << "ERROR: G4VisManager::IsValidView ():";
      if (fpSceneHandler -> GetScene ()) {
	G4warn <<
	  "\n  The current scene \""
	       << fpScene -> GetName ()
	       << "\" is not handled by"
	  "\n  the current scene handler \""
	       << fpSceneHandler -> GetName ()
	       << "\""
	  "\n  (it currently handles scene \""
	       << fpSceneHandler -> GetScene () -> GetName ()
	       << "\")."
	  "\n  Either:"
	  "\n  (a) attach it to the scene handler with"
	  "\n      /vis/sceneHandler/attach "
	       << fpScene -> GetName ()
	       <<	", or"
	  "\n  (b) create a new scene handler with "
	  "\n      /vis/sceneHandler/create <graphics-system>,"
	  "\n      in which case it should pick up the the new scene."
	       << G4endl;
      }
      else {
	G4warn << "\n  Scene handler \""
	       << fpSceneHandler -> GetName ()
	       << "\" has null scene pointer."
	  "\n  Attach a scene with /vis/sceneHandler/attach [<scene-name>]"
	       << G4endl;
      }
    }
    return false;
  }

  const G4ViewerList& viewerList = fpSceneHandler -> GetViewerList ();
  if (viewerList.size () == 0) {
    if (fVerbosity >= errors) {
      G4warn <<
	"ERROR: G4VisManager::IsValidView (): the current scene handler\n  \""
	     << fpSceneHandler -> GetName ()
	     << "\" has no viewers.  Do /vis/viewer/create."
	     << G4endl;
    }
    return false;
  }

  G4bool isValid = true;
  if (fpScene -> IsEmpty ()) {  // Add world by default if possible...
    G4bool warn(fVerbosity >= warnings);
    G4bool successful = fpScene -> AddWorldIfEmpty (warn);
    if (!successful || fpScene -> IsEmpty ()) {        // If still empty...
      if (fVerbosity >= errors) {
	G4warn << "ERROR: G4VisManager::IsValidView ():";
	G4warn <<
	  "\n  Attempt at some drawing operation when scene is empty."
	  "\n  Maybe the geometry has not yet been defined."
	  "  Try /run/initialize."
          "\n  Or use \"/vis/scene/add/extent\"."
	       << G4endl;
      }
      isValid = false;
    }
    else {
      G4UImanager::GetUIpointer()->ApplyCommand ("/vis/scene/notifyHandlers");
      if (fVerbosity >= warnings) {
	G4warn <<
	  "WARNING: G4VisManager: the scene was empty, \"world\" has been"
	  "\n  added and the scene handlers notified.";
	G4warn << G4endl;
      }
    }
  }
  return isValid;
}

void
G4VisManager::RegisterModelFactories() 
{
  if (fVerbosity >= warnings) {
    G4warn<<"G4VisManager: No model factories registered with G4VisManager."<<G4endl;
    G4warn<<"G4VisManager::RegisterModelFactories() should be overridden in derived"<<G4endl;
    G4warn<<"class. See G4VisExecutive for an example."<<G4endl;
  }
}

void G4VisManager::SetUpForAThread()
{
  // TODO: protect with G4Threading::IsMultithreadedApplication() instead?
#ifdef G4MULTITHREADED
  new G4VisStateDependent(this); 
#endif
}

void G4VisManager::IgnoreStateChanges(G4bool val)
{
  fIgnoreStateChanges = val; 
}
