//
// ********************************************************************
// * 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.          *
// ********************************************************************
//
// -------------------------------------------------------------------
//
// GEANT4 Class file
//
//
// File name:     G4VEmProcess
//
// Author:        Vladimir Ivanchenko on base of Laszlo Urban code
//
// Creation date: 01.10.2003
//
// Modifications: by V.Ivanchenko
//
// Class Description: based class for discrete and rest/discrete EM processes
//

// -------------------------------------------------------------------
//
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

#include "G4VEmProcess.hh"
#include "G4PhysicalConstants.hh"
#include "G4SystemOfUnits.hh"
#include "G4ProcessManager.hh"
#include "G4LossTableManager.hh"
#include "G4LossTableBuilder.hh"
#include "G4Step.hh"
#include "G4ParticleDefinition.hh"
#include "G4VEmModel.hh"
#include "G4DataVector.hh"
#include "G4PhysicsTable.hh"
#include "G4EmDataHandler.hh"
#include "G4PhysicsLogVector.hh"
#include "G4VParticleChange.hh"
#include "G4ProductionCutsTable.hh"
#include "G4Region.hh"
#include "G4Gamma.hh"
#include "G4Electron.hh"
#include "G4Positron.hh"
#include "G4PhysicsTableHelper.hh"
#include "G4EmBiasingManager.hh"
#include "G4EmParameters.hh"
#include "G4EmProcessSubType.hh"
#include "G4EmTableUtil.hh"
#include "G4EmUtility.hh"
#include "G4DNAModelSubType.hh"
#include "G4GenericIon.hh"
#include "G4Log.hh"
#include <iostream>

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

