///////////////////////////////////////////////////////////////////////////////
//
// Tracks photons through the PMT and decides if photoelectrons are
// created. This code tracks the photons within the PMT itself, it
// roughly follows the SNOMAN approach.
//
// Author: Phil G Jones <p.jones22@physics.ox.ac.uk>
//
// REVISION HISTORY:
//     05/11/2010 : P G Jones - New file
//     2014-08-05 : P G Jones - Update doxygen.
//     2016-10-23 : N Barros - Added a history word to the photoelectron.
//
///////////////////////////////////////////////////////////////////////////////

#ifndef __RAT_PMTOpticalModel_hh__
#define __RAT_PMTOpticalModel_hh__

#include <G4VFastSimulationModel.hh>

#include <RAT/OpticalModelBase.hh>

#include <string>
#include <vector>
#include <complex>

class G4Region;
class G4OpticalSurface;

namespace RAT
{

class PMTOpticalModel : public G4VFastSimulationModel,
                        public OpticalModelBase
{
public:
  // The constructor invoked with the model name, region of application and envelope flag.
  //
  // name: name of this model
  // region: region this model is applied in
  // params: parameters that control this models settings, index to RATDB
  // pmtVolume: PMT volume this model applies to
  // envelopeVolume: envelope volume this model applies to
  // photocathodeSurface: optical surface properties
  // mirrorSurface: optical surface properties
  PMTOpticalModel( const G4String name, G4Region* const region,
                   const std::string params, G4LogicalVolume* const pmtVolume,
                   G4LogicalVolume* const envelopeVolume,
                   G4OpticalSurface* const photocathodeSurface,
                   G4OpticalSurface* const mirrorSurface );

  // The Destructor
  ~PMTOpticalModel();

  // Returns true if this model applies to the particle type
  //
  // type: of particle this model applies to
  // Returns true if the type is an optical photon
  G4bool IsApplicable( const G4ParticleDefinition& type );

  // Returns true if this model should be invoked
  //
  // fastTrack: track to consider
  // Returns true if the fastTrack triggers this model
  G4bool ModelTrigger( const G4FastTrack & fastTrack );

  // The method that does the actual tracking
  //
  // fastTrack: track to track
  // fastStep: result of tracking
  virtual void DoIt( const G4FastTrack& fastTrack, G4FastStep& fastStep );

protected:
  // Enum of existing PMT volumes, each one also acts as an index to fPMTVolNames, so must ensure they match
  enum EPMTVolumes { eGlass, ePCVacuum, ePCAlVacuum, eAlVacuum, eDynode, eOut };

  // Track the photon
  //
  // fastTrack: photon track information
  // ipmt: PMT ID
  // position: position in local coordinates
  // direction: direction in local coordinates
  // polarisation: polarisation in local coordinates
  // time: time in global MC units
  // weight: weight of the track >0 to be alive
  void TrackPhoton( const G4FastTrack& fastTrack, const G4int ipmt,
                    G4ThreeVector& position, G4ThreeVector& direction,
                    G4ThreeVector& polarisation, G4double& time,
                    G4double& weight );

  // Track the photon across a border between volumes, this can lead to reflection, absorption, refraction etc...
  //
  // position: position in local coordinates
  // energy: energy of the photon
  // time: time in global MC units
  // previousVolume: volume the track is exiting
  // currentVolume: volume the track is entering
  // ipmt: PMT ID
  // trackID: track ID of the track
  // direction: direction in local coordinates
  // polarisation: polarisation in local coordinates
  // weight: weight of the track >0 to be alive
  bool TrackAcrossBorder( const G4FastTrack& fastTrack, const G4ThreeVector& position, const G4double energy,
                          const G4double time,
                          const EPMTVolumes previousVolume,
                          const EPMTVolumes currentVolume,
                          const G4int ipmt, const G4int trackID,
                          G4ThreeVector& direction,
                          G4ThreeVector& polarisation, G4double& weight );

  // Calculate the T and R probabilities for Fresnel interfaces
  // This expects the direction to to point towards the surface, and the normal away
  //
  // direction: direction in local coordinates
  // polarisation: polarisation in local coordinates
  // normal: normal of the surface in local coordinates
  // n1: refractive index of exit media
  // n2: refractive index of entry media
  // T: transmission probability
  // R: reflection probability
  void CalculateTRFresnel( const G4ThreeVector direction,
                           const G4ThreeVector polarisation,
                           const G4ThreeVector normal,
                           const G4double n1, const std::complex<double> n2,
                           G4double& T, G4double& R );

  // Calculate the T and R probabilites for thin film interfaces
  // This expects the direction to to point towards the surface, and the normal away
  //
  // direction: direction in local coordinates
  // polarisation: polarisation in local coordinates
  // normal: normal of the surface in local coordinates
  // n1: refractive index of exit media
  // n2: refractive index of thin film
  // n3: refractive index of entry media
  // energy: Energy of the photon
  // thickness: thickness of the film
  // T: transmission probability
  // R: reflection probability
  void CalculateTRThinFilm( const G4ThreeVector direction,
                            const G4ThreeVector polarisation,
                            const G4ThreeVector normal,
                            const G4double n1, const std::complex<double> n2,
                            const std::complex<double> n3, const G4double energy,
                            const G4double thickness,
                            G4double& T, G4double& R );

  // Helper function for CalculateTRThinFilm, input parameters described in
  // SNO+-doc-742.
  //
  // Passes back T, the transmission probability and R, the reflection
  // probability
  void ToTR( const double u, const double eta, const std::complex<double> g,
             const double abs_r12, const double abs_r23, const double abs_t12,
             const double abs_t23, const double arg_r12, const double arg_r23,
             const double exp1, double& T, double& R );

  // Returns the probability that an absorbed hit caused a pe signal
  //
  // pmtID: PMT ID
  // position: position in local coordinates
  // energy: energy of the photon
  // weight: weight of the track >0 to be alive
  virtual G4double GetSignalProbability( const G4int pmtID,
                                         const G4ThreeVector position,
                                         const G4double energy,
                                         const G4double weight );

  std::vector<std::string> fPMTVolNames; // The names of the PMT volumes, indexed by EPMTVolumes

  G4VSolid* fGlassVolume;      // Glass Volume, also pmt-envelope volume
  G4VSolid* fPCVacuumVolume;   // Upper inner volume
  G4VSolid* fPCAlVacuumVolume; // Middle inner volume
  G4VSolid* fAlVacuumVolume;   // Lower inner volume
  G4VSolid* fDynodeVolume;     // Dynode volume

  G4MaterialPropertyVector* fWaterRIndex;         // Water refractive index
  G4MaterialPropertyVector* fGlassRIndex;         // Glass refractive index
  G4MaterialPropertyVector* fPCRIndex;            // Photocathode real refractive index
  G4MaterialPropertyVector* fPCKIndex;            // Photocathode imaginary refractive index
  G4MaterialPropertyVector* fPEEscapeProbability; // Probability of Photoelectron escaping photocathode, f(energy)
  G4MaterialPropertyVector* fPCThickness;         // Photocathode thickness f(Z)
  G4MaterialPropertyVector* fAlRIndex;            // Aluminium real refractive index
  G4MaterialPropertyVector* fAlKIndex;            // Aluminium imaginary refractive index
  G4MaterialPropertyVector* fGlassGVelocity;      // Glass group velocity
  G4MaterialPropertyVector* fPCVacuumGVelocity;   // Photocathode vacuum group velocity
  G4MaterialPropertyVector* fPCAlVacuumGVelocity; // Photocathode aluminium vacuum group velocity
  G4MaterialPropertyVector* fAlVacuumGVelocity;   // Aluminium group velocity

  G4double fDynodeZTranslation;   // Position of the dynode in the PMT co-ord system
  G4double fCollectionEfficiency; // Collection efficiency of the PMT
  G4double fDynodeReflectivity;   // Reflectivity of the dynode stack
};

} //::RAT
#endif