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

/*! @file TrkNucleiZCut.cpp The TrkNucleiZCut class implementation file */
#ifndef NO_TRKNUCLEI

#include "TrkNucleiZCut.h"

const float TrkNucleiZCut::_meanRigMean[] = { .992, 1.99, 3., 4., 5., 6. }; // the charge distribution peak for Z=1..6
const float TrkNucleiZCut::_sigmaRigMean[] = { 0.06, 0.1, 0.17, 0.34, 0.44, 0.59 }; // the charge-width for Z=1..6

const float TrkNucleiZCut::_meanBetaMean[] = { 1., 2., 3., 4., 5., 6. }; // the charge distribution peak for Z=1..6
const float TrkNucleiZCut::_sigmaBetaMean[] = { 0.09, 0.1, 0.17, 0.34, 0.44, 0.59 }; // the charge-width for Z=1..6


int TrkNucleiZCut::Check(PamLevel2 *event) {

  if (_lowZ < 1 || _lowZ > 6) {
    return TRKNUCLEIZ_ILLEGALLOWZ;
  }
  if ((_highZ < (int) _lowZ && _highZ != -1) || _highZ > 6) {
    return TRKNUCLEIZ_ILLEGALLOWZ;
  }

  /* Check if we must perform a range selection */
  bool range = false;
  if (_highZ != -1)
    range = true;

  // ******** Check if TrkNuclei has already been initialized ********
  if (_trkNuclei == NULL)
    _trkNuclei = new TrkNuclei();
  _trkNuclei->Set(event->GetTrack(0)->GetTrkTrack());

  // ******** First of all, check if the minimum number of planes is matched. ********
  // NOTE: in current implementation a plane is considered valid if it is tagged as good in Tracker level 2
  //       routines. This should correspond to a valid charge.
  // TODO Check the above statement.

  // Prepare the valid charges counter
  unsigned int nValid = 0;
  for (unsigned int ip = 0; ip < 6; ip++) {
    if (_trkNuclei->GetTrkTrack()->XGood(ip) && _trkNuclei->GetTrkTrack()->XGood(ip))
      nValid++;
  }
  if (nValid < _minLayers)
    return TRKNUCLEIZ_TOOFEWLAYERS;

  // ******** Check the mean charge value ********
  if (_howToCheck == TRKNUCLEIZ_CHECKMEAN) {

    Float_t charge = 0., meanLowZ = 0., sigmaLowZ = 0., meanHighZ = 0., sigmaHighZ = 0.;
    ;
    switch (_method) {
    case TRKNUCLEIZ_RIG:
      charge = _trkNuclei->GetZ_Rigidity();
      meanLowZ = _meanRigMean[_lowZ - 1];
      sigmaLowZ = _sigmaRigMean[_lowZ - 1];
      if (range) {
        meanHighZ = _meanRigMean[_highZ - 1];
        sigmaHighZ = _sigmaRigMean[_highZ - 1];
      }
      break;
    case TRKNUCLEIZ_BETA:
      charge = _trkNuclei->GetZ_Beta(event->GetTrack(0)->GetToFTrack()->beta[12]);
      meanLowZ = _meanBetaMean[_lowZ - 1];
      sigmaLowZ = _sigmaBetaMean[_lowZ - 1];
      if (range) {
        meanHighZ = _meanBetaMean[_highZ - 1];
        sigmaHighZ = _sigmaBetaMean[_highZ - 1];
      }

      break;
    }

    if (!range) {
      if ((charge > meanLowZ + _upperLimit * sigmaLowZ) || (charge < meanLowZ - _lowerLimit * sigmaLowZ)) {
        return TRKNUCLEIZ_OUTOFBOUNDS;
      }
    }
    else {
      if ((charge > meanHighZ + _upperLimit * sigmaHighZ) || (charge < meanLowZ - _lowerLimit * sigmaLowZ)) {
        return TRKNUCLEIZ_OUTOFBOUNDS;
      }
    }
  }
  // ******** Check the charge value from each plane ********
  // NB. In current implementation only 2 check methods (mean and single values) are used, so an "else" statement
  //     is sufficient. Implement an "if" cascade or a "switch" if another check method is necessary.

  else {
    Float_t charge[6] = { 0., 0., 0., 0., 0., 0. };
    nValid = 0;
    switch (_method) {
    case TRKNUCLEIZ_RIG:
      for (int ip = 0; ip < 6; ip++) {
        charge[ip] = _trkNuclei->GetZ_Rigidity(ip);
        if (charge[ip] > 0.) {
          nValid++;
        }
      }
      break;
    case TRKNUCLEIZ_BETA:
      for (int ip = 0; ip < 6; ip++) {
        charge[ip] = _trkNuclei->GetZ_Beta(ip, event->GetTrack(0)->GetToFTrack()->beta[12]);
        if (charge[ip] > 0.) {
          nValid++;
        }
      }
      break;
    }

    for (int ip = 0; ip < 6; ip++) {
      if (charge[ip] > 0.) { // Check only good layers
        // Currently, no calibration for single layers is available. Z is used as a mean value, while for sigma
        // we use the standard values use for mean dE/dx Vs. beta.
        if (!range) {
          if ((charge[ip] > _lowZ + _upperLimit * _sigmaBetaMean[_lowZ - 1]) || (charge[ip] < _lowZ - _lowerLimit
              * _sigmaBetaMean[_lowZ - 1])) {
            return TRKNUCLEIZ_OUTOFBOUNDS;
          }
        }
        else {
          if ((charge[ip] > _highZ + _upperLimit * _sigmaBetaMean[_highZ - 1]) || (charge[ip] < _lowZ - _lowerLimit
              * _sigmaBetaMean[_lowZ - 1])) {
            return TRKNUCLEIZ_OUTOFBOUNDS;
          }
        }
      }
    }
  }

  return CUTOK;

}

#endif /* NO_TRKNUCLEI */
