/*
 * Histo1DAction.h
 *
 *  Created on: 25/ago/2009
 *      Author: Nicola Mori
 */

/*! @file Histo1DAction.h The Histo1DAction class declaration file. */

#ifndef HISTO1DACTION_H_
#define HISTO1DACTION_H_

#include "../../CollectionAction/CollectionAction.h"

#include <TH1I.h>
#include <TH1F.h>
#include <TH1D.h>

/*! @brief An abstract action that fills a 1-dimensional histogram.
 *
 * This abstract class provides all the common methods needed to produce 1-dimensional histograms,
 * like reading the bins from a file, setting up the root histogram and so on. It handles histograms
 * both as a ROOT histogram and as a vector. The OnGood() method has to be implemented in children
 * classes, with the specific physical variable to fill the histogram. Eg., a rigidity histogram class
 * derived from Histo1DAction could have this OnGood() implementation:
 *
 * Fill(event->GetTrack(0)->GetTrkTrack()->GetRigidity());
 *
 * Fill() automatically fills the ROOT and the vector histogram.
 * The template argument HistoType is the type of histogram (Int_t, Double_t,...). In current
 * implementation, ROOT histograms are only supported for Int_t, Float_t and Double_t. For all the
 * other types, only the the vector histogram with text file output will be produced. If you want to
 * implement some new types of ROOT histograms, please specialize the template definitions of _CreateHisto()
 * and ~Histo1DAction() (see the specializations in the .cpp file).
 *
 * */
template<class HistoType>
class Histo1DAction: public CollectionAction {
public:

  /*! @brief Constructor.
   *
   * Binning is read from a text file.
   *
   * @param actionName The action's name.
   * @param title The ROOT histogram title.
   * @param outFileBase The file base name for the histogram output. If "", no output file will be produced.
   * @param mode The mode of ROOT file creation (see documentation of TFile constructor
   *             in ROOT's reference guide).
   * @param outRoot If true, an output ROOT file named outFileBase + ".root" will be produced.
   * @param outText If true, an output text file named outFileBase + ".txt" will be produced. It will overwrite an
   *                eventually existing file with the same name.
   */
  Histo1DAction(const char *actionName, TString title, TString outFileBase = "", TString mode = "UPDATE", bool outRoot =
      false, bool outText = false);

  /*! @brief Destructor
   *
   * This has to be specialized for each kind of ROOT histogram.
   *
   * */
  ~Histo1DAction();

  /*! @brief Sets the X axis using a binning read from a a vector.
   *
   * @param label The axis' label.
   * @param bins A vector containing the histogram binning (with sign, eventually; from lowest to highest).
   */
  void SetXAxis(TString label, vector<float> &bins);
  /*! @brief Sets the X axis reading the binning from a file.
   *
   * @param label The axis' label.
   * @param binsFile The file containing the bins (with sign, eventually; from lowest to highest).
   */
  void SetXAxis(TString label, TString binsFile);
  /*! @brief Sets the X axis specifying the binning parameters.
   *
   * @param label The axis' label.
   * @param nBins The number of bins.
   * @param min The lower limit of the axis.
   * @param max The upper limit of the axis.
   * @param logBinning If true, bins will be logarithmically spaced.
   */
  void SetXAxis(TString label, unsigned int nBins, float min, float max, bool logBinning = false);

  /*! @brief Sets up the histogram
   *
   * This routine effectively prepares the histogram, after the desired parameters has been set by #SetXAxis().
   *
   * @param events Pointer to PamLevel2 events (unused).
   */
  void Setup(PamLevel2 *events) {
    CollectionAction::Setup(events);
    _InitHistos();

  }
  /*! @ brief Post-analysis tasks.
   *
   * This method writes the output on a ROOT file if the proper option was set in the constructor.
   */
  void Finalize();

  /*! @brief Returns the histogram.
   *
   * @return A reference to a vector containing the values of the bins of the histogram.
   */
  vector<HistoType> &GetHisto() {
    return _histo;
  }

  /*! Fills the ROOT and the vector histogram.
   *
   * @param value The value of the X coordinate associated to the event.
   * @param weight The weight which will be applied to the event.
   */
  void Fill(double value, double weight = 1.);

  /*! @brief The number of events which fell below the lower histogram limit. */
  HistoType GetOverflow() {
    return _overflow;
  }

  /*! @brief The number of events which fell above the upper histogram limit. */
  HistoType GetUnderflow() {
    return _underflow;
  }

protected:

  /*! @brief The vector containing the limits of the bins(from lower to higher). */
  std::vector<float> _bins;
  /*! @brief A vector containing the value of the histogram for each bin. */
  vector<HistoType> _histo;
  /*! @brief The ROOT histogram. */
  TH1 *_rootHisto;

  /*! @brief Base name of the output file. */
  TString _outFileBase;
  /*! @brief Output file open mode (UPDATE or RECREATE, see documentation of TFile). */
  TString _mode;
  /*! @brief Title for the ROOT histogram. */
  TString _title;
  /*! @brief Axis labels for the ROOT histogram. */
  TString _xLabel;

private:

