//
// ********************************************************************
// * 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.          *
// ********************************************************************
//
//
// $Id: G4FastStep.cc,v 1.16 2006/06/29 21:09:32 gunter Exp $
// GEANT4 tag $Name: geant4-09-03 $
//
//---------------------------------------------------------------
//
//  G4FastStep.cc
//
//  Description:
//    Encapsulates a G4ParticleChange and insure friendly interface
//    methods to manage the primary/secondaries final state for 
//    Fast Simulation Models.
//
//  History:
//    Oct 97: Verderi && MoraDeFreitas - First Implementation.
//    Apr 98: MoraDeFreitas - G4FastStep becomes the G4ParticleChange
//                      for the Fast Simulation Process.
//
//---------------------------------------------------------------

#include "G4FastStep.hh"
#include "G4Track.hh"
#include "G4Step.hh"
#include "G4TrackFastVector.hh"
#include "G4DynamicParticle.hh"

void G4FastStep::Initialize(const G4FastTrack& fastTrack)
{
  // keeps the fastTrack reference
  fFastTrack=&fastTrack;

  // currentTrack will be used to Initialize the other data members
  const G4Track& currentTrack = *(fFastTrack->GetPrimaryTrack());

  // use base class's method at first
  G4VParticleChange::Initialize(currentTrack);

  // set Energy/Momentum etc. equal to those of the parent particle
  const G4DynamicParticle*  pParticle = currentTrack.GetDynamicParticle();
  theEnergyChange          = pParticle->GetKineticEnergy();
  theMomentumChange        = pParticle->GetMomentumDirection();
  thePolarizationChange    = pParticle->GetPolarization();
  theProperTimeChange      = pParticle->GetProperTime();

  // set Position/Time etc. equal to those of the parent track
  thePositionChange      = currentTrack.GetPosition();
  theTimeChange          = currentTrack.GetGlobalTime();

  // switch off stepping hit invokation by default:
  theSteppingControlFlag = AvoidHitInvocation;

  // event biasing weigth:
  theWeightChange        = currentTrack.GetWeight();
}  

//----------------------------------------
// -- Set the StopAndKilled signal
// -- and put kinetic energy to 0.0. in the
// -- G4ParticleChange.
//----------------------------------------
void G4FastStep::KillPrimaryTrack()
{
  SetPrimaryTrackFinalKineticEnergy(0.) ;
  ProposeTrackStatus(fStopAndKill) ;
}

//--------------------
//
//--------------------
void 
G4FastStep::
ProposePrimaryTrackFinalPosition(const G4ThreeVector &position,
                                 G4bool localCoordinates)
{
  // Compute the position coordinate in global
  // reference system if needed ...
  G4ThreeVector globalPosition = position;
  if (localCoordinates) 
    globalPosition = fFastTrack->GetInverseAffineTransformation()->
      TransformPoint(position);
  // ...and feed the globalPosition:
  thePositionChange = globalPosition;
}

void 
G4FastStep::
SetPrimaryTrackFinalPosition(const G4ThreeVector &position,
                             G4bool localCoordinates)
{
  ProposePrimaryTrackFinalPosition(position, localCoordinates);
}

//--------------------
//
//--------------------
void 
G4FastStep::
ProposePrimaryTrackFinalMomentumDirection(const G4ThreeVector &momentum,
                                          G4bool localCoordinates)
{
  // Compute the momentum in global reference
  // system if needed ...
  G4ThreeVector globalMomentum = momentum;
  if (localCoordinates)
    globalMomentum = fFastTrack->GetInverseAffineTransformation()->
      TransformAxis(momentum);
  // ...and feed the globalMomentum (ensuring unitarity)
  SetMomentumChange(globalMomentum.unit());
}

void 
G4FastStep::
SetPrimaryTrackFinalMomentum(const G4ThreeVector &momentum,
                             G4bool localCoordinates)
{
  ProposePrimaryTrackFinalMomentumDirection(momentum, localCoordinates);
}

//--------------------
//
//--------------------
void 
G4FastStep::
ProposePrimaryTrackFinalKineticEnergyAndDirection(G4double kineticEnergy,
                                                  const G4ThreeVector &direction,
                                                  G4bool localCoordinates)
{
  // Compute global direction if needed...
  G4ThreeVector globalDirection = direction;
  if (localCoordinates)
    globalDirection =fFastTrack->GetInverseAffineTransformation()-> 
      TransformAxis(direction);
  // ...and feed the globalMomentum (ensuring unitarity)
  SetMomentumChange(globalDirection.unit());
  SetPrimaryTrackFinalKineticEnergy(kineticEnergy);
}