G4VEmProcess::G4VEmProcess(const G4String& name, G4ProcessType type):
  G4VDiscreteProcess(name, type)
{
  theParameters = G4EmParameters::Instance();
  SetVerboseLevel(1);

  // Size of tables
  minKinEnergy = 0.1*CLHEP::keV;
  maxKinEnergy = 100.0*CLHEP::TeV;

  // default lambda factor
  invLambdaFactor = 1.0/lambdaFactor;

  // particle types
  theGamma = G4Gamma::Gamma();
  theElectron = G4Electron::Electron();
  thePositron = G4Positron::Positron();

  pParticleChange = &fParticleChange;
  fParticleChange.SetSecondaryWeightByProcess(true);
  secParticles.reserve(5);

  modelManager = new G4EmModelManager();
  lManager = G4LossTableManager::Instance();
  lManager->Register(this);
  isTheMaster = lManager->IsMaster();
  G4LossTableBuilder* bld = lManager->GetTableBuilder();
  theDensityFactor = bld->GetDensityFactors();
  theDensityIdx = bld->GetCoupleIndexes();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

G4VEmProcess::~G4VEmProcess()
{
  if (isTheMaster) {
    delete theEnergyOfCrossSectionMax;
  }
  delete modelManager;
  delete biasManager;
  lManager->DeRegister(this);
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void G4VEmProcess::AddEmModel(G4int order, G4VEmModel* ptr, 
                              const G4Region* region)
{
  if(nullptr == ptr) { return; }
  G4VEmFluctuationModel* fm = nullptr;
  modelManager->AddEmModel(order, ptr, fm, region);
  ptr->SetParticleChange(pParticleChange);
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void G4VEmProcess::SetEmModel(G4VEmModel* ptr, G4int) 
{
  if(nullptr == ptr) { return; }
  if(!emModels.empty()) {
    for(auto & em : emModels) { if(em == ptr) { return; } }
  }
  emModels.push_back(ptr);
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void G4VEmProcess::PreparePhysicsTable(const G4ParticleDefinition& part)
{
  if(nullptr == particle) { SetParticle(&part); }

  if(part.GetParticleType() == "nucleus" && 
     part.GetParticleSubType() == "generic") {

    G4String pname = part.GetParticleName();
    if(pname != "deuteron" && pname != "triton" &&
       pname != "He3" && pname != "alpha" && pname != "alpha+" &&
       pname != "helium" && pname != "hydrogen") {

      particle = G4GenericIon::GenericIon();
      isIon = true;
    }
  }
  if(particle != &part) { return; }

  lManager->PreparePhysicsTable(&part, this);

  // for new run
  currentCouple = nullptr;
  preStepLambda = 0.0;
  fLambdaEnergy = 0.0;

  InitialiseProcess(particle);

  G4LossTableBuilder* bld = lManager->GetTableBuilder();
  const G4ProductionCutsTable* theCoupleTable=
    G4ProductionCutsTable::GetProductionCutsTable();
  theCutsGamma    = theCoupleTable->GetEnergyCutsVector(idxG4GammaCut);
  theCutsElectron = theCoupleTable->GetEnergyCutsVector(idxG4ElectronCut);
  theCutsPositron = theCoupleTable->GetEnergyCutsVector(idxG4PositronCut);

  // initialisation of the process  
  if(!actMinKinEnergy) { minKinEnergy = theParameters->MinKinEnergy(); }
  if(!actMaxKinEnergy) { maxKinEnergy = theParameters->MaxKinEnergy(); }

  applyCuts       = theParameters->ApplyCuts();
  lambdaFactor    = theParameters->LambdaFactor();
  invLambdaFactor = 1.0/lambdaFactor;
  theParameters->DefineRegParamForEM(this);

  // integral option may be disabled
  if(!theParameters->Integral()) { fXSType = fEmNoIntegral; }

  // prepare tables
  if(isTheMaster) {
    if(buildLambdaTable) {
      if (nullptr == theData) { theData = new G4EmDataHandler(2, particle->GetParticleName()); }
      theLambdaTable = theData->MakeTable(theLambdaTable, 0);
      bld->InitialiseBaseMaterials(theLambdaTable);
    }
    // high energy table
    if(minKinEnergyPrim < maxKinEnergy) {
      if (nullptr == theData) { theData = new G4EmDataHandler(2, particle->GetParticleName()); }
      theLambdaTablePrim = theData->MakeTable(theLambdaTablePrim, 1);
      bld->InitialiseBaseMaterials(theLambdaTablePrim);
    }
  }
  // models
  baseMat = bld->GetBaseMaterialFlag();
  numberOfModels = modelManager->NumberOfModels();
  currentModel = modelManager->GetModel(0);
  if(nullptr != lManager->AtomDeexcitation()) { 
    modelManager->SetFluoFlag(true); 
  }
  // forced biasing
  if(nullptr != biasManager) { 
    biasManager->Initialise(part, GetProcessName(), verboseLevel); 
    biasFlag = false;
  }

  theCuts =
    G4EmTableUtil::PrepareEmProcess(this, particle, secondaryParticle,
                                    modelManager, maxKinEnergy, 
                                    secID, tripletID, mainSecondaries,
                                    verboseLevel, isTheMaster);
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void G4VEmProcess::BuildPhysicsTable(const G4ParticleDefinition& part)
{
  if(nullptr == masterProc) {
    if(isTheMaster) { masterProc = this; }
    else { masterProc = static_cast<const G4VEmProcess*>(GetMasterProcess());}
  }
  G4int nModels = modelManager->NumberOfModels();
  G4bool isLocked = theParameters->IsPrintLocked();
  G4bool toBuild = (buildLambdaTable || minKinEnergyPrim < maxKinEnergy);

  G4EmTableUtil::BuildEmProcess(this, masterProc, particle, &part,
                                nModels, verboseLevel, isTheMaster,
                                isLocked, toBuild, baseMat);
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void G4VEmProcess::BuildLambdaTable()
{
  G4double scale = theParameters->MaxKinEnergy()/theParameters->MinKinEnergy();
  G4int nbin = 
    theParameters->NumberOfBinsPerDecade()*G4lrint(std::log10(scale));
  if(actBinning) { nbin = std::max(nbin, nLambdaBins); }
  scale = nbin/G4Log(scale);
  
  G4LossTableBuilder* bld = lManager->GetTableBuilder();
  G4EmTableUtil::BuildLambdaTable(this, particle, modelManager,
                                  bld, theLambdaTable, theLambdaTablePrim,
                                  minKinEnergy, minKinEnergyPrim,
                                  maxKinEnergy, scale, verboseLevel,
                                  startFromNull, splineFlag);
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void G4VEmProcess::StreamInfo(std::ostream& out, 
                  const G4ParticleDefinition& part, G4bool rst) const
{
  G4String indent = (rst ? "  " : "");
  out << std::setprecision(6);
  out << G4endl << indent << GetProcessName() << ": ";
  if (!rst) {
    out << " for " << part.GetParticleName();
  }
  if(fXSType != fEmNoIntegral)  { out << " XStype:" << fXSType; }
  if(applyCuts) { out << " applyCuts:1 "; }
  G4int subtype = GetProcessSubType();
  out << " SubType=" << subtype;
  if (subtype == fAnnihilation) {
    G4int mod = theParameters->PositronAtRestModelType();
    const G4String namp[2] = {"Simple", "Allison"};
    out << " AtRestModel:" << namp[mod];
  }
  if(biasFactor != 1.0) { out << "  BiasingFactor=" << biasFactor; }
  out << " BuildTable=" << buildLambdaTable << G4endl;
  if(buildLambdaTable) {
    if(particle == &part) { 
      for(auto & v : *theLambdaTable) {
        if(nullptr != v) {
          out << "      Lambda table from ";
          G4double emin = v->Energy(0);
          G4double emax = v->GetMaxEnergy();
          G4int nbin = G4int(v->GetVectorLength() - 1);
          if(emin > minKinEnergy) { out << "threshold "; }
          else { out << G4BestUnit(emin,"Energy"); } 
          out << " to "
              << G4BestUnit(emax,"Energy")
              << ", " << G4lrint(nbin/std::log10(emax/emin))
              << " bins/decade, spline: " 
              << splineFlag << G4endl;
          break;
        }
      }
    } else {
      out << "      Used Lambda table of " 
      << particle->GetParticleName() << G4endl;
    }
  }
  if(minKinEnergyPrim < maxKinEnergy) {
    if(particle == &part) {
      for(auto & v : *theLambdaTablePrim) {
        if(nullptr != v) {
          out << "      LambdaPrime table from "
              << G4BestUnit(v->Energy(0),"Energy") 
              << " to "
              << G4BestUnit(v->GetMaxEnergy(),"Energy")
              << " in " << v->GetVectorLength()-1
              << " bins " << G4endl;
          break;
        }
      }
    } else {
      out << "      Used LambdaPrime table of " 
               << particle->GetParticleName() << G4endl;
    }
  }
  StreamProcessInfo(out);
  modelManager->DumpModelList(out, verboseLevel);

  if(verboseLevel > 2 && buildLambdaTable) {
    out << "      LambdaTable address= " << theLambdaTable << G4endl;
    if(theLambdaTable && particle == &part) { 
      out << (*theLambdaTable) << G4endl;
    }
  }
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void G4VEmProcess::StartTracking(G4Track* track)
{
  // reset parameters for the new track
  currentParticle = track->GetParticleDefinition();
  theNumberOfInteractionLengthLeft = -1.0;
  mfpKinEnergy = DBL_MAX;
  preStepLambda = 0.0;

  if(isIon) { massRatio = proton_mass_c2/currentParticle->GetPDGMass(); }

  // forced biasing only for primary particles
  if(biasManager) {
    if(0 == track->GetParentID()) {
      // primary particle
      biasFlag = true; 
      biasManager->ResetForcedInteraction(); 
    }
  }
  for (G4int i=0; i<numberOfModels; ++i) {
    auto ptr = GetModelByIndex(i);
    ptr->StartTracking(track);
  }
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

G4double G4VEmProcess::PostStepGetPhysicalInteractionLength(
                             const G4Track& track,
                             G4double   previousStepSize,
                             G4ForceCondition* condition)
{
  *condition = NotForced;
  G4double x = DBL_MAX;

  DefineMaterial(track.GetMaterialCutsCouple());
  preStepKinEnergy = track.GetKineticEnergy();
  const G4double scaledEnergy = preStepKinEnergy*massRatio;
  SelectModel(scaledEnergy, currentCoupleIndex);

  // In models applied to ions the dynamic charge is needed
  if (isIon) { currentModel->ChargeSquareRatio(track); }
  /*
  G4cout << "PostStepGetPhysicalInteractionLength: idx= " << currentCoupleIndex
         << "  couple: " << currentCouple << G4endl;
  */
  if(!currentModel->IsActive(scaledEnergy)) { 
    theNumberOfInteractionLengthLeft = -1.0;
    currentInteractionLength = DBL_MAX;
    mfpKinEnergy = DBL_MAX;
    preStepLambda = 0.0;
    return x; 
  }
 
  // forced biasing only for primary particles
  if(biasManager) {
    if(0 == track.GetParentID()) {
      if(biasFlag && 
         biasManager->ForcedInteractionRegion((G4int)currentCoupleIndex)) {
        return biasManager->GetStepLimit((G4int)currentCoupleIndex, previousStepSize);
      }
    }
  }

  // compute mean free path

  ComputeIntegralLambda(preStepKinEnergy, track);

  // zero cross section
  if(preStepLambda <= 0.0) { 
    theNumberOfInteractionLengthLeft = -1.0;
    currentInteractionLength = DBL_MAX;

  } else {

    // non-zero cross section
    if (theNumberOfInteractionLengthLeft < 0.0) {

      // beggining of tracking (or just after DoIt of this process)
      theNumberOfInteractionLengthLeft = -G4Log( G4UniformRand() );
      theInitialNumberOfInteractionLength = theNumberOfInteractionLengthLeft; 

    } else {

      theNumberOfInteractionLengthLeft -= 
        previousStepSize/currentInteractionLength;
      theNumberOfInteractionLengthLeft = 
        std::max(theNumberOfInteractionLengthLeft, 0.0);
    }

    // new mean free path and step limit for the next step
    currentInteractionLength = 1.0/preStepLambda;
    x = theNumberOfInteractionLengthLeft * currentInteractionLength;
  }
  return x;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void G4VEmProcess::ComputeIntegralLambda(G4double e, const G4Track& track)
{
  if (fXSType == fEmNoIntegral) {
    preStepLambda = GetCurrentLambda(e, LogEkin(track));

  } else if (fXSType == fEmIncreasing) {
    if(e*invLambdaFactor < mfpKinEnergy) {
      preStepLambda = GetCurrentLambda(e, LogEkin(track));
      mfpKinEnergy = (preStepLambda > 0.0) ? e : 0.0;
    }

  } else if(fXSType == fEmDecreasing) {
    if(e < mfpKinEnergy) { 
      const G4double e1 = e*lambdaFactor;
      preStepLambda = GetCurrentLambda(e1); 
      mfpKinEnergy = e1;
    }

  } else if(fXSType == fEmOnePeak) {
    const G4double epeak = (*theEnergyOfCrossSectionMax)[currentCoupleIndex];
    if(e <= epeak) {
      if(e*invLambdaFactor < mfpKinEnergy) {
        preStepLambda = GetCurrentLambda(e, LogEkin(track));
        mfpKinEnergy = (preStepLambda > 0.0) ? e : 0.0;
      }
    } else if(e < mfpKinEnergy) { 
      const G4double e1 = std::max(epeak, e*lambdaFactor);
      preStepLambda = GetCurrentLambda(e1);
      mfpKinEnergy = e1;
    }
  } else {
    preStepLambda = GetCurrentLambda(e, LogEkin(track));
  }
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

G4VParticleChange* G4VEmProcess::PostStepDoIt(const G4Track& track,
                                              const G4Step& step)
{
  // clear number of interaction lengths in any case
  theNumberOfInteractionLengthLeft = -1.0;
  mfpKinEnergy = DBL_MAX;

  fParticleChange.InitializeForPostStep(track);

  // Do not make anything if particle is stopped, the annihilation then
  // should be performed by the AtRestDoIt!
  if (track.GetTrackStatus() == fStopButAlive) { return &fParticleChange; }

  const G4double finalT = track.GetKineticEnergy();

  // forced process - should happen only once per track
  if(biasFlag) {
    if(biasManager->ForcedInteractionRegion((G4int)currentCoupleIndex)) {
      biasFlag = false;
    }
  }

  // check active and select model
  const G4double scaledEnergy = finalT*massRatio;
  SelectModel(scaledEnergy, currentCoupleIndex);
  if(!currentModel->IsActive(scaledEnergy)) { return &fParticleChange; }

  // Integral approach
  if (fXSType != fEmNoIntegral) {
    const G4double logFinalT = 
      track.GetDynamicParticle()->GetLogKineticEnergy();
    const G4double lx = std::max(GetCurrentLambda(finalT, logFinalT), 0.0);
#ifdef G4VERBOSE
    if(preStepLambda < lx && 1 < verboseLevel) {
      G4cout << "WARNING: for " << currentParticle->GetParticleName() 
             << " and " << GetProcessName() << " E(MeV)= " << finalT/MeV
             << " preLambda= " << preStepLambda 
             << " < " << lx << " (postLambda) " << G4endl;  
    }
#endif
    // if false interaction then use new cross section value
    // if both values are zero - no interaction
    if(preStepLambda*G4UniformRand() >= lx) {
      return &fParticleChange;
    }
  }

  // define new weight for primary and secondaries
  G4double weight = fParticleChange.GetParentWeight();
  if(weightFlag) { 
    weight /= biasFactor; 
    fParticleChange.ProposeWeight(weight);
  }
  
#ifdef G4VERBOSE
  if(1 < verboseLevel) {
    G4cout << "G4VEmProcess::PostStepDoIt: Sample secondary; E= "
           << finalT/MeV
           << " MeV; model= (" << currentModel->LowEnergyLimit()
           << ", " <<  currentModel->HighEnergyLimit() << ")"
           << G4endl;
  }
#endif

  // sample secondaries
  secParticles.clear();
  currentModel->SampleSecondaries(&secParticles, 
                                  currentCouple, 
                                  track.GetDynamicParticle(),
                                  (*theCuts)[currentCoupleIndex]);

  G4int num0 = (G4int)secParticles.size();

  // splitting or Russian roulette
  if(biasManager) {
    if(biasManager->SecondaryBiasingRegion((G4int)currentCoupleIndex)) {
      G4double eloss = 0.0;
      weight *= biasManager->ApplySecondaryBiasing(
        secParticles, track, currentModel, &fParticleChange, eloss, 
        (G4int)currentCoupleIndex, (*theCuts)[currentCoupleIndex],
        step.GetPostStepPoint()->GetSafety());
      if(eloss > 0.0) {
        eloss += fParticleChange.GetLocalEnergyDeposit();
        fParticleChange.ProposeLocalEnergyDeposit(eloss);
      }
    }
  }

  // save secondaries
  G4int num = (G4int)secParticles.size();
  if(num > 0) {

    fParticleChange.SetNumberOfSecondaries(num);
    G4double edep = fParticleChange.GetLocalEnergyDeposit();
    G4double time = track.GetGlobalTime();

    G4int n1(0), n2(0);
    if(num0 > mainSecondaries) { 
      currentModel->FillNumberOfSecondaries(n1, n2);
    }
     
    for (G4int i=0; i<num; ++i) {
      G4DynamicParticle* dp = secParticles[i];
      if (nullptr != dp) {
        const G4ParticleDefinition* p = dp->GetParticleDefinition();
        G4double e = dp->GetKineticEnergy();
        G4bool good = true;
        if(applyCuts) {
          if (p == theGamma) {
            if (e < (*theCutsGamma)[currentCoupleIndex]) { good = false; }

          } else if (p == theElectron) {
            if (e < (*theCutsElectron)[currentCoupleIndex]) { good = false; }

          } else if (p == thePositron) {
            if (electron_mass_c2 < (*theCutsGamma)[currentCoupleIndex] &&
                e < (*theCutsPositron)[currentCoupleIndex]) {
              good = false;
              e += 2.0*electron_mass_c2;
            }
          }
          // added secondary if it is good
        }
        if (good) { 
          G4Track* t = new G4Track(dp, time, track.GetPosition());
          t->SetTouchableHandle(track.GetTouchableHandle());
          if (biasManager) {
            t->SetWeight(weight * biasManager->GetWeight(i));
          } else {
            t->SetWeight(weight);
          }
          pParticleChange->AddSecondary(t);

          // define type of secondary
          if(i < mainSecondaries) { 
            t->SetCreatorModelID(secID);
            if(GetProcessSubType() == fComptonScattering && p == theGamma) {
              t->SetCreatorModelID(_ComptonGamma);
            }
          } else if(i < mainSecondaries + n1) {
            t->SetCreatorModelID(tripletID);
          } else if(i < mainSecondaries + n1 + n2) {
            t->SetCreatorModelID(_IonRecoil);
          } else {
            if(i < num0) {
              if(p == theGamma) { 
                t->SetCreatorModelID(fluoID);
              } else {
                t->SetCreatorModelID(augerID);
              }
            } else {
              t->SetCreatorModelID(biasID);
            }
          }
          /* 
          G4cout << "Secondary(post step) has weight " << t->GetWeight() 
                 << ", Ekin= " << t->GetKineticEnergy()/MeV << " MeV "
                 << GetProcessName() << " fluoID= " << fluoID
                 << " augerID= " << augerID <<G4endl;
          */
        } else {
          delete dp;
          edep += e;
        }
      } 
    }
    fParticleChange.ProposeLocalEnergyDeposit(edep);
  }

  if(0.0 == fParticleChange.GetProposedKineticEnergy() &&
     fAlive == fParticleChange.GetTrackStatus()) {
    if(particle->GetProcessManager()->GetAtRestProcessVector()->size() > 0)
         { fParticleChange.ProposeTrackStatus(fStopButAlive); }
    else { fParticleChange.ProposeTrackStatus(fStopAndKill); }
  }

  return &fParticleChange;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

G4bool G4VEmProcess::StorePhysicsTable(const G4ParticleDefinition* part,
                                       const G4String& directory,
                                       G4bool ascii)
{
  if(!isTheMaster || part != particle) { return true; }
  if(G4EmTableUtil::StoreTable(this, part, theLambdaTable,
			       directory, "Lambda",
                               verboseLevel, ascii) &&
     G4EmTableUtil::StoreTable(this, part, theLambdaTablePrim,
			       directory, "LambdaPrim",
                               verboseLevel, ascii)) { 
     return true;
  }
  return false;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo.....

G4bool G4VEmProcess::RetrievePhysicsTable(const G4ParticleDefinition* part,
                                          const G4String& dir,
                                          G4bool ascii)
{
  if(!isTheMaster || part != particle) { return true; }
  G4bool yes = true;
  if(buildLambdaTable) {
    yes = G4EmTableUtil::RetrieveTable(this, part, theLambdaTable, dir,
                                       "Lambda", verboseLevel,
                                       ascii, splineFlag);
  }
  if(yes && minKinEnergyPrim < maxKinEnergy) {
    yes = G4EmTableUtil::RetrieveTable(this, part, theLambdaTablePrim, dir,
                                       "LambdaPrim", verboseLevel,
                                       ascii, splineFlag);
  }
  return yes;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

G4double G4VEmProcess::GetCrossSection(G4double kinEnergy,
                                       const G4MaterialCutsCouple* couple)
{
  CurrentSetup(couple, kinEnergy);
  return GetCurrentLambda(kinEnergy, G4Log(kinEnergy));
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

G4double G4VEmProcess::GetMeanFreePath(const G4Track& track,
                                       G4double,
                                       G4ForceCondition* condition)
{
  *condition = NotForced;
  return G4VEmProcess::MeanFreePath(track);
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

G4double 
G4VEmProcess::ComputeCrossSectionPerAtom(G4double kinEnergy, 
                                         G4double Z, G4double A, G4double cut)
{
  SelectModel(kinEnergy, currentCoupleIndex);
  return (currentModel) ? 
    currentModel->ComputeCrossSectionPerAtom(currentParticle, kinEnergy,
                                             Z, A, cut) : 0.0;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

G4PhysicsVector* 
G4VEmProcess::LambdaPhysicsVector(const G4MaterialCutsCouple* couple)
{
  DefineMaterial(couple);
  G4PhysicsVector* newv = new G4PhysicsLogVector(minKinEnergy, maxKinEnergy, 
                                                 nLambdaBins, splineFlag);
  return newv;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

const G4Element* G4VEmProcess::GetCurrentElement() const
{
  return (nullptr != currentModel) ?
    currentModel->GetCurrentElement(currentMaterial) : nullptr; 
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

const G4Element* G4VEmProcess::GetTargetElement() const
{
  return (nullptr != currentModel) ?
    currentModel->GetCurrentElement(currentMaterial) : nullptr;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

const G4Isotope* G4VEmProcess::GetTargetIsotope() const
{
  return (nullptr != currentModel) ?
    currentModel->GetCurrentIsotope(GetCurrentElement()) : nullptr;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void G4VEmProcess::SetCrossSectionBiasingFactor(G4double f, G4bool flag)
{
  if(f > 0.0) { 
    biasFactor = f; 
    weightFlag = flag;
    if(1 < verboseLevel) {
      G4cout << "### SetCrossSectionBiasingFactor: for " 
             << particle->GetParticleName() 
             << " and process " << GetProcessName()
             << " biasFactor= " << f << " weightFlag= " << flag 
             << G4endl; 
    }
  }
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void 
G4VEmProcess::ActivateForcedInteraction(G4double length, const G4String& r,
                                        G4bool flag)
{
  if(nullptr == biasManager) { biasManager = new G4EmBiasingManager(); }
  if(1 < verboseLevel) {
    G4cout << "### ActivateForcedInteraction: for " 
           << particle->GetParticleName() 
           << " and process " << GetProcessName()
           << " length(mm)= " << length/mm
           << " in G4Region <" << r 
           << "> weightFlag= " << flag 
           << G4endl; 
  }
  weightFlag = flag;
  biasManager->ActivateForcedInteraction(length, r);
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void
G4VEmProcess::ActivateSecondaryBiasing(const G4String& region,
                 G4double factor,
                 G4double energyLimit)
{
  if (0.0 <= factor) {

    // Range cut can be applied only for e-
    if(0.0 == factor && secondaryParticle != G4Electron::Electron())
      { return; }

    if(!biasManager) { biasManager = new G4EmBiasingManager(); }
    biasManager->ActivateSecondaryBiasing(region, factor, energyLimit);
    if(1 < verboseLevel) {
      G4cout << "### ActivateSecondaryBiasing: for "
       << " process " << GetProcessName()
       << " factor= " << factor
       << " in G4Region <" << region
       << "> energyLimit(MeV)= " << energyLimit/MeV
       << G4endl;
    }
  }
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void G4VEmProcess::SetLambdaBinning(G4int n)
{
  if(5 < n && n < 10000000) {  
    nLambdaBins = n; 
    actBinning = true;
  } else { 
    G4double e = (G4double)n;
    PrintWarning("SetLambdaBinning", e); 
  } 
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void G4VEmProcess::SetMinKinEnergy(G4double e)
{
  if(1.e-3*eV < e && e < maxKinEnergy) { 
    nLambdaBins = G4lrint(nLambdaBins*G4Log(maxKinEnergy/e)
                          /G4Log(maxKinEnergy/minKinEnergy));
    minKinEnergy = e;
    actMinKinEnergy = true;
  } else { PrintWarning("SetMinKinEnergy", e); } 
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void G4VEmProcess::SetMaxKinEnergy(G4double e)
{
  if(minKinEnergy < e && e < 1.e+6*TeV) { 
    nLambdaBins = G4lrint(nLambdaBins*G4Log(e/minKinEnergy)
                          /G4Log(maxKinEnergy/minKinEnergy));
    maxKinEnergy = e;
    actMaxKinEnergy = true;
  } else { PrintWarning("SetMaxKinEnergy", e); } 
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void G4VEmProcess::SetMinKinEnergyPrim(G4double e)
{
  if(theParameters->MinKinEnergy() <= e && 
     e <= theParameters->MaxKinEnergy()) { minKinEnergyPrim = e; } 
  else { PrintWarning("SetMinKinEnergyPrim", e); } 
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

G4VEmProcess* G4VEmProcess::GetEmProcess(const G4String& nam)
{
  return (nam == GetProcessName()) ? this : nullptr;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

G4double G4VEmProcess::PolarAngleLimit() const
{
  return theParameters->MscThetaLimit();
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void G4VEmProcess::PrintWarning(G4String tit, G4double val)
{
  G4String ss = "G4VEmProcess::" + tit;
  G4ExceptionDescription ed;
  ed << "Parameter is out of range: " << val 
     << " it will have no effect!\n" << "  Process " 
     << GetProcessName() << "  nbins= " << theParameters->NumberOfBins()
     << " Emin(keV)= " << theParameters->MinKinEnergy()/keV 
     << " Emax(GeV)= " << theParameters->MaxKinEnergy()/GeV;
  G4Exception(ss, "em0044", JustWarning, ed);
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....

void G4VEmProcess::ProcessDescription(std::ostream& out) const
{
  if(nullptr != particle) {
    StreamInfo(out, *particle, true);
  }
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