  HistoType _underflow, _overflow;
  bool _outRoot;
  bool _outText;
  void _CreateHisto();
  void _InitHistos();
};

template<class HistoType>
void Histo1DAction<HistoType>::_CreateHisto() {

  // No ROOT histogram for generic type; see template specializations in .cpp file.
  _rootHisto = NULL;
}
// Specializations for _CreateHistos(). See Histo1DAction.cpp
template<>
void Histo1DAction<Int_t>::_CreateHisto();

template<>
void Histo1DAction<Float_t>::_CreateHisto();

template<>
void Histo1DAction<Double_t>::_CreateHisto();

template<class HistoType>
void Histo1DAction<HistoType>::_InitHistos() {

  _CreateHisto();
  if (_bins.size() < 2) // SetXAxis not called by the main program, or wrongly filled (only 1 bin limit)
    SetXAxis("Default X", 10, 0., 1.);

  if (_rootHisto) {
    Double_t *auxArray = new Double_t[_bins.size()];

    for (unsigned int i = 0; i < _bins.size(); i++) {
      auxArray[i] = _bins[i];
    }

    _rootHisto->SetBins(_bins.size() - 1, auxArray);
    _rootHisto->SetName(GetName());
    _rootHisto->SetTitle(_title);
    _rootHisto->SetXTitle(_xLabel);
    _rootHisto->SetYTitle("Events");

    delete[] auxArray;

  }

  _histo.resize(_bins.size() - 1);

}

template<class HistoType>
Histo1DAction<HistoType>::~Histo1DAction() {

  delete _rootHisto;
  _rootHisto = NULL;

}

template<class HistoType>
Histo1DAction<HistoType>::Histo1DAction(const char *actionName, TString title, TString outFileBase, TString mode,
    bool outRoot, bool outText) :
  CollectionAction(actionName), _bins(0), _histo(0), _rootHisto(NULL), _outFileBase(outFileBase), _mode(mode), _title(
      title), _xLabel(""), _underflow(0), _overflow(0), _outRoot(outRoot), _outText(outText) {

}

template<class HistoType>
void Histo1DAction<HistoType>::SetXAxis(TString label, vector<float> &bins) {

  _bins = bins;
  _xLabel = label;

}

template<class HistoType>
void Histo1DAction<HistoType>::SetXAxis(TString label, TString binsFile) {

  // Reading the bins from file
  ifstream binListFile;
  binListFile.open(binsFile);

  TString auxString;
  _bins.resize(0);
  while (!binListFile.eof()) {
    binListFile >> auxString;
    if (auxString != "") {
      _bins.push_back(auxString.Atof());
    }
  }
  binListFile.close();

  _xLabel = label;

}

template<class HistoType>
void Histo1DAction<HistoType>::SetXAxis(TString label, unsigned int nBins, float min, float max, bool logBinning) {

  _bins.resize(nBins + 1);

  if (!logBinning || (logBinning && min <= 0.)) {

#ifdef DEBUGPAMCUT
    if (logbinning && rigMin <= 0.)
    cout << "Warning: logarithmic binning was chosen for X axis but min <= 0. Using linear binning."
#endif

    float step = (max - min) / nBins;
    for (unsigned int i = 0; i < nBins + 1; i++) {
      _bins[i] = min + i * step;
    }

  }
  else {

    double maxExp = log10(max / min);
    for (unsigned int i = 0; i < nBins + 1; i++) {
      _bins[i] = min * pow(10., (double) i / ((double) nBins) * maxExp);
    }

  }

  _xLabel = label;
}

template<class HistoType>
inline void Histo1DAction<HistoType>::Fill(double value, double weight) {

  _rootHisto->Fill(value, weight);

  if (value < _bins[0]) {
    _underflow += (HistoType) weight;
    return;
  }

  if (value > _bins[_bins.size() - 1]) {
    _overflow += (HistoType) weight;
    return;
  }

  int xBin = 1;
  while (value >= _bins[xBin]) {
    xBin++;
  }
  xBin--;

  _histo[xBin] += (HistoType) weight;
}

template<class HistoType>
void Histo1DAction<HistoType>::Finalize() {

  if (_outFileBase != "") {
    // Write the ROOT file
    if (_rootHisto && _outRoot) {
      TFile outRootFile((_outFileBase + ".root"), _mode);
      outRootFile.cd();
      _rootHisto->Write();
      outRootFile.Close();
    }

    //Write the text file
    if (_outText) {
      ofstream outTextFile((_outFileBase + ".txt").Data(), ios_base::out);
      for (unsigned int i = 0; i < _histo.size(); i++) {
        outTextFile << _histo[i] << "\n";
      }
      outTextFile << endl;
      outTextFile.close();
    }
  }

}
#endif /* HISTO1DACTION_H_ */
