//Libraries used for the B+ mass fit model
//Renata Kopecna
#ifndef MASSFIT_HPP
#define MASSFIT_HPP

#include "GlobalFunctions.hh"
#include "RooFit/RooDoubleCB/RooDoubleCB.h"
#include "BmassShape/SignalPdf.hpp"

using namespace std;
using namespace RooFit;
using namespace RooStats;

//////////////////////////////////////////////////////
///  \brief GetsWeightPlots()
//////////////////////////////////////////////////////
///
/// Returns path where the sWeighting control plots are stored
///
//////////////////////////////////////////////////////
///  \brief massFit()
//////////////////////////////////////////////////////
/// Based on quickFit in nTrackWeights.cc, but made into a seperate file
/// in on order to be able to be run as a standalone code
///
/// Main purpose is to fit the Bmass distribution and save the yield of the fit.
/// the fitted B+ mass spectrum may be saved in pdf and root files
///
///
/// FEATURES:
/// - choose your background and signal model:
///         signal:     (double) Gaussian, left/right/double/two-tailed CrystalBall function
///         background: (double) Exponential, Exponential plus RooExpGaus or no background
/// - get signal shape from MC data if needed and restrict shape to these parameters
/// - choose if fit all data, Jpsi q^2 region or q^2 region without resonances
/// - choose if fit low/high q^2 region
/// - choose to fit LL/DD Kshort sample
/// - choose to fit up/down/both
/// - choose to fit per year or per run///
///
/// OPTIONAL:
/// - cut on TMVAresponse
/// - fit only randomly selected half of the data sample
/// - Calculate yield in a 2sigma/fixed window
/// - Calculate yield over the full region
/// - Turn off the fit of MC needed to get the shape parameters (usefull in loops)
/// - Save the mass plots in efficiency file
/// - Fit in bins of selected variable
///
/// - from the fit to the B+ mass distribution, a sPlot weight is calculated and saved to a new Branch in this merged file.
///     If only Jpsi is selected, Reference channel is used and if sWeight==true,
///     J/psi data only is saved
///
//////////////////////////////////////////////////////
///  \brief quickFit()
//////////////////////////////////////////////////////
///
/// massFit() with simplified options
/// suitable for reweighting MC and creating sPlots
///
/////////////////////////////////////////////////////////
///  efficiencyFit()
//////////////////////////////////////////////////////
///
/// \brief massFit() with simplified options
/// suitable for efficiency estimation fits
///
/////////////////////////////////////////////////////////
///  \brief basicYieldFit()
//////////////////////////////////////////////////////
///
/// massFit() with simplified options
/// suitable for getting yield information
///
/// Available also to be done for:
///     All years
///     All years and q2 regions
///     Both Runs
///
/////////////////////////////////////////////////////////
///  \brief massFitTestAll()
//////////////////////////////////////////////////////
///
/// Fits given data sample with different
/// signal and background shapes, useful
/// when deciding what shape describes
/// the data the best
///
//////////////////////////////////////////////////////
///  \brief PrintFitResults()
//////////////////////////////////////////////////////
/// Takes RooFitResult as an iput and prints its content
///
///
///


//////////////////////////////////////////////////////
/// TODO at some point
/// \param year
/// \param UseOnlyJpsiEvents
/// \param UseOnlyMuMuEvents
/// \param KshortDecaysInVelo
/// \param GetShapeFromMC
/// \param SigType
/// \param BckGndType
/// \param ConstrainParameters
///
///
/// 


class TMefficiencyClass{

public:
    string sVariable;
    std::vector<string> sBranchName;
    Int_t Bins;
    Float_t Range[2];
    std::vector<std::array<double, 2>> vVarRange;
    std::vector<double> binEdges;
    std::vector<double> binEdgesEquidistant;
    bool isEquidistant;
    Float_t Step;
    string cut;

    TMefficiencyClass(){ //default constructor
        sVariable   = "";
        sBranchName = {};
        Bins = 0;
        Range[0] = 0.;
        Range[1] = 0.;
        vVarRange = {};
        binEdges = {};
        binEdgesEquidistant = {};
        cut = "";

    }

    void fillBinEdges(){
        double step = (Range[1] - Range[0]) / Bins;
        for (int i = 0; i <= Bins; i++){
            binEdgesEquidistant.push_back(Range[0]+i*step);
        }
        return;
    }
    void isEq(string &varName){ //'_equal' has to appear at the end of the variable!
        //See if the '_equal' is present
        size_t found = varName.find("_equal");
        if (found != string::npos) isEquidistant = true;
        else isEquidistant = false;
        //Remove '_equal' from the string
        replace(varName,"_equal","");
    }
    bool check_vector_size(bool isEqui){
        if (!isEqui){
            if (int(binEdges.size()) != Bins+1){
                coutDebug("Bins edges should be " + to_string(Bins+1) + " and are " + to_string(binEdges.size()));
                coutERROR("Wrong number of bin edges! Check " + sVariable);
                return false;
            }
            else return true;
        }
        else{
            if (int(binEdgesEquidistant.size()) != Bins+1){
                coutDebug("Bins edges should be " + to_string(Bins+1) + " and are " + to_string(binEdgesEquidistant.size()));
                coutERROR("Wrong number of bin edges! Check " + sVariable);
                return false;
            }
            else return true;
        }
    }

