// Written by Peter Stromberger
// based on work of Mirco Deckenhoff
// modified by Bastian Rössler


#include "SteppingAction.hh"
#include "G4Step.hh"
#include "G4VTouchable.hh"
#include "G4SteppingManager.hh"
#include "G4UnitsTable.hh"
#include "G4ParticleTypes.hh"
#include "G4ProcessManager.hh"
#include "G4VProcess.hh"
#include "G4ProcessVector.hh"
#include "EventAction.hh"
#include <string>
#include "Parameters.hh"
#include "Analysis.hh"
#include "G4RunManager.hh"
#include "Randomize.hh"
#include "G4SystemOfUnits.hh"


SteppingAction::SteppingAction(EventAction* eventAction, PrimaryGeneratorAction* p)
{
    creatorProcess = -1;
    fEventAction = eventAction;
    pGA = p;
}

SteppingAction::~SteppingAction(){}

void SteppingAction::UserSteppingAction( const G4Step * step )
{

    primDefinition = (pGA->GetGun())->GetParticleDefinition();
    primParticle = (primDefinition->GetParticleName()).c_str();

    /* ++ SteppingAction for optical photons ++ */
    if(step->GetTrack()->GetDefinition() == G4OpticalPhoton::OpticalPhotonDefinition())
    {
        G4int trackId = step->GetTrack()->GetTrackID();
        G4double stepLength = step->GetStepLength();
    
        G4String prePhysicalVolumeName = step->GetPreStepPoint()->GetPhysicalVolume()->GetName();
        G4String postPhysicalVolumeName = "";

        // PostStep does only exist if the particle is not exiting the EpoxyBox volume!
        if(step->GetTrack()->GetTrackStatus() != fStopAndKill)
            postPhysicalVolumeName = step->GetPostStepPoint()->GetPhysicalVolume()->GetName();

    
        // Kill photons at fibre surface
        // Count the reflections and refractions
    
        if(step->GetPostStepPoint()->GetStepStatus()==fGeomBoundary)
        {
            if(postPhysicalVolumeName == "EpoxyBox" && prePhysicalVolumeName.substr(0,9) == "Cladding2")
            {
                if(Parameters::GetInstance()->ProbabilityOfPhotonLossAtSurface() == 1)
                    step->GetTrack()->SetTrackStatus(fStopAndKill);

                if(Parameters::GetInstance()->ProbabilityOfPhotonLossAtSurface() < 1
                   && Parameters::GetInstance()->ProbabilityOfPhotonLossAtSurface() > 0)
                {
                    G4double randomNumber = CLHEP::RandFlat::shoot();

                    if(Parameters::GetInstance()->ProbabilityOfPhotonLossAtSurface() < randomNumber)
                    {
                        if (step->GetStepLength() > 0)
                            Analysis::GetInstance()->IncreaseReflectionsAtFibreSurface(trackId);
                    }
                    else
                        step->GetTrack()->SetTrackStatus(fStopAndKill);
                }

                if (step->GetStepLength() > 0)
                    Analysis::GetInstance()->IncreaseReflectionsAtFibreSurface(trackId);
            }
            if(postPhysicalVolumeName == "Mirror")
                Analysis::GetInstance()->IncreaseReflectionsAtMirror(trackId);
      
       
            // Retrieve status of the photon boundary process
      
      
            G4OpBoundaryProcessStatus theBoundaryStatus = Undefined;
      
            G4ProcessManager* OpManager = G4OpticalPhoton::OpticalPhoton()->GetProcessManager();
      
            if(OpManager)
            {
                G4int MAXofPostStepLoops = OpManager->GetPostStepProcessVector()->entries();
                G4ProcessVector* fPostStepDoItVector = OpManager->GetPostStepProcessVector(typeDoIt);
	
                for(G4int i = 0; i<MAXofPostStepLoops; i++)
                {
                    G4VProcess* fCurrentProcess = (*fPostStepDoItVector)[i];
                    opBoundaryProcess = dynamic_cast<G4OpBoundaryProcess*>(fCurrentProcess);
	  			
                    if(opBoundaryProcess)
                    {
                        theBoundaryStatus = opBoundaryProcess->GetStatus();
                        break;
                    }
                }
            }
      
            if(theBoundaryStatus != Undefined)
            {
                if((prePhysicalVolumeName.substr(0,9) == "Cladding1" && postPhysicalVolumeName.substr(0,9) == "Cladding2")
                   || (prePhysicalVolumeName.substr(0,9) == "Cladding2" && postPhysicalVolumeName.substr(0,9) == "Cladding1"))
                {
                    if(theBoundaryStatus == TotalInternalReflection)
                        Analysis::GetInstance()->IncreaseTotalReflectionsAtCladCladInterface(trackId);

                    if(theBoundaryStatus == FresnelReflection)
                        Analysis::GetInstance()->IncreaseFresnelReflectionsAtCladCladInterface(trackId);

                    if(theBoundaryStatus == FresnelRefraction)
                        Analysis::GetInstance()->IncreaseRefractionsAtCladCladInterface(trackId);
                }

                if ((prePhysicalVolumeName.substr(0,4) == "Core" && postPhysicalVolumeName.substr(0,9) == "Cladding1")
                    || (prePhysicalVolumeName.substr(0,9) == "Cladding1" && postPhysicalVolumeName.substr(0,9) == "Core"))
                {
                    if(theBoundaryStatus == TotalInternalReflection)
                        Analysis::GetInstance()->IncreaseTotalReflectionsAtCoreCladInterface(trackId);

                    if(theBoundaryStatus == FresnelReflection)
                        Analysis::GetInstance()->IncreaseFresnelReflectionsAtCoreCladInterface(trackId);

                    if(theBoundaryStatus == FresnelRefraction)
                        Analysis::GetInstance()->IncreaseRefractionsAtCoreCladInterface(trackId);
                }
            }
        }
        else // Check for Rayleigh scattering
            if (step->GetDeltaMomentum()[0] != 0 || step->GetDeltaMomentum()[1] != 0 || step->GetDeltaMomentum()[2] != 0)
                Analysis::GetInstance()->IncreaseRayleighScatterings(trackId);

      
        // Store length per volume

        if (prePhysicalVolumeName.substr(0,4) == "Core")
            Analysis::GetInstance()->IncreaseLengthInCore(trackId,stepLength);

        if (prePhysicalVolumeName.substr(0,9) == "Cladding1")
            Analysis::GetInstance()->IncreaseLengthInInnerCladding(trackId,stepLength);

        if (prePhysicalVolumeName.substr(0,9) == "Cladding2")
            Analysis::GetInstance()->IncreaseLengthInOuterCladding(trackId,stepLength);



        // Store photons killed by OpWLS process, if not primary particle

        if(step->GetTrack()->GetParentID() > 0 && step->GetTrack()->GetTrackStatus() == fStopAndKill && step->GetSecondary()->size() == 1)
        {
            G4int creatorProcessId = 0;

            if(step->GetTrack()->GetCreatorProcess()->GetProcessName() == "Scintillation")
                creatorProcessId = 1;

            if(step->GetTrack()->GetCreatorProcess()->GetProcessName() == "Cerenkov")
                creatorProcessId = 2;

            if(step->GetTrack()->GetCreatorProcess()->GetProcessName() == "OpWLS")
                creatorProcessId = 3;
        }



    }
    /* ++ End of SteppingAction of optical photons. ++ */


    /* ++ SteppingAction for electrons and muon ++ */
    if((step->GetTrack()->GetDefinition() == primDefinition))
    {
        G4double runID = (G4double) G4RunManager::GetRunManager()->GetCurrentRun()->GetRunID();
        G4double eventID = (G4double) G4EventManager::GetEventManager()->GetConstCurrentEvent()->GetEventID();
	
        Analysis::GetInstance()->FillPrimaryParticleTrack(runID,
                                                          eventID,
                                                          step->GetTrack()->GetPosition()[0],
                                                          step->GetTrack()->GetPosition()[1],
                                                          step->GetTrack()->GetPosition()[2]);

        G4String prePhysicalVolumeName = step->GetPreStepPoint()->GetPhysicalVolume()->GetName();
        if (prePhysicalVolumeName.substr(0,4) == "Core")
        {
            G4double edep = step->GetTotalEnergyDeposit();
            G4double stepLength = step->GetStepLength();

            fEventAction->AddCore(edep, stepLength);
        }
		
        if (prePhysicalVolumeName == "Trigger")
        {
            G4double edep = step->GetTotalEnergyDeposit();
            Analysis::GetInstance()->FillTrigger(runID,
                                                 eventID,
                                                 edep,
                                                 step->GetTrack()->GetPosition()[0],
                                                 step->GetTrack()->GetPosition()[1],
                                                 step->GetTrack()->GetPosition()[2]);
        }
    }
    /* ++ End of SteppingAction for electrons and muon ++ */
}