void 
G4FastStep::
SetPrimaryTrackFinalKineticEnergyAndDirection(G4double kineticEnergy,
                                              const G4ThreeVector &direction,
                                              G4bool localCoordinates)
{
  ProposePrimaryTrackFinalKineticEnergyAndDirection(kineticEnergy, direction, localCoordinates);
}

//--------------------
//
//--------------------
void 
G4FastStep::
ProposePrimaryTrackFinalPolarization(const G4ThreeVector &polarization,
                                     G4bool localCoordinates)
{
  // Compute polarization in global system if needed:
  G4ThreeVector globalPolarization(polarization);
  if (localCoordinates)
    globalPolarization = fFastTrack->GetInverseAffineTransformation()->
      TransformAxis(globalPolarization);  
  // Feed the particle globalPolarization:
  thePolarizationChange = globalPolarization;
}

void 
G4FastStep::
SetPrimaryTrackFinalPolarization(const G4ThreeVector &polarization,
                                 G4bool localCoordinates)
{
  ProposePrimaryTrackFinalPolarization(polarization, localCoordinates);
}

//--------------------
//
//--------------------
G4Track* G4FastStep::
CreateSecondaryTrack(const G4DynamicParticle& dynamics,
                     G4ThreeVector polarization,
                     G4ThreeVector position,
                     G4double time,
                     G4bool localCoordinates     )
{
  G4DynamicParticle dummyDynamics(dynamics);
  
  // ------------------------------------------
  // Add the polarization to the dummyDynamics:
  // ------------------------------------------
  dummyDynamics.SetPolarization(polarization.x(),
                                polarization.y(),
                                polarization.z());
  
  return CreateSecondaryTrack(dummyDynamics, position, time, localCoordinates);
}

//--------------------
//
//--------------------
G4Track* G4FastStep::
CreateSecondaryTrack(const G4DynamicParticle& dynamics,
                     G4ThreeVector position,
                     G4double time,
                     G4bool localCoordinates     )
{
  // ----------------------------------------
  // Quantities in global coordinates system.
  //  
  // The allocated globalDynamics is deleted
  // by the destructor of the G4Track.
  // ----------------------------------------
  G4DynamicParticle* globalDynamics =
    new G4DynamicParticle(dynamics);
  G4ThreeVector globalPosition(position);
  
  // -----------------------------------
  // Convert to global system if needed:
  // -----------------------------------
  if (localCoordinates)
    {
      // -- Momentum Direction:
      globalDynamics->SetMomentumDirection(fFastTrack->
                                           GetInverseAffineTransformation()->
                                           TransformAxis(globalDynamics->
                                                         GetMomentumDirection()));
      // -- Polarization:
      G4ThreeVector globalPolarization;
      globalPolarization = fFastTrack->GetInverseAffineTransformation()->
        TransformAxis(globalDynamics->GetPolarization());
      globalDynamics->SetPolarization(
                                      globalPolarization.x(),
                                      globalPolarization.y(),
                                      globalPolarization.z()
                                      );
      
      // -- Position:
      globalPosition = fFastTrack->GetInverseAffineTransformation()->
        TransformPoint(globalPosition);
    }
  
  //-------------------------------------
  // Create the G4Track of the secondary:
  //-------------------------------------
  G4Track* secondary = new G4Track(
                                   globalDynamics,
                                   time,
                                   globalPosition
                                   );
  
  //-------------------------------
  // and feed the changes:
  //-------------------------------
  AddSecondary(secondary);
  
  //--------------------------------------
  // returns the pointer on the secondary:
  //--------------------------------------
  return secondary;
}

// G4FastStep should never be Initialized in this way
// but we must define it to avoid warnings.
void G4FastStep::Initialize(const G4Track&)
{
  G4Exception("G4FastStep::Initialize()", "InvalidCondition", FatalException,
              "G4FastStep can be initialised only through G4FastTrack!");
}

G4FastStep::G4FastStep()
  : G4VParticleChange()
{
  if (verboseLevel>2)
  {
    G4cerr << "G4FastStep::G4FastStep() " << G4endl;
  }
}

G4FastStep::~G4FastStep() 
{
  if (verboseLevel>2)
  {
    G4cerr << "G4FastStep::~G4FastStep() " << G4endl;
  }
}

// copy and assignment operators are implemented as "shallow copy"
G4FastStep::G4FastStep(const G4FastStep &right)
  : G4VParticleChange()
{
   *this = right;
}


