// $Id: Geant4Hits.h 513 2013-04-05 14:31:53Z gaede $
//====================================================================
//  AIDA Detector description implementation
//--------------------------------------------------------------------
//
//  Author     : M.Frank
//
//====================================================================
#include "DD4hep/Printout.h"
#include "DD4hep/Primitives.h"
#include "DD4hep/InstanceCount.h"

#include "G4UserRunAction.hh"
#include "G4UserEventAction.hh"
#include "G4UserTrackingAction.hh"
#include "G4UserStackingAction.hh"
#include "G4UserSteppingAction.hh"
#include "G4UserReactionAction.hh"
#include "G4VUserPhysicsList.hh"
#include "G4VModularPhysicsList.hh"
#include "G4VUserPrimaryGeneratorAction.hh"
#include "G4VSensitiveDetector.hh"

#include "DD4hep/Handle.h"
#include "DDG4/Geant4RunAction.h"
#include "DDG4/Geant4EventAction.h"
#include "DDG4/Geant4SteppingAction.h"
#include "DDG4/Geant4TrackingAction.h"
#include "DDG4/Geant4StackingAction.h"
#include "DDG4/Geant4GeneratorAction.h"
#include "DDG4/Geant4PhysicsList.h"
#include "DDG4/Geant4SensDetAction.h"
#include "DDG4/Geant4HitCollection.h"
#include "DDG4/Geant4Kernel.h"

#include <memory>
/*
 *   DD4hep namespace declaration
 */
namespace DD4hep {

  /*
   *   Simulation namespace declaration
   */
  namespace Simulation   {

    template <typename T> struct _Seq  {
      typedef _Seq<T> Base;
      T* m_sequence;
      _Seq() : m_sequence(0) {                 }
      _Seq(T* seq)           {   _aquire(seq); }
      virtual ~_Seq()        { _release();     }
      void _aquire(T* s)  {
	InstanceCount::increment(this);
	m_sequence = s;
	m_sequence->addRef();
      }
      void _release()  {
	releasePtr(m_sequence);
	InstanceCount::decrement(this);
      }
    };

    /** @class Geant4UserRunAction
     * 
     * Concrete implementation of the Geant4 run action
     *
     * @author  M.Frank
     * @version 1.0
     */
    struct Geant4UserRunAction : public G4UserRunAction, public _Seq<Geant4RunActionSequence>    {
      /// Standard constructor
      Geant4UserRunAction(Geant4RunActionSequence* seq) : Base(seq)  {                    }
      /// Default destructor
      virtual ~Geant4UserRunAction()                           {                          }
      /// begin-of-run callback
      virtual void  BeginOfRunAction(const G4Run* run)         { m_sequence->begin(run);  }
      /// End-of-run callback
      virtual void  EndOfRunAction(const G4Run* run)           { m_sequence->end(run);    }
    };

    /** @class Geant4UserEventAction
     * 
     * Concrete implementation of the Geant4 event action
     *
     * @author  M.Frank
     * @version 1.0
     */
    struct Geant4UserEventAction : public G4UserEventAction, public _Seq<Geant4EventActionSequence> {
      /// Standard constructor
      Geant4UserEventAction(Geant4EventActionSequence* seq) : Base(seq) {                 }
      /// Default destructor
      virtual ~Geant4UserEventAction()                         {                          }
      /// begin-of-event callback
      virtual void  BeginOfEventAction(const G4Event* evt)     { m_sequence->begin(evt);  }
      /// End-of-event callback
      virtual void  EndOfEventAction(const G4Event* evt)       { m_sequence->end(evt);    }
    };

    /** @class Geant4UserTrackingAction
     * 
     * Concrete implementation of the Geant4 tracking action
     *
     * @author  M.Frank
     * @version 1.0
     */
    struct Geant4UserTrackingAction : public G4UserTrackingAction, public _Seq<Geant4TrackingActionSequence>  {
      /// Standard constructor
      Geant4UserTrackingAction(Geant4TrackingActionSequence* seq) : Base(seq)   {         }
      /// Default destructor
      virtual ~Geant4UserTrackingAction()                      {                          }
      /// Pre-track action callback
      virtual void  PreUserTrackingAction(const G4Track* trk)  { m_sequence->begin(trk);  }
      /// Post-track action callback
      virtual void  PostUserTrackingAction(const G4Track* trk) { m_sequence->end(trk);    }
    };

