/*
 * TrkNucleiZCut.h
 *
 *  Created on: 10-mag-2009
 *      Author: Nicola Mori
 */

/*! @file TrkNucleiZCut.h The TofNucleiZCut class definition file */

#ifndef NO_TRKNUCLEI

#ifndef TRKNUCLEIZCUT_H_
#define TRKNUCLEIZCUT_H_

#include "../../PamCutBase/PamCutBase.h"
#include <TrkNuclei.h>

#include <TH2F.h>

/*! @enum TrkNucleiZ_Return Return values for rejected events */
enum TrkNucleiZ_Return {
  TRKNUCLEIZ_OUTOFBOUNDS, ///< Discarded because charge is out of bounds
  TRKNUCLEIZ_TOOFEWLAYERS, ///< Discarded because charge can be measured in too few layers.
  TRKNUCLEIZ_ILLEGALLOWZ, ///< Discarded because charge is not between 1 and 6
  TRKNUCLEIZ_ILLEGALHIGHZ ///< Discarded because higher charge is not higher than lowZ and it is not -1 (default value)
};

/*! @enum TrkNucleiZ_method Method to use to measure Z. */
enum TrkNucleiZ_method {
  TRKNUCLEIZ_BETA, ///< Tracker dE/dx Vs. ToF beta
  TRKNUCLEIZ_RIG,
  ///< Tracker standalon e: dE/dx Vs. rigidity
};

/*! @enum TrkNucleiZ_Check Handy aliases to set check mean */
enum TrkNucleiZ_Check {
  TRKNUCLEIZ_CHECKMEAN, ///< Check the mean value.
  TRKNUCLEIZ_CHECKSINGLEVALUES
  ///< Check each single charge from each layer.
};

/*! @brief The TrkNuclei charge cut.
 *
 * This cut uses the TrkNuclei library to cut events depending on particle's charge value.
 *
 */

class TrkNucleiZCut: public PamCut {

public:
  /*! @brief Constructor.
   *
   * The charge to look for is Z. For each charge, there is an associated distribution of
   * events, with mean Zmean(Z) and standard deviation sigma(Z). These values will be used to check the events.
   * If highZ is different from default (-1), the routine will select nuclei with charge in the range
   * [Zmean(lowZ) - lowerBound*sigma(lowZ), Zmean(highZ) + upperBound*sigma(highZ)]; otherwise, it will select nuclei in the range
   * [Zmean(lowZ) - lowerBound*sigma(lowZ), Zmean(lowZ) + upperBound*sigma(lowZ)], eg., it will look for a single charge defined by lowZ.
   *
   * @param cutName The cut's name.
   * @param lowZ The charge value.
   * @param highZ The upper charge value.
   * @param lowerLimit The lower bound (in units of sigma(Z)) of the desired charge range, defined as Zmean - lowerBound*sigma(Z).
   * @param upperLimit The upper bound (in units of sigma(Z)) of the desired charge range, defined as Zmean + upperBound*sigma(Z).
   * @param minLayers The minimum required number of layers which give a valid charge information.
   * @param howToCheck Flag to set the check by mean release or by release in single planes (see #TrkNucleiZ_Check).
   * @param method The method to use to measure the particle's charge (see #TrkNucleiZ_method).
   * @return
   */
  TrkNucleiZCut(const char *cutName, unsigned int lowZ, int highZ = -1, float lowerLimit = 1., float upperLimit = 1., unsigned int minLayers = 1,
      unsigned int howToCheck = TRKNUCLEIZ_CHECKMEAN, unsigned int method = TRKNUCLEIZ_RIG) :
  PamCut(cutName), _lowZ(lowZ), _highZ(highZ), _lowerLimit(lowerLimit), _upperLimit(upperLimit), _trkNuclei(NULL), _minLayers(minLayers),
  _howToCheck(howToCheck), _method(method) {

  }
  /*! @brief Destructor. */
  ~TrkNucleiZCut() {
    delete _trkNuclei;
  }