G4FastStep & G4FastStep::operator=(const G4FastStep &right)
{
   if (this != &right)
   {
     G4VParticleChange::operator=(right);
     theListOfSecondaries          = right.theListOfSecondaries;
     theSizeOftheListOfSecondaries = right.theSizeOftheListOfSecondaries;
     theNumberOfSecondaries        = right.theNumberOfSecondaries;
     theStatusChange               = right.theStatusChange;
     theMomentumChange             = right.theMomentumChange;
     thePolarizationChange         = right.thePolarizationChange;
     thePositionChange             = right.thePositionChange;
     theTimeChange                 = right.theTimeChange;
     theEnergyChange               = right.theEnergyChange;
     theTrueStepLength             = right.theTrueStepLength;
     theLocalEnergyDeposit         = right.theLocalEnergyDeposit;
     theSteppingControlFlag        = right.theSteppingControlFlag;
     theWeightChange               = right.theWeightChange;
   }
   return *this;
}





G4bool G4FastStep::operator==(const G4FastStep &right) const
{
   return ((G4VParticleChange *)this == (G4VParticleChange *) &right);
}

G4bool G4FastStep::operator!=(const G4FastStep &right) const
{
   return ((G4VParticleChange *)this != (G4VParticleChange *) &right);
}

//----------------------------------------------------------------
// methods for updating G4Step 
//

G4Step* G4FastStep::UpdateStepForPostStep(G4Step* pStep)
{ 
  // A physics process always calculates the final state of the particle

  // Take note that the return type of GetMomentumChange is a
  // pointer to G4ParticleMometum. Also it is a normalized 
  // momentum vector.

  //  G4StepPoint* pPreStepPoint  = pStep->GetPreStepPoint(); 
  G4StepPoint* pPostStepPoint = pStep->GetPostStepPoint(); 
  G4Track*     aTrack  = pStep->GetTrack();
  //  G4double     mass = aTrack->GetDynamicParticle()->GetMass();
 
  // update kinetic energy and momentum direction
  pPostStepPoint->SetMomentumDirection(theMomentumChange);
  pPostStepPoint->SetKineticEnergy( theEnergyChange );

   // update polarization
  pPostStepPoint->SetPolarization( thePolarizationChange );
      
  // update position and time
  pPostStepPoint->SetPosition( thePositionChange  );
  pPostStepPoint->SetGlobalTime( theTimeChange  );
  pPostStepPoint->AddLocalTime( theTimeChange 
                                 - aTrack->GetGlobalTime());
  pPostStepPoint->SetProperTime( theProperTimeChange  );

  // update weight
  pPostStepPoint->SetWeight( theWeightChange );

  if (debugFlag) CheckIt(*aTrack);

  
  //  Update the G4Step specific attributes 
  return UpdateStepInfo(pStep);
}

G4Step* G4FastStep::UpdateStepForAtRest(G4Step* pStep)
{ 
  // A physics process always calculates the final state of the particle

  // G4StepPoint* pPreStepPoint  = pStep->GetPreStepPoint(); 
  G4StepPoint* pPostStepPoint = pStep->GetPostStepPoint(); 
  G4Track*     aTrack  = pStep->GetTrack();
  // G4double     mass = aTrack->GetDynamicParticle()->GetMass();
 
  // update kinetic energy and momentum direction
  pPostStepPoint->SetMomentumDirection(theMomentumChange);
  pPostStepPoint->SetKineticEnergy( theEnergyChange );

  // update polarization
  pPostStepPoint->SetPolarization( thePolarizationChange );
      
  // update position and time
  pPostStepPoint->SetPosition( thePositionChange  );
  pPostStepPoint->SetGlobalTime( theTimeChange  );
  pPostStepPoint->AddLocalTime( theTimeChange 
                                 - aTrack->GetGlobalTime());
  pPostStepPoint->SetProperTime( theProperTimeChange  );

  // update weight
  pPostStepPoint->SetWeight( theWeightChange );

  if (debugFlag) CheckIt(*aTrack);

  //  Update the G4Step specific attributes 
  return UpdateStepInfo(pStep);
}

//----------------------------------------------------------------
// methods for printing messages  
//

