#ifndef _tls_Geant4Manager_h_
#define _tls_Geant4Manager_h_

class G4RunManager;
class G4VUserDetectorConstruction;
class G4VUserPhysicsList;
class G4VisManager;

#include <vector>
#include <utl/Singleton.h>
#include <tls/Geant4Customization.h>

#include <tls/GlobalPhysicsList.h>


namespace tls{
  class G4VPhysicsListCustomization;
  /**
    \class Geant4Manager
    \brief class that handles multiple Geant4 runs in one offline session
    \author    Martin Maur
    \author    Javier Gonzalez

    The Geant4Manager class is the main class in the Geant4 tools
    group. It handles (as best as we can) the switching between
    different sets of Geant4 configurations. It also initializes the
    run manager, sets the seed in CLHEPs random engine, and redirects
    Geant4 output through the framework's error logger.

    To run a Geant4 module in the same sequence as another Geant4
    module, each module has to provide a class derived from
    Geant4Customization. This will contain a
    G4VPhysicsListCustomization, a G4VUserDetectorConstruction, a
    G4VUserPrimaryGeneratorAction, and optionally some user
    actions.

    An attempt was made to automate the memory handling. This did not
    work because the singleton instance is deleted at exit but it
    seems to happen _after_ something else in Geant4 is already
    deleted, giving a segmentation fault at exit. Haven't checked this
    further. As a result, each module that registers a customization
    has to call the NotifyDelete method. After this method is called
    as many times as the AddCustomization method, Geant4's run manager
    is deleted.


    \author    Martin Maur
    \author    Javier Gonzalez
    \date     04 May 2012
    \ingroup geant4
  */

  class Geant4Manager: public utl::Singleton<Geant4Manager> {

  public:
    G4RunManager* GetRunManager();

    /// Set the default physics list. This will throw an exception if the run manager has been initialized already.
    void SetDefaultPhysicsList(G4VUserPhysicsList* physicsList);

    /// Add a visualization manager. Only one visualization manager is accepted.
    void AddVisManager(G4VisManager* visManager);

    /// This is the main method a Geant4 module needs to call. It
    /// should be called in the module's Init method, to give chance
    /// to all modules to provide their own customization before doing
    /// any actual simulation.
    void AddCustomization(Geant4Customization& custom);
    /// Method to switch to one particular configuration. This method
    /// should be called from the Run method, right before calling
    /// beamOn.
    void Customize(const std::string& name);
    /// Undo the customizations introduced by the current configuration.
    void Reset();

    /// Book-keeping method. Call this to let the manager know your module does not need Geant4 anymore.
    void NotifyDelete();

    void DumpProcesses();

  private:
    Geant4Manager();
    ~Geant4Manager();

    // disable these two
    Geant4Manager(const Geant4Manager&);
    Geant4Manager operator=(const Geant4Manager&);

    void InitPhysics();

    void SetVerbosity(int verbosity);

    G4RunManager* fRunManager;

    GlobalPhysicsList* fMasterPhysicsList;
    G4VisManager* fVisManager;
    bool fPhysicsInitialized;
    unsigned int fClosedModules;
    unsigned int fVerboseLevel;

    std::map<std::string, Geant4Customization> fCustomizations;

    std::string fCurrentCustomization;

    friend class utl::Singleton<Geant4Manager>;
  };

}

#endif