// UserHooks.cc is a part of the PYTHIA event generator.
// Copyright (C) 2025 Torbjorn Sjostrand.
// PYTHIA is licenced under the GNU GPL v2 or later, see COPYING for details.
// Please respect the MCnet Guidelines, see GUIDELINES for details.

// Function definitions (not found in the header) for the UserHooks class.

// Note: compilation crashes if PhaseSpace.h is moved to UserHooks.h.
#include "Pythia8/PhaseSpace.h"
#include "Pythia8/UserHooks.h"

namespace Pythia8 {

//==========================================================================

// The UserHooks class.

//--------------------------------------------------------------------------

// multiplySigmaBy allows the user to introduce a multiplicative factor
// that modifies the cross section of a hard process. Since it is called
// from before the event record is generated in full, the normal analysis
// does not work. The code here provides a rather extensive summary of
// which methods actually do work. It is a convenient starting point for
// writing your own derived routine.

double UserHooks::multiplySigmaBy( const SigmaProcess* sigmaProcessPtr,
  const PhaseSpace* phaseSpacePtr, bool inEvent) {

  // Process code, necessary when some to be treated differently.
  //int code       = sigmaProcessPtr->code();

  // Final multiplicity, i.e. whether 2 -> 1 or 2 -> 2.
  //int nFinal     = sigmaProcessPtr->nFinal();

  // Incoming x1 and x2 to the hard collision, and factorization scale.
  //double x1      = phaseSpacePtr->x1();
  //double x2      = phaseSpacePtr->x2();
  //double Q2Fac   = sigmaProcessPtr->Q2Fac();

  // Renormalization scale and assumed alpha_strong and alpha_EM.
  //double Q2Ren   = sigmaProcessPtr->Q2Ren();
  //double alphaS  = sigmaProcessPtr->alphaSRen();
  //double alphaEM = sigmaProcessPtr->alphaEMRen();

  // Subprocess mass-square.
  //double sHat = phaseSpacePtr->sHat();

  // Now methods only relevant for 2 -> 2.
  //if (nFinal == 2) {

    // Mandelstam variables and hard-process pT.
    //double tHat  = phaseSpacePtr->tHat();
    //double uHat  = phaseSpacePtr->uHat();
    //double pTHat = phaseSpacePtr->pTHat();

    // Masses of the final-state particles. (Here 0 for light quarks.)
    //double m3    = sigmaProcessPtr->m(3);
    //double m4    = sigmaProcessPtr->m(4);
  //}

  // Dummy statement to avoid compiler warnings.
  return ((inEvent && sigmaProcessPtr->code() == 0
    && phaseSpacePtr->sHat() < 0.) ? 0. : 1.);

}

//--------------------------------------------------------------------------

// biasSelectionBy allows the user to introduce a multiplicative factor
// that modifies the cross section of a hard process. The event is assigned
// a wegith that is the inverse of the selection bias, such that the
// cross section is unchanged. Since it is called from before the
// event record is generated in full, the normal analysis does not work.
// The code here provides a rather extensive summary of which methods
// actually do work. It is a convenient starting point for writing
// your own derived routine.

double UserHooks::biasSelectionBy( const SigmaProcess* sigmaProcessPtr,
  const PhaseSpace* phaseSpacePtr, bool inEvent) {

  // Process code, necessary when some to be treated differently.
  //int code       = sigmaProcessPtr->code();

  // Final multiplicity, i.e. whether 2 -> 1 or 2 -> 2.
  //int nFinal     = sigmaProcessPtr->nFinal();

  // Incoming x1 and x2 to the hard collision, and factorization scale.
  //double x1      = phaseSpacePtr->x1();
  //double x2      = phaseSpacePtr->x2();
  //double Q2Fac   = sigmaProcessPtr->Q2Fac();

  // Renormalization scale and assumed alpha_strong and alpha_EM.
  //double Q2Ren   = sigmaProcessPtr->Q2Ren();
  //double alphaS  = sigmaProcessPtr->alphaSRen();
  //double alphaEM = sigmaProcessPtr->alphaEMRen();

  // Subprocess mass-square.
  //double sHat = phaseSpacePtr->sHat();

  // Now methods only relevant for 2 -> 2.
  //if (nFinal == 2) {

    // Mandelstam variables and hard-process pT.
    //double tHat  = phaseSpacePtr->tHat();
    //double uHat  = phaseSpacePtr->uHat();
    //double pTHat = phaseSpacePtr->pTHat();

    // Masses of the final-state particles. (Here 0 for light quarks.)
    //double m3    = sigmaProcessPtr->m(3);
    //double m4    = sigmaProcessPtr->m(4);
  //}

  // Insert here your calculation of the selection bias.
  // Here illustrated by a weighting up of events at high pT.
  //selBias = pow4(phaseSpacePtr->pTHat());

  // Return the selBias weight.
  // Warning: if you use another variable than selBias
  // the compensating weight will not be set correctly.
  //return selBias;

  // Dummy statement to avoid compiler warnings.
  return ((inEvent && sigmaProcessPtr->code() == 0
    && phaseSpacePtr->sHat() < 0.) ? 0. : 1.);
}

//--------------------------------------------------------------------------

// omitResonanceDecays omits resonance decay chains from process record.

void UserHooks::omitResonanceDecays(const Event& process, bool finalOnly) {

  // Reset work event to be empty
  workEvent.clear();

  // Loop through all partons. Beam particles should be copied.
  for (int i = 0; i < process.size(); ++i) {
    bool doCopy  = false;
    bool isFinal = false;
    if (i < 3) doCopy = true;

    // Daughters of beams should normally be copied.
    else {
      int iMother = process[i].mother1();
      if (iMother == 1 || iMother == 2) doCopy = true;

      // Granddaughters of beams should normally be copied and are final.
      else if (iMother > 2) {
        int iGrandMother =  process[iMother].mother1();
        if (iGrandMother == 1 || iGrandMother == 2) {
          doCopy  = true;
          isFinal = true;
        }
      }
    }

    // Optionally non-final are not copied.
    if (finalOnly && !isFinal) doCopy = false;

    // Do copying and modify status/daughters of final.
    if (doCopy) {
      int iNew = workEvent.append( process[i]);
      if (isFinal) {
        workEvent[iNew].statusPos();
        workEvent[iNew].daughters( 0, 0);
        // When final only : no mothers; position in full event as daughters.
        if (finalOnly) {
          workEvent[iNew].mothers( 0, 0);
          workEvent[iNew].daughters( i, i);
        }
      }
    }
  }

}

//--------------------------------------------------------------------------

// subEvent extracts currently resolved partons in the hard process.

void UserHooks::subEvent(const Event& event, bool isHardest) {

  // Reset work event to be empty.
  workEvent.clear();

  // At the PartonLevel final partons are bookkept by subsystem.
  if (partonSystemsPtr->sizeSys() > 0) {

    // Find which subsystem to study.
    int iSys = 0;
    if (!isHardest) iSys = partonSystemsPtr->sizeSys() - 1;

    // Loop through all the final partons of the given subsystem.
    for (int i = 0; i < partonSystemsPtr->sizeOut(iSys); ++i) {
      int iOld = partonSystemsPtr->getOut( iSys, i);

      // Copy partons to work event.
      int iNew = workEvent.append( event[iOld]);

      // No mothers. Position in full event as daughters.
      workEvent[iNew].mothers( 0, 0);
      workEvent[iNew].daughters( iOld, iOld);
    }

  // At the ProcessLevel no subsystems have been defined.
  } else {

    // Loop through all partons, and copy all final ones.
    for (int iOld = 0; iOld < event.size(); ++iOld)
    if (event[iOld].isFinal()) {
      int iNew = workEvent.append( event[iOld]);

      // No mothers. Position in full event as daughters.
      workEvent[iNew].mothers( 0, 0);
      workEvent[iNew].daughters( iOld, iOld);
    }
  }

}

//==========================================================================

// The SuppressSmallPT class, derived from UserHooks.

//--------------------------------------------------------------------------

// Modify event weight at the trial level, before selection.

double SuppressSmallPT::multiplySigmaBy( const SigmaProcess* sigmaProcessPtr,
  const PhaseSpace* phaseSpacePtr, bool ) {

  // Need to initialize first time this method is called.
  if (!isInit) {

    // Calculate pT0 as for multiparton interactions.
    // Fudge factor allows offset relative to MPI framework.
    double eCM    = phaseSpacePtr->ecm();
    double pT0Ref = parm("MultipartonInteractions:pT0Ref");
    double ecmRef = parm("MultipartonInteractions:ecmRef");
    double ecmPow = parm("MultipartonInteractions:ecmPow");
    double pT0    = pT0timesMPI * pT0Ref * pow(eCM / ecmRef, ecmPow);
    pT20          = pT0 * pT0;

    // Initialize alpha_strong object as for multiparton interactions,
    // alternatively as for hard processes.
    double alphaSvalue;
    int    alphaSorder;
    int    alphaSnfmax = mode("StandardModel:alphaSnfmax");
    if (useSameAlphaSasMPI) {
      alphaSvalue = parm("MultipartonInteractions:alphaSvalue");
      alphaSorder = mode("MultipartonInteractions:alphaSorder");
    } else {
      alphaSvalue = parm("SigmaProcess:alphaSvalue");
      alphaSorder = mode("SigmaProcess:alphaSorder");
    }
    alphaS.init( alphaSvalue, alphaSorder, alphaSnfmax, false);

    // Initialization finished.
    isInit = true;
  }

  // Only modify 2 -> 2 processes.
  int nFinal = sigmaProcessPtr->nFinal();
  if (nFinal != 2) return 1.;

  // pT scale of process. Weight pT^4 / (pT^2 + pT0^2)^2
  double pTHat     = phaseSpacePtr->pTHat();
  double pT2       = pTHat * pTHat;
  double wt        = pow2( pT2 / (pT20 + pT2) );

  if (numberAlphaS > 0) {
    // Renormalization scale and assumed alpha_strong.
    double Q2RenOld  = sigmaProcessPtr->Q2Ren();
    double alphaSOld = sigmaProcessPtr->alphaSRen();

    // Reweight to new alpha_strong at new scale.
    double Q2RenNew  = pT20 + Q2RenOld;
    double alphaSNew = alphaS.alphaS(Q2RenNew);
    wt              *= pow( alphaSNew / alphaSOld, numberAlphaS);
  }

  // End weight calculation.
  return wt;

}


//==========================================================================

} // end namespace Pythia8