    /** @class Geant4UserStackingAction
     * 
     * Concrete implementation of the Geant4 stacking action sequence
     *
     * @author  M.Frank
     * @version 1.0
     */
    struct Geant4UserStackingAction : public G4UserStackingAction, public _Seq<Geant4StackingActionSequence>  {
      /// Standard constructor
      Geant4UserStackingAction(Geant4StackingActionSequence* seq) : Base(seq)   {         }
      /// Default destructor
      virtual ~Geant4UserStackingAction()                      {                          }
      /// New-stage callback
      virtual void  NewStage()                                 { m_sequence->newStage();  }
      /// Preparation callback
      virtual void  PrepareNewEvent()                          { m_sequence->prepare();   }
    };

    /** @class Geant4UserGeneratorAction
     * 
     * Concrete implementation of the Geant4 generator action
     *
     * @author  M.Frank
     * @version 1.0
     */
    struct Geant4UserGeneratorAction : public G4VUserPrimaryGeneratorAction, public _Seq<Geant4GeneratorActionSequence>    {
      /// Standard constructor
      Geant4UserGeneratorAction(Geant4GeneratorActionSequence* seq)
	: G4VUserPrimaryGeneratorAction(), Base(seq)           {                         }
      /// Default destructor
      virtual ~Geant4UserGeneratorAction()                     {                         }
      /// Generate primary particles
      virtual void GeneratePrimaries(G4Event* event)           { (*m_sequence)(event);   }
    };

    /** @class Geant4UserSteppingAction
     * 
     * Concrete implementation of the Geant4 stepping action
     *
     * @author  M.Frank
     * @version 1.0
     */
    struct Geant4UserSteppingAction : public G4UserSteppingAction, public _Seq<Geant4SteppingActionSequence>  {
      /// Standard constructor
      Geant4UserSteppingAction(Geant4SteppingActionSequence* seq) : Base(seq)   {        }
      /// Default destructor
      virtual ~Geant4UserSteppingAction()                      {                         }
      /// User stepping callback
      virtual void UserSteppingAction(const G4Step* s) { (*m_sequence)(s,fpSteppingManager); }
    };

    /** @class Geant4UserPhysicsList
     * 
     * Concrete implementation of the Geant4 user physics list
     *
     * @author  M.Frank
     * @version 1.0
     */
    struct Geant4UserPhysicsList : virtual public G4VModularPhysicsList,
				   virtual public Geant4UserPhysics,
				   virtual public _Seq<Geant4PhysicsListActionSequence>
    {
      /// Standard constructor
      Geant4UserPhysicsList(Geant4PhysicsListActionSequence* seq)
	: G4VModularPhysicsList(), Geant4UserPhysics(), Base(seq)   {                          }
      /// Default destructor
      virtual ~Geant4UserPhysicsList()   {                                                  }
      /// User construction callback
      virtual void ConstructProcess()    {  m_sequence->constructProcess(this);             }
      /// User construction callback
      virtual void ConstructParticle()   {  m_sequence->constructParticles(this);            }
      /// Enable transportation
      virtual void AddTransportation()   {  this->G4VUserPhysicsList::AddTransportation();  }
      // Register Physics Constructor 
      virtual void RegisterPhysics(G4VPhysicsConstructor* physics)
      { this->G4VModularPhysicsList::RegisterPhysics(physics);   }
    };

