/** basicROOTTools.h
 *  Header file implementing some basic ROOT tools for everyday's use
 *  Author:   Christian Elsasser (elsasser@cern.ch)
 *  Date:     2012-04-18 (last modified: 2013-03-21)
 *  Version:  v1.0
 *  Tested:   No (last tested version: none)
 */

#ifndef BASICROOTTOOLS_H
#define BASICROOTTOOLS_H

#include <limits>
#include <sstream>

#include "TROOT.h"
#include "TH1.h"

#include "TTree.h"
#include "TLeaf.h"
#include "TBranch.h"
#include "TChain.h"

#include "TF1.h"
#include "TVectorD.h"

#include "TParticle.h"

//////////
// Histogram tools
//////////

// Normalize histogram to one (under-/overflow bin neglected)
void Normalize(TH1* histo){
  histo->Scale(1.0/histo->Integral());
}

// Get average width of bins
Double_t GetWidth(TH1* histo,char axis='X'){
  if (axis=='Y' || axis=='y') {
    return (histo->GetYaxis()->GetXmax()-histo->GetYaxis()->GetXmin())/histo->GetNbinsY();
  }else if (axis=='Z' || axis=='Z') {
    return (histo->GetZaxis()->GetXmax()-histo->GetZaxis()->GetXmin())/histo->GetNbinsZ();
  }
  return (histo->GetXaxis()->GetXmax()-histo->GetXaxis()->GetXmin())/histo->GetNbinsX();
}

// Scale error of histogram by constant
void ScaleError(TH1* histo, Double_t scale){
  for (int x=0; x<histo->GetNbinsX()+2; x++) {
    for (int y=0; y<histo->GetNbinsY()+2; y++) {
      for (int z=0; z<histo->GetNbinsZ()+2; z++) {
        histo->SetBinError(x,y,z,
                           histo->GetBinError(x,y,z)*scale);
      }
    }
  }
  return;
}

//////////
// Tree tools
//////////

// Get efficiency of cut
Double_t GetEfficiency(TTree* t, char* var,std::string cut=""){
  TH1F* h_dummy = new TH1F("h_dummy","dummy histo",10,0.0,1.0);
  t->Draw(Form("%s>>h_dummy",var),cut.c_str(),"goff");
  double eff = 1.0*h_dummy->GetEntries()/t->GetEntries();
  h_dummy->Delete();
  return eff;
}

// Get branch type (stolen from Ph. Hunt)
std::string GetBranchType(TTree* tt, std::string brName)
{
  TChain* ch=dynamic_cast<TChain*>(tt);
  TBranch* br = ch==NULL?tt->GetBranch(brName.c_str()):ch->GetBranch(brName.c_str());
  if (!br) {
    std::stringstream msg;
    msg << "Unable to retrieve branch with name " << brName << " from TTree "
    << tt->GetName();
    return "null";
  }
  return ((TLeaf*)br->GetListOfLeaves()->At(0))->GetTypeName();
}


//////////
// Function tools
//////////

// Retrieving gradient df/da for parameters at point x
TVectorD GetParaGradient(TF1* func, Double_t x){
  Double_t* a_grad = 0x0;
  func->GradientPar(&x,a_grad);
  TVectorD v_grad(func->GetNpar(),a_grad);
  return v_grad;
}

//////////
// Vector tools
//////////

// Transforms c++ vector to ROOT vector
template<class T> TVectorT<T> getROOTVector(std::vector<T> v){
  TVectorT<T> v_root(v.size());
  for (int i=0; i<v.size(); i++) {
    v_root(i) = v[i];
  }
  return v_root;
}

// Add ROOT vector to c++ vector
template<class T> std::vector<T> addROOTVector(std::vector<T> v, TVectorT<T> vr){
  for (int i=0; i<vr.GetNoElements(); i++) {
    v.push_back(vr(i));
  }
  return v;
}


// Multiply TVectorT<T> by elements
TVectorT<double> multiElem(TVectorT<double> v1,
                           TVectorT<double> v2){
  TVectorT<double> v(v1.GetNoElements());
  if (v1.GetNoElements()!=v2.GetNoElements()){
    Error("multiElem","The two vectors have not the same length! Returning zero vector");
    return v;
  }
  
  for (int i=0; i<v.GetNoElements(); i++) {
    v(i) = v1(i)*v2(i);
  }
  return v;
}

// Return element index of element closest to input value
template<class T> int getClosestIndex(TVectorT<T> v,double val){
  int index = -1;
  double dist = std::numeric_limits<double>::max();
  for (int i=0; i<v.GetNoElements(); i++) {
    if (TMath::Abs((double)v(i)-val)<dist) {
      index = i;
      dist = TMath::Abs((double)v(i)-val);
    }
  }
  return index;
}

//////////
// Decoration tools
//////////

// Transforms PDG code to particle name
const char* GetParticleName(int i){
  TParticle *p = new TParticle();
  p->SetPdgCode(i);
  return p->GetName();
}



//////////
// Not ROOT related tools
//////////

// Checks if std::string is number



#endif