  /*! @brief The TrkNucleiZ check.
   * The selection procedure depends on the value of highZ. If highZ = -1, the routine will look for a single charge lowZ, eg., for those
   * events whose value of Zmean(lowZ) obtained from the TrkNuclei class is in the range
   * [Zmean(lowZ) - sigma(Z)*lowerBound, Zmean(lowZ) + sigma(lowZ)*upperBound]. If instead highZ is grater than lowZ, the routine
   * will select all the nuclei with charges in the range [lowZ, highZ]; in practice, an event will survive the selection if
   * its charge as obtained from TrkNuclei is in the range  [Zmean(lowZ) - sigma(Z)*lowerBound, Zmean(highZ) + sigma(highZ)*upperBound].
   *
   * Zmean and sigma are defined respectively as the mean and the standard deviation of the charge distributions obtained using TrkNuclei.
   * In current implementation, calibrations for Z=1 and Z=2 for mean dE/dx Vs. rigidity are:
   *
   *  Z=1 |   mean     sigma
   *  ------------------------
   *      |  0.992      0.06
   *
   *
   *  Z=2 |   mean     sigma
   *  ------------------------
   *      |  1.99       0.1
   *
   * For all other cases (Z>2 or single plane releases or dE/dx Vs. beta), means are taken to be equal to the charge value (Zmean = Z) and sigmas are:
   *
   *   Z |  sigma
   *   -----------
   *   1 |  0.09
   *   2 |  0.10
   *   3 |  0.17
   *   4 |  0.34
   *   5 |  0.44
   *   6 |  0.59
   *
   * The TofNuclei calibrations used are the standard ones (from Wolfgang, see TrkNuclei.cpp).
   * First of all, the number of layers giving a valid charge information is checked to be greater than minLayers; if it's not so,
   * the routine will return #TRKNUCLEIZ_TOOFEWLAYERS. Next, it will check if the charge obtained considering the mean dE/dx in the tracker's
   * layers is in the validity range if the constructor's flag howToCheckMean was set to TRKNUCLEIZ_CHECKMEAN; otherwise, it will check each
   * single valid charge value (mean for X and Y views) obtained from each layer. In the latter case, it will return #CUTOK only if all
   * valid layers (ie., those tagged as good for both X and Y in traccker level 2 routines) give a charge inside the bounds, regardless of
   * the value of minLayers.
   *
   * @param event The event to analyze.
   * @return #CUTOK if the charge from mean dE/dx(if howToCheck == #TRKNUCLEIZ_CHECKMEAN) or all the single layer charges
   *         (if howToCheck == #TRKNUCLEIZ_CHECKSINGLEVALUES) obtained from TrkNuclei are in the range [Zmean(lowZ) - sigma(Z)*lowerBound, Zmean(lowZ) + sigma(lowZ)*upperBound]
   *         (for single charge selection) or in the range [Zmean(lowZ) - sigma(Z)*lowerBound, Zmean(highZ) + sigma(highZ)*upperBound] (for charge range selection).
   * @return #TRKNUCLEIZ_ILLEGALLOWZ if lowZ is less than 1 or greater than 6.
   * @return #TRKNUCLEIZ_ILLEGALHIGHZ if highZ is less than lowZ and not the default value (-1), or if it is greater than 6.
   * @return #TRKNUCLEIZ_OUTOFBOUNDS if the charge from mean dE/dx(if howToCheck == #TRKNUCLEIZ_CHECKMEAN) or at least one valid single layer
   *         charge (if howToCheck == #TRKNUCLEIZ_CHECKSINGLEVALUES) is out of bounds.
   * @return #TRKNUCLEIZ_TOOFEWLAYERS if charge information is available for a number of layers lesser than minLayers.
   * @return #TRKNUCLEIZ_OUTOFBOUNDS if Z < 1 or Z > 6.
   *
   */
  int Check(PamLevel2 *event);

private:

  unsigned int _lowZ;
  int _highZ;

  static const float _meanRigMean[]; //Means for mean dE/dx Vs. rigidity
  static const float _sigmaRigMean[];//Sigmas for mean dE/dx Vs. rigidity
  static const float _meanBetaMean[]; //Means for mean dE/dx Vs. beta
  static const float _sigmaBetaMean[];//Sigmas for mean dE/dx Vs. beta

  float _lowerLimit;
  float _upperLimit;
  TrkNuclei *_trkNuclei;
  unsigned int _minLayers;
  unsigned int _howToCheck;
  unsigned int _method;
};
#endif /* TRKNUCLEIZCUT_H_ */

#endif /* NO_TRKNUCLEI */