    struct Geant4SensDet : virtual public G4VSensitiveDetector, 
			   virtual public G4VSDFilter,
			   virtual public Geant4ActionSD,
			   virtual public _Seq<Geant4SensDetActionSequence>
    {
      /// Constructor. The detector element is identified by the name
      Geant4SensDet(const std::string& nam, Geometry::LCDD& lcdd)
	: G4VSensitiveDetector(nam), G4VSDFilter(nam), 
	  Geant4Action(0,nam), Geant4ActionSD(nam), Base()
      {
	Geant4Kernel& kernel = Geant4Kernel::access(lcdd);
	setContext(kernel.context());
	_aquire(kernel.sensitiveAction(nam));
	m_sequence->defineCollections(this);
	this->G4VSensitiveDetector::SetFilter(this);
      }

      /// Destructor
      virtual ~Geant4SensDet()        {                                 }
      /// Overload to avoid ambiguity between G4VSensitiveDetector and G4VSDFilter
      inline G4String GetName() const
      {  return this->G4VSensitiveDetector::SensitiveDetectorName;      }
      /// G4VSensitiveDetector internals: Access to the detector path name
      virtual std::string path()  const
      {  return this->G4VSensitiveDetector::GetPathName();              }
      /// G4VSensitiveDetector internals: Access to the detector path name
      virtual std::string fullPath()  const
      {  return this->G4VSensitiveDetector::GetFullPathName();          }
      /// Is the detector active?
      virtual bool isActive() const
      {  return this->G4VSensitiveDetector::isActive();                 }
      /// This is a utility method which returns the hits collection ID 
      virtual G4int GetCollectionID(G4int i)
      {  return this->G4VSensitiveDetector::GetCollectionID(i);         }
      /// Access to the readout geometry of the sensitive detector
      virtual G4VReadOutGeometry* readoutGeometry() const
      {  return this->G4VSensitiveDetector::GetROgeometry();            }
      /// Callback if the sequence should be accepted or filtered off
      virtual G4bool Accept(const G4Step* step) const
      {  return m_sequence->accept(step);                               }
      /// Method invoked at the begining of each event. 
      virtual void Initialize(G4HCofThisEvent* hce)
      {  m_sequence->begin(hce);                                        }
      /// Method invoked at the end of each event. 
      virtual void EndOfEvent(G4HCofThisEvent* hce)
      {  m_sequence->end(hce);                                          }
      /// Method for generating hit(s) using the information of G4Step object.
      virtual G4bool ProcessHits(G4Step* step,G4TouchableHistory* hist)
      {  return m_sequence->process(step,hist);                         }
      /// G4VSensitiveDetector interface: Method invoked if the event was aborted.
      virtual void clear()
      {  m_sequence->clear();                                           }
      /// Initialize the usage of a hit collection. Returns the collection identifier
      virtual size_t defineCollection(const std::string& coll)  {
	if ( coll.empty() ) {
	  except("Geant4Sensitive: No collection defined for %s [Invalid name]",c_name());
	}
	collectionName.push_back(coll);
	return collectionName.size()-1;
      }

    };
  }    // End namespace Simulation
}      // End namespace DD4hep


#include "DDG4/Factories.h"

typedef DD4hep::Simulation::Geant4SensDet Geant4SensDet;
typedef DD4hep::Simulation::Geant4SensDet Geant4tracker;
typedef DD4hep::Simulation::Geant4SensDet Geant4calorimeter;

DECLARE_GEANT4SENSITIVEDETECTOR(Geant4SensDet)
DECLARE_GEANT4SENSITIVEDETECTOR(Geant4tracker)
DECLARE_GEANT4SENSITIVEDETECTOR(Geant4calorimeter)


#include "DD4hep/LCDD.h"
#include "DDG4/Geant4DetectorConstruction.h"

#include "QGSP_BERT.hh"

using namespace std;
using namespace DD4hep;
using namespace DD4hep::Simulation;

// Geant4 include files
#include "G4RunManager.hh"

//--
#define G4VIS_USE_OPENGL
#include "G4UImanager.hh"
#include "G4UIsession.hh"
#ifdef G4VIS_USE_OPENGLX
#include "G4OpenGLImmediateX.hh"
#include "G4OpenGLStoredX.hh"
#endif
#include "G4VisManager.hh"
#include "G4VisExecutive.hh"
#include "G4UIExecutive.hh"