    TMefficiencyClass(string varName){ // constructor

        //first, replace the variable name
        isEq(varName);
        coutDebug("Is equidistant? " + to_string(isEquidistant));

        //q^2:
        if (varName == "q2"){
            sVariable   = "q^{2} [MeV^{2}]";
            sBranchName = {"Q2"};
            Bins = 10;
            Range[0] = 0.; // in MeV^2
            Range[1] = 20000000.; // in MeV^2
            vVarRange = {{Range[0],Range[1]}};
            fillBinEdges();
            binEdges = {};
            cut = sBranchName.at(0);
        }
        else if (varName == "q2_binned"){ //binned as in the analysis
            sVariable   = "q^{2} [GeV^{2}]";
            sBranchName = {"Q2"};
            Bins = 11;
            Range[0] = 0.1e6; // in MeV^2
            Range[1] = 20e6;
            vVarRange = {{Range[0],Range[1]}};
            fillBinEdges();
            binEdges = {0.1e6, 0.98e6, 1.1e6, 2.5e6, 4.0e6, 6.0e6, 8.0e6, 11.0e6, 12.5e6, 15.0e6, 17.0e6, 20.0e6};
            cut = sBranchName.at(0);
        }
        else if (varName == "q2_binned_fit"){ //binned as in the analysis
            sVariable   = "q^{2} [GeV^{2}]";
            sBranchName = {"Q2"};
            Bins = 6;
            Range[0] = 1.1e6; // in MeV^2
            Range[1] = 20e6;
            vVarRange = {{Range[0],Range[1]}};
            fillBinEdges();
            binEdges = {0.1e6, 4.0e6, 8.0e6, 11.0e6, 12.5e6, 15.0e6, 20.0e6};
            cut = sBranchName.at(0);
        }


        //cos(Theta_k):
        else if (varName == "thetak"){
            sVariable   = "cos(#theta_{k})";
            sBranchName = {"B_plus_ThetaK"};
            Bins = 10;
            Range[0] = -1.0;
            Range[1] = 1.0;
            //Range[0] = 0.;
            //Range[1] = 3.142;
            Step = (Range[1] - Range[0]) / Bins;
            fillBinEdges();
            binEdges = {0.000, 1.053, 1.294, 1.488, 1.662, 1.827, 1.993, 2.163, 2.353, 2.585, 3.150};
            vVarRange = {{0.,3.142}};
            cut = "cos(B_plus_ThetaK)";
        }
        //cos(Theta_l):
        else if (varName == "thetal"){
            sVariable   = "cos(#theta_{l})";
            sBranchName = {"B_plus_ThetaL"};
            Bins = 10;
            Range[0] = -1.0;
            Range[1] = 1.0;
            //Range[0] = 0.;
            //Range[1] = 3.142;
            Step = (Range[1] - Range[0]) / Bins;
            fillBinEdges();
            binEdges = {0.000, 0.630, 0.896, 1.110, 1.302, 1.486, 1.670, 1.866, 2.086, 2.368, 3.150};
            vVarRange = {{0.,3.142}};
            cut = "cos(B_plus_ThetaL)";
        }
        //phi
        else if (varName == "phi"){
            sVariable   = "#phi";
            sBranchName = {"B_plus_Phi"};
            Bins = 10;
            Range[0] = -3.15;
            Range[1] = 3.15;
            Step = (Range[1] - Range[0]) / Bins;
            fillBinEdges();
            binEdges = {-3.150, -2.476, -1.832, -1.207, -0.593, 0.015, 0.624, 1.241, 1.870, 2.517, 3.150};
            vVarRange = {{Range[0],Range[1]}};
            cut = sBranchName.at(0);
        }
        else if (varName == "pi_zero_ETA"){
            sVariable   = "#eta(#pi^{0})";
            sBranchName = {"pi_zero_resolved_ETA_DTF"};
            Bins = 10;
            Range[0] = 1.5;
            Range[1] = 4.5;
            Step = (Range[1] - Range[0]) / Bins;
            fillBinEdges();
            binEdges = {};
            vVarRange = {{Range[0],Range[1]}};
            cut = sBranchName.at(0);
        }
        else if (varName == "pi_zero_ETA-K_plus_ETA"){
            sVariable   = "|#eta(#pi^{0})-#eta(K^{+})|";
            sBranchName = {"pi_zero_resolved_ETA_DTF","K_plus_ETA_DTF"};
            Bins = 15;
            Range[0] = 0.0;
            Range[1] = 1.0;
            Step = (Range[1] - Range[0]) / Bins;
            fillBinEdges();
            binEdges = {};
            vVarRange = {{0.0,5.0},{0.0,5.0}};
            cut = "TMath::Abs(pi_zero_resolved_ETA_DTF-K_plus_ETA_DTF)";
        }
        else if (varName == "pi_zero_P"){
            sVariable   = "log(P(#pi^{0}))";
            sBranchName = {"pi_zero_resolved_P"};
            Bins = 10;
            Range[0] = 2980;  //not log!
            Range[1] = 100000;//not log!
            Step = (Range[1] - Range[0]) / Bins;
            fillBinEdges();
            binEdges = {8.0, 8.75367, 9.01297, 9.21333, 9.38583, 9.55554, 9.72447, 9.90965, 10.1241, 10.405, 11.5};
            vVarRange = {{Range[0],Range[1]}};
            cut = "log(pi_zero_resolved_P)";//TODO fix equidistant binning, now based on 2016 PHSP
        }
        else if (varName == "pi_zero_P_DTF"){
            sVariable   = "log(P(#pi^{0}^{DTF}})";
            sBranchName = {"pi_zero_resolved_P_DTF"};
            Bins = 10;
            Range[0] = 2980;  //not log!
            Range[1] = 100000;//not log!
            Step = (Range[1] - Range[0]) / Bins;
            fillBinEdges();
            binEdges = {8.0, 8.7508, 9.01136, 9.20829, 9.38185, 9.55001, 9.723, 9.90559, 10.1195, 10.4024, 11.5};
            vVarRange = {{Range[0],Range[1]}};
            cut = "log(pi_zero_resolved_P_DTF)";//TODO fix equidistant binning, now based on 2016 PHSP
        }
        else if (varName == "pi_zero_PT"){
            sVariable   = "log(P_{T}(#pi^{0}))";
            sBranchName = {"pi_zero_resolved_PT"};
            Bins = 10;
            Range[0] = 800; //not log!
            Range[1] = 8000;//not log!
            Step = (Range[1] - Range[0]) / Bins;
            fillBinEdges();
            binEdges = {6.5, 6.81709, 6.91294, 7.01023, 7.11076, 7.21995, 7.34191, 7.48141, 7.64965, 7.88324, 9.0};
            vVarRange = {{Range[0],Range[1]}};
            cut = "log(pi_zero_resolved_PT)"; //TODO fix equidistant binning, now based on 2016 PHSP
        }
        else if (varName == "pi_zero_PT_DTF"){
            sVariable   = "log(P_{T}(#pi^{0})^{DTF})";
            sBranchName = {"pi_zero_resolved_PT_DTF"};
            Bins = 10;
            Range[0] = 800; //not log!
            Range[1] = 8000;//not log!
            Step = (Range[1] - Range[0]) / Bins;
            fillBinEdges();
            binEdges = {6.5, 6.8152, 6.91109, 7.00766, 7.10771, 7.21609, 7.33793, 7.47641, 7.64706, 7.88087, 9.0};
            vVarRange = {{Range[0],Range[1]}};
            cut = "log(pi_zero_resolved_PT_DTF)";//TODO fix equidistant binning, now based on 2016 PHSP
        }
        else if (varName == "K_plus_PT"){
            sVariable   = "log(P_{T} (K^{+})) [MeV^{2}]";
            sBranchName = {"K_plus_PT"};
            Bins = 10;
            Range[0] = 270; //not log!
            Range[1] = 16000;//not log!
            Step = (Range[1] - Range[0]) / Bins;
            fillBinEdges();
            binEdges = {5.6, 6.658, 6.91969, 7.12516, 7.30566, 7.47015, 7.64604, 7.8327, 8.04823, 8.35614, 9.7};
            vVarRange = {{Range[0],Range[1]}};
            cut =  "log(K_plus_PT)";
        }
        else{
            sVariable   = "";
            sBranchName = {};
            Bins = 1;
            Range[0] = 0.;
            Range[1] = 0.;
            vVarRange = {};
            fillBinEdges();
            binEdges={Range[0],Range[1]};
            Step = 0;
            cut = "";
            isEquidistant = false;
        }
        if (!check_vector_size(isEquidistant)){
            sVariable   = "";
            sBranchName = {};
            Bins = 0;
            Range[0] = 0.;
            Range[1] = 0.;
            vVarRange = {};
            fillBinEdges();
            Step = 0;
            cut = "";
        }

    }
    ~TMefficiencyClass();   //destuctor
};

