/*
 * EffCollection.cpp
 *
 *  Created on: 10/ago/2009
 *      Author: Nicola Mori
 */

/*! @file EffCollection.cpp The EffCollection class implementation file. */

#include "EffCollection.h"
#include "TGraphAsymmErrors.h"

extern "C" {
/*! @brief Fortran routine by Sergio Ricciarini which computes errors on the efficiency.
 *
 * This routine has been developed to carefully evaluate the error bars on the efficiency,
 * which must have a value between 0 and 1. See the appendix of Sergio's PhD thesis for more
 * details.
 * The important thing is that the computed efficiency will have the unphysical value 1.1
 * if there are less than 8 events in the efficiency sample (eg., the sample used to compute the
 * efficiency). This is necessary to have a reliable estimate of the errors.
 *
 * @param sel Pointer to an Int_t variable containing the number of events in the efficiency sample.
 * @param det Pointer to an Int_t variable containing the number of events in the efficiency sample
 *            which satisfied the set of cuts  for which the efficiency is being measured.
 * @param eff Pointer to a Double_t variable where the computed efficiency will be returned.
 * @param errLow Pointer to a Double_t variable where the length of the lower error bar will
 *               be returned.
 * @param errHigh Pointer to a Double_t variable where the length of the upper error bar will
 *               be returned.
 * @return Not clear at this point... ignore it.
 */
bool efficiency_(Int_t* sel, Int_t* det, Double_t* eff, Double_t* errLow, Double_t* errHigh);
}

EffCollection::EffCollection(const char *collectionName, TString outFileBase, int errMethod, bool owns) :
  VerboseCollection(collectionName, false), _selCollection("selCollection",owns), _detCollection("detCollection",
      owns), _outFileBase(outFileBase), _errMethod(errMethod), _det(0), _sel(0) {

}

void EffCollection::AddDetectorCut(PamCut *cut) {
  _detCollection.AddCut(cut);
}

void EffCollection::AddSelectionCut(PamCut *cut) {
  _selCollection.AddCut(cut);
}

void EffCollection::AddDetectorAction(CollectionAction *action) {
  _detCollection.AddAction(action);
}

void EffCollection::AddSelectionAction(CollectionAction *action) {
  _selCollection.AddAction(action);
}

void EffCollection::Setup(PamLevel2 *events){
  // Base class have a single vector for cuts and another for actions. Here the cuts and actions
  // are not contained inside these vectors but rather inside two SmartCollection object members.
  // So we must call their Setup().
  _selCollection.Setup(events);
  _detCollection.Setup(events);

  // We call also base class' Setup(), which will likely do nothing since _sel and _det are empty.
  VerboseCollection::Setup(events);

}

int EffCollection::ApplyCut(PamLevel2 *event) {

  _nEv++;
  if (_selCollection.ApplyCut(event) == CUTOK) {
    _sel++;
    if (_detCollection.ApplyCut(event) == CUTOK) {
      _det++;
      _nGood++;
      return CUTOK;
    }
  }

  return 0;
}

void EffCollection::Finalize() {
  // Let's add all the cuts to the vector of the collection before calling VerboseCollection::Finalize
  for (unsigned int i = 0; i < _selCollection.GetSize(); i++)
    _cuts.push_back(_selCollection.GetCut(i));
  for (unsigned int i = 0; i < _selCollection.GetActionsSize(); i++){
    _actions.push_back(_selCollection.GetAction(i));
    _actionsPositions.push_back(_selCollection.GetActionPosition(i));
  }
  for (unsigned int i = 0; i < _detCollection.GetSize(); i++)
    _cuts.push_back(_detCollection.GetCut(i));
  for (unsigned int i = 0; i < _detCollection.GetActionsSize(); i++){
    _actions.push_back(_detCollection.GetAction(i));
    _actionsPositions.push_back(_detCollection.GetActionPosition(i) + _selCollection.GetSize());
  }

  // Now all the cuts and actions are in place, and VerboseCollection can print its report and call Finalize() for
  // every cut and action (calling SmartCollection::Finalize().
  VerboseCollection::Finalize();

  // Compute the error
  Int_t sel = (Int_t) _sel;
  Int_t det = (Int_t) _det;
  Double_t eff, errLow, errHigh;
  if (_errMethod == EFFERR_ROOT) {
    if (sel < 8) {
      eff = 1.1;
      errLow = errHigh = 0.;
    }
    else {
      TH1I pass("pass", "pass", 1, 0., 1.);
      TH1I total("total", "total", 1, 0., 1.);
      pass.Fill(0.5, det);
      total.Fill(0.5, sel);
      TGraphAsymmErrors errGraph;
      errGraph.BayesDivide(&pass, &total);
      Double_t dummy;
      errGraph.GetPoint(0, dummy, eff);
      errLow = errGraph.GetErrorYlow(0);
      errHigh = errGraph.GetErrorYhigh(0);
    }
  }
  if (_errMethod == EFFERR_SERGIO) {
    efficiency_(&sel, &det, &eff, &errLow, &errHigh);
  }

  // Add some info about efficiency to the stdout
  cout << "    ****** Efficiency informations ******\n";
  cout << "    Detector cuts:\n";
  for (unsigned int i = 0; i < _detCollection.GetSize(); i++)
    cout << "      - " << _detCollection.GetCut(i)->GetName() << "\n";
  cout << "    Total detector efficiency: " << eff << " +" << errHigh << "-" << errLow << endl;

  // Write the output file
  if (_outFileBase != "") {

    ofstream outTextFile((_outFileBase + "-" + GetName() + ".txt").Data(), ios_base::out);
    streamsize newPrec = 4;
    outTextFile.precision(newPrec);
    outTextFile.setf(ios::fixed, ios::floatfield);
    outTextFile << setw(10) << det << setw(10) << sel << setw(10) << eff << setw(10) << errLow << setw(10) << errHigh
        << endl;
    outTextFile.close();
  }

}