/// Configure the simulation
int Geant4Exec::configure(Geant4Kernel& kernel)   {
  CLHEP::HepRandom::setTheEngine(new CLHEP::RanecuEngine);
  Geometry::LCDD& lcdd = kernel.lcdd();

  // Construct the default run manager
  G4RunManager& runManager = kernel.runManager();

  // Check if the geometry was loaded
  if ( lcdd.detectors().size() <= 2 )  {
    cout << "Error, no geometry." << endl;
  }
  // Get the detector constructed
  Geant4DetectorConstruction* detector = new Geant4DetectorConstruction(lcdd);

  runManager.SetUserInitialization(detector);
  G4VUserPhysicsList* physics = 0;
  if ( kernel.physicsList(false) )
    physics = new Geant4UserPhysicsList(kernel.physicsList(false));
  else  {
    printout(INFO,"Geant4Exec","+++ Using Geant4 physics constructor QGSP_BERT");
    physics = new QGSP_BERT(); // physics from N04
  }
  runManager.SetUserInitialization(physics);
  
  // Set user generator action sequence
  if ( kernel.generatorAction(false) )  {
    Geant4UserGeneratorAction* action = new Geant4UserGeneratorAction(kernel.generatorAction(false));
    runManager.SetUserAction(action);
  }
  // Set the run action sequence
  if ( kernel.runAction(false) )  {
    Geant4UserRunAction* action = new Geant4UserRunAction(kernel.runAction(false));
    runManager.SetUserAction(action);
  }
  // Set the event action sequence
  if ( kernel.eventAction(false) )  {
    Geant4UserEventAction* action = new Geant4UserEventAction(kernel.eventAction(false));
    runManager.SetUserAction(action);
  }
  // Set the stepping action sequence
  if ( kernel.steppingAction(false) )  {
    Geant4UserSteppingAction* action = new Geant4UserSteppingAction(kernel.steppingAction(false));
    runManager.SetUserAction(action);
  }
  // Set the stacking action sequence
  if ( kernel.stackingAction(false) )  {
    Geant4UserStackingAction* action = new Geant4UserStackingAction(kernel.stackingAction(false));
    runManager.SetUserAction(action);
  }
  return 1;
}

/// Initialize the simulation
int Geant4Exec::initialize(Geant4Kernel& kernel)   {
  // Construct the default run manager
  G4RunManager& runManager = kernel.runManager();
  //
  // Initialize G4 engine
  //
  runManager.Initialize();
  return 1;
}

/// Run the simulation
int Geant4Exec::run(Geant4Kernel& )   {
  bool is_visual = false;
  string gui_setup, vis_setup, gui_type="tcsh";

  // Initialize visualization
  G4VisManager* visManager = 0;
  if ( is_visual )  {
    visManager = new G4VisExecutive();
#ifdef G4VIS_USE_OPENGLX
    //visManager->RegisterGraphicsSystem (new G4OpenGLImmediateX());
    //visManager->RegisterGraphicsSystem (new G4OpenGLStoredX());
#endif
    visManager->Initialize();
  }

  // Get the pointer to the User Interface manager  
  G4UImanager* UImanager = G4UImanager::GetUIpointer();
  G4String command = "/control/execute run.mac";
  cout << "++++++++++++++++++++++++++++ executing command:" << command << endl;
  UImanager->ApplyCommand(command);

  G4UIExecutive* ui = 0;
  if ( !gui_type.empty() ) {  // interactive mode : define UI session    
    const char* args[] = {"cmd"};
    ui = new G4UIExecutive(1,(char**)args);//,gui_type);
    if ( is_visual && !vis_setup.empty() )   {
      UImanager->ApplyCommand("/control/execute vis.mac"); 
      cout << "++++++++++++++++++++++++++++ executed vis.mac" << endl;
    }
    if (ui->IsGUI()) {
      if ( !gui_setup.empty() )  {
	UImanager->ApplyCommand("/control/execute "+gui_setup);
	cout << "++++++++++++++++++++++++++++ executed gui.mac" << endl;
      }
    }
    ui->SessionStart();
    delete ui;
  }
  // Job termination
  // Free the store: user actions, physics_list and detector_description are
  //                 owned and deleted by the run manager, so they should not
  //                 be deleted in the main() program !  
  if ( visManager ) delete visManager;
  return 1;
}

/// Run the simulation
int Geant4Exec::terminate(Geant4Kernel& )   {
  return 1;
}