void G4FastStep::DumpInfo() const
{
// use base-class DumpInfo
  G4VParticleChange::DumpInfo();

  G4cout.precision(3);
  G4cout << "        Position - x (mm)   : " 
       << std::setw(20) << thePositionChange.x()/mm
       << G4endl; 
  G4cout << "        Position - y (mm)   : " 
       << std::setw(20) << thePositionChange.y()/mm
       << G4endl; 
  G4cout << "        Position - z (mm)   : " 
       << std::setw(20) << thePositionChange.z()/mm
       << G4endl;
  G4cout << "        Time (ns)           : " 
       << std::setw(20) << theTimeChange/ns
       << G4endl;
  G4cout << "        Proper Time (ns)    : " 
       << std::setw(20) << theProperTimeChange/ns
       << G4endl;
  G4cout << "        Momentum Direct - x : " 
       << std::setw(20) << theMomentumChange.x()
       << G4endl;
  G4cout << "        Momentum Direct - y : " 
       << std::setw(20) << theMomentumChange.y()
       << G4endl;
  G4cout << "        Momentum Direct - z : " 
       << std::setw(20) << theMomentumChange.z()
       << G4endl;
  G4cout << "        Kinetic Energy (MeV): " 
       << std::setw(20) << theEnergyChange/MeV
       << G4endl;
  G4cout << "        Polarization - x    : " 
       << std::setw(20) << thePolarizationChange.x()
       << G4endl;
  G4cout << "        Polarization - y    : " 
       << std::setw(20) << thePolarizationChange.y()
       << G4endl;
  G4cout << "        Polarization - z    : " 
       << std::setw(20) <<  thePolarizationChange.z()
       << G4endl;
}

G4bool G4FastStep::CheckIt(const G4Track& aTrack)
{
  //
  //      In the G4FastStep::CheckIt
  //      We only check a bit
  //      
  //      If the user violates the energy,
  //      We don't care, we agree.
  //
  //      But for theMomentumDirectionChange,
  //      We do pay attention.
  //      And if too large is its range,
  //      We issue an Exception.
  //
  //
  // It means, the G4FastStep::CheckIt issues an exception only for the
  // theMomentumDirectionChange which should be an unit vector
  // and it corrects it because it could cause problems for the ulterior
  // tracking.For the rest, only warning are issued.

  G4bool    itsOK = true;
  G4bool    exitWithError = false;
  G4double  accuracy;
  
  // Energy should not be larger than the initial value
  accuracy = ( theEnergyChange - aTrack.GetKineticEnergy())/MeV;
  if (accuracy > GetAccuracyForWarning()) {
    G4cout << "ERROR - G4FastStep::CheckIt()" << G4endl
           << "        The energy becomes larger than the initial value !!"
           << G4endl
           << "        Difference: " << accuracy << "[MeV] " << G4endl;
    itsOK = false;
    if (accuracy > GetAccuracyForException())  {exitWithError = true;}
  }

  G4bool itsOKforMomentum = true;
  if ( theEnergyChange >0.) {
    accuracy = std::abs(theMomentumChange.mag2()-1.0);
    if (accuracy > GetAccuracyForWarning()) {
      G4cout << "ERROR - G4FastStep::CheckIt()" << G4endl
             << "        The Momentum Change is not unit vector !!" << G4endl
             << "        Difference: " << accuracy << G4endl;
      itsOK = itsOKforMomentum = false;
      if (accuracy > GetAccuracyForException())  {exitWithError = true;}
    }
  }
  
  accuracy = (aTrack.GetGlobalTime()- theTimeChange)/ns;  
  if (accuracy > GetAccuracyForWarning()) {
    G4cout << "ERROR - G4FastStep::CheckIt()" << G4endl
           << "        The global time goes back  !!" << G4endl
           << "        Difference: " << accuracy << "[ns] " << G4endl;
    itsOK = false;
  }
  
  accuracy = (aTrack.GetProperTime() - theProperTimeChange )/ns;
  if (accuracy) {
    G4cout << "ERROR - G4FastStep::CheckIt()" << G4endl
           << "        The proper time goes back  !!" << G4endl
           << "        Difference: " <<  accuracy << "[ns] " << G4endl;
    itsOK = false;
  }
  
  if (!itsOK) { 
    G4cout << "ERROR - G4FastStep::CheckIt() " << G4endl;
    G4cout << "        Pointer : " << this << G4endl ;
    DumpInfo();
  }
  
  // Exit with error
  if (exitWithError) {
    G4Exception("G4FastStep::CheckIt()", "FatalError",
                FatalException, "Error condition in G4ParticleChange.");
  }

  //correction for Momentum only.
  if (!itsOKforMomentum) {
    G4double vmag = theMomentumChange.mag();
    theMomentumChange = (1./vmag)*theMomentumChange;
  }

  itsOK = (itsOK) && G4VParticleChange::CheckIt(aTrack); 
  return itsOK;
}