TMefficiencyClass::~TMefficiencyClass(){}//destuctor

bool useExtraVarBool(string extraVar);

string GetsWeightPlots(string year, bool UseOnlyJpsiEvents, bool UseOnlyMuMuEvents, bool KshortDecaysInVelo, bool GetShapeFromMC, string SignalType, string BkgType, bool ConstrainParameters);

double massFit(string year, string magnet, int Run,
               bool MC, bool Preselected, bool TM, bool PHSP,       //input/output file selection
               bool UseOnlyJpsiEvents, bool UseOnlyMuMuEvents,      //signal/reference
               bool GetShapeFromMC, string SigType, string BkgType, bool ConstrainParameters, //shape
               bool KshortDecaysInVelo, bool UseLowQ2Range,         //LL/DD? q2range?
               Double_t TMVAcut,  int randomSubset,                 //TMVA options
               bool fixedMassRegion, bool yieldOverFullRange,       //yield calculation region
               bool sWeight,                                        //sWeight data?
               bool loopFit, bool IsEfficiency,                     //additional options
               string sExtraVar, int nExtraBin,                     //fit in bins of extra variable
               bool removeMultiple,                                 //Remove multiple candidates?
               bool weighted, bool weightedFromPi0, string whichWeight, //use weight in the fit?
               bool nonTM, string customTMbranch, bool gammaTM,     //TM options
               bool InclusiveSample);

int quickFit(string year, bool MC, bool sWeight, bool UseOnlyJpsiEvents, bool UseOnlyMuMuEvents, bool KshortDecaysInVelo, bool GetShapeFromMC, string SigType, string BkgType, bool ConstrainParameters);

int efficiencyFit(std::string year, string magnet, int Run ,
                  bool Preselected, bool TM, bool PHSP,         //input/output file selection
                  bool UseOnlyJpsiEvents, bool UseOnlyMuMuEvents,  //signal/reference
                  bool GetShapeFromMC, std::string SigType , std::string BckGndType , bool ConstrainParameters, //shape
                  bool KshortDecaysInVelo,           //LL/DD?
                  Double_t TMVAcut,                          //TMVA options
                  bool fixedMassRegion,             //yield calculation region
                  bool UseLowQ2Range,     //q2 ranges
                  string sExtraVar, int nExtraBin, bool removeMultiple,
                  bool weighted, bool weightedFromPi0, string whichWeight, string customTMbranch, bool gammaTM);


int basicYieldFit(std::string year, int Run ,
                  bool MC, bool PHSP,        //input/output file selection
                  bool UseOnlyJpsiEvents, bool UseOnlyMuMuEvents,  //signal/reference
                  bool GetShapeFromMC, std::string SigType , std::string BckGndType , bool ConstrainParameters, //shape
                  bool KshortDecaysInVelo, bool UseLowQ2Range ,  //LL/DD? q2range?
                  Double_t TMVAcut,                                       //TMVA options
                  bool fixedMassRegion, bool loopFit, bool removeMultiple);

int basicYieldFitAllYears(bool MC, bool PHSP,        //input/output file selection
                  bool UseOnlyJpsiEvents, bool UseOnlyMuMuEvents,  //signal/reference
                  bool GetShapeFromMC, std::string SigType , std::string BckGndType , bool ConstrainParameters, //shape
                  bool KshortDecaysInVelo, bool UseLowQ2Range ,  //LL/DD? q2range?
                  Double_t TMVAcut,                                       //TMVA options
                  bool fixedMassRegion, bool loopFit, bool removeMultiple);


int basicFitAllYearsAndRegions(bool MC, bool PHSP,        //input/output file selection
        bool GetShapeFromMC, std::string SigType , std::string BckGndType , bool ConstrainParameters, //shape
        bool KshortDecaysInVelo, bool UseLowQ2Range ,  //LL/DD? q2range?
        Double_t TMVAcut, bool removeMultiple  //TMVA options, remove multiple
        );


int basicYieldFitAllRuns(
                  bool MC,        //input/output file selection
                  bool UseOnlyJpsiEvents, bool UseOnlyMuMuEvents,  //signal/reference
                  bool GetShapeFromMC, std::string SigType , std::string BckGndType , bool ConstrainParameters, //shape
                  bool KshortDecaysInVelo, bool UseLowQ2Range ,  //LL/DD? q2range?
                  Double_t TMVAcut,                                       //TMVA options
                  bool fixedMassRegion, bool loopFit,bool removeMultiple//yield calculation region
                  );


//Print efficiencies and fit parameters
int PrintFitResults(RooFitResult* fitRes);

int fitJpsi(string year, bool MC, double TMVAcut, bool RemoveMultiple);

#endif // MASSFIT_HPP
