//Renata Kopecna

#include <fstream>

#include <TChain.h>
#include <TH1D.h>
#include <TF1.h>
#include <TCanvas.h>
#include <TFile.h>
#include <TMath.h>
#include <TColor.h>
#include <TLegend.h>
#include <TStyle.h>
#include <TLatex.h>
#include <TROOT.h>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <RooDataSet.h>
#include <RooGaussian.h>
#include <RooFitResult.h>
#include <RooPlot.h>
#include <RooRealVar.h>
#include "EvaluateToys.hh"
#include <design.hh>
#include <helpers.hh>
#include "ScriptHelpers.hh"

#include <math.h>

//This code is as broken as it gets, the string for latex names and var names should be in a struct so when one has to skip stuff one can also skip the name and value actually
//This way it is error prone and god knows whether it even saves everything properly
//And it should've been connected directly with the csv file... like... no wonder some stuff took that long before

const bool UnbinnedFit = true;
const bool UseCellColor = true;
Int_t Idx = 0;

const UInt_t nPDFs = 2;

struct pullInfo{
    double sigma;
    double sigmaErr;
    double mean;
    double meanErr;
    double failRate;
    pullInfo(){
        sigma    = DEFAULT_TREE_VAL;
        sigmaErr = DEFAULT_TREE_ERR;
        mean     = DEFAULT_TREE_VAL;
        meanErr  = DEFAULT_TREE_ERR;
        failRate = DEFAULT_TREE_VAL;
    }
    pullInfo(double sig, double sigErr, double mu, double muErr, double rate){
        sigma    = sig;
        sigmaErr = sigErr;
        mean     = mu;
        meanErr  = muErr;
        failRate = rate;
    }
};

struct q2Bins{
    unsigned int nBins;
    std::vector<double> q2min;
    std::vector<double> q2max;

    q2Bins(basic_params params){
        nBins = params.nBins;
        q2min = get_TheQ2binsmin(nBins, params.reference);
        q2max = get_TheQ2binsmax(nBins, params.reference);
    }
};

const std::vector<std::vector<double>> bkg_340 = {{47.0,57.0},{0.0,15.0},{-10.0,0.0},{-25.0,-10.0},{-5.0,5.0},{-5.0,3.0},{-48.0,-38.0}};

//draw legend to the pull plots
void drawLegend(TLatex * leg, double q2min, double q2max, pullInfo info,
                int tot_fits, int oor_fits, int failed_fits){
    std::ostringstream bindescription;
    //draw q2 bin
    bindescription << std::setprecision(2) << std::fixed << "( " << q2min << " < q^{2} < " << q2max << " )";
    spdlog::trace(bindescription.str());
    leg->DrawLatex(0.21,0.88, bindescription.str().c_str());
    //draw mean value
    std::ostringstream sMean;
    sMean << std::fixed << std::setprecision(4) << std::fixed << info.mean << " #pm " << info.meanErr;
    spdlog::trace(sMean.str());
    leg->DrawLatex(0.13,0.80, "#bf{mean:}");
    leg->DrawLatex(0.13,0.77, sMean.str().c_str());
    //draw sigma value
    std::ostringstream sRMS;
    sRMS << std::fixed << std::setprecision(4) << std::fixed << info.sigma << " #pm " << info.sigmaErr;
    spdlog::trace(sRMS.str());
    leg->DrawLatex(0.13,0.73, "#bf{sigma:}");
    leg->DrawLatex(0.13,0.69, sRMS.str().c_str());
    //draw percentage of results in histo range
    //
    //std::ostringstream sInRange;
    //sInRange << std::fixed << std::setprecision(1) << std::fixed << 100. * (tot_fits - oor_fits - failed_fits) / (tot_fits - failed_fits) << "% in range";
    //spdlog::trace(sInRange.str());
    //leg->DrawLatex(0.13,0.64, sInRange.str().c_str());
    //draw failRate
    std::ostringstream sfailRate;
    if(!UnbinnedFit){
        sfailRate << std::fixed << std::setprecision(1) << std::fixed << info.failRate << "% failed";
        spdlog::trace(sfailRate.str());
        leg->DrawLatex(0.13,0.59, sfailRate.str().c_str());
    }
    return;

}

//function to load toy fit results of one angular observables and create pull (or residual) plots for each q2 bin.
//In this function the histogram is fitted using a single Gaussian bell shape. Either a standard binned fit or a RooFit unbinned fit
//weirdShape is there for the cases the fitter generates stuff with different parameters than it fits, eg different polynomial bkg description
std::vector<pullInfo> eval_toys(std::string filename, std::string treename, basic_params params,
                                bool doPulls, double pullRange_low, double pullRange_high, int whichPDf){

    const q2Bins bins(params);

    //configure TPad style format and suppress pop-up windows from TCanvas:
    gROOT->SetBatch(kTRUE);
    gROOT->SetStyle("Plain");
    TPad foo;
    set_gStyle();
    gStyle->SetOptTitle(0);
    gStyle->SetTitleFont(132, "t");
    gStyle->SetTextFont(132);
    gStyle->SetEndErrorSize(10.0);
    gErrorIgnoreLevel = kWarning;
    RooMsgService::instance().setGlobalKillBelow(RooFit::ERROR);


    //start loading the values from the toy fit results root files:
    std::vector<unsigned int> tot_fits(bins.nBins, 0); //counter for total fits
    std::vector<unsigned int> oor_fits(bins.nBins, 0); //counter for fit results outside the histogram range (oor=out of range)
    std::vector<unsigned int> failed_fits(bins.nBins, 0); //counter for fits with non-300 fit result

    std::vector<double> max_value(bins.nBins, -10.);
    std::vector<double> min_value(bins.nBins, +10.);

    std::vector<double>all_Values[bins.nBins];

    //load chain with all results of bootstrapping
    TChain * ch = new TChain(treename.c_str());
    Int_t nFiles = 0;

    //when wildcard [0-9] is used, add all files and  the digits of the counter until no files are found
    //[0-9] stands for any digit, i.e. the combination 'file_[0-9][0-9][0-9][0-9].root' would add all files from 'file_0000.root' to 'file_9999.root'
    if(filename.find("[0-9]") != std::string::npos){
        Int_t addedFiles = 1;
        while(addedFiles > 0){
            addedFiles = ch->Add(filename.c_str());
            //add another wildcard to the filename in order to search for files with job IDs in the next order of magnitude
            filename.replace(filename.find("[0-9]"), 5, "[0-9][0-9]");
            //Can someone explain to me why the fuck shoudl this be needed?
            //add the number of found files to the total number of files
            nFiles += addedFiles;
        }
    }
    else{ //if no wildcard '[0-9]' or the general wildcard '*' is used, simply add all files:
        nFiles = ch->Add(filename.c_str());
    }

    //check that at least one file with at least one entry is found:
    if(nFiles == 0){
        spdlog::error("No files found for name='"+filename+"'.");
        return {};
    }
    UInt_t nEntries = ch->GetEntries();
    if(nEntries == 0){
        spdlog::error("No entries found in files for tree='"+treename+"'.");
       return {};
    }
    //make sure that the number of entries is equivalent to the number of files times the number of q2 bins times the number of simultaneous PDFs
    if(TMath::Abs((int)(nEntries/(nPDFs*bins.nBins))) != TMath::Abs(nFiles)){
        spdlog::error("Number of entries found in files for tree='"+treename+"' does not match the number of files.");
        return {};
    }
    spdlog::info("[LOAD]\t\tReading {0:d} events from {1:d} files for variable='{2:s}'.", nEntries, nFiles, treename);

    //link variables to branches
    double startvalue= DEFAULT_TREE_VAL; //TODO: check the start_value of the parameter is not changed anywhere, as it isn't a const it is very likely it is changed
    double value     = DEFAULT_TREE_VAL;
    double error     = DEFAULT_TREE_ERR;
    double errorup   = DEFAULT_TREE_ERR;
    double errordown = DEFAULT_TREE_ERR;

    int migrad  = DEFAULT_TREE_INT;
    int cov     = DEFAULT_TREE_INT;
    int bin = DEFAULT_TREE_INT;
    int pdf = DEFAULT_TREE_INT;

    ch->SetBranchStatus("*",1); //TODO optimize
    ch->SetBranchAddress("value", &value);
    ch->SetBranchAddress("start_value", &startvalue);
    ch->SetBranchAddress("error", &error);
    ch->SetBranchAddress("error_up", &errorup);
    ch->SetBranchAddress("error_down", &errordown);
    ch->SetBranchAddress("migrad", &migrad);
    ch->SetBranchAddress("status_cov", &cov);
    ch->SetBranchAddress("bin", &bin);
    ch->SetBranchAddress("pdf", &pdf);

    //create a new simple tree that used the calculated pull or residual value, in case that unbinned fit is selected
    //this tree is used as input to the RooFitter
    TTree * unbinnedTree = nullptr;
    double x; //this variable is used for either the pull or residual value (depending on arg 'doPulls')
    if(UnbinnedFit){
        unbinnedTree = new TTree("tmp_tree", "tmp_tree");
        unbinnedTree->Branch("value", &x);
        unbinnedTree->Branch("bin", &bin);
    }
    //or else create histograms for the binned fit:
    TH1D * h_pull[bins.nBins];
    TH1D * h_values[bins.nBins];


    double binwidth = doPulls ? 0.5 : 0.05;//0.05;
    double xrange = doPulls ? 2.0*pullRange_high : 1.0;//2.0;
    if(!UnbinnedFit){
        for(UInt_t b = 0; b < bins.nBins; b++){ //FUCK PEOPLE WHO DON'T DO BRACKETS
            h_pull[b] = new TH1D((treename+"_bin"+std::to_string(b)).c_str(),
                            (treename+" in q^{2} bin #"+std::to_string(b)+(doPulls ? ";(x-x_{nomi.})/#sigma" : ";(x-x_{nomi.})")+";entries").c_str(),
                            2*xrange/binwidth, -xrange, +xrange);
        }
    }
    spdlog::debug("Finished initializing the histograms");

    //load results from files:
    for(UInt_t e = 0; e < nEntries; e++){
        //since all PDFs are saved in the TTree, only use 8(5) q2bins out of 32(10) for the KS0(pi0) channel
        //Eeee... e?
        //if(e%(bins.nBins*nPDFs) > (bins.nBins-1)) continue;

        ch->GetEntry(e);

        if(pdf != whichPDf) continue;
        spdlog::trace("Parameter value at entry {0:d}: {1:f}", e, value);
        all_Values[bin].push_back(value);
        tot_fits.at(bin)++;

        //require convered migrad
        if(migrad != 0){
            failed_fits.at(bin)++;
            continue;
        }
        //require fitresult = 300 or 100 (or 200)
        if(!(cov == 1 || cov == 3 || cov == 2)){
            failed_fits.at(bin)++;
            continue;
        }

        //derive residual and pull
        double diff = value - startvalue;
        if(error == 0. || isnan(error)){
            spdlog::error("[{0:s}][BIN{1:d}]{2:d}\tError equal to zero!", treename, bin, e);
            return {};
        }
        double pull = diff/error;
        spdlog::trace("[{0:s}][BIN{1:d}]{2:d}\tdiff={3:f}\tpull={4:f}\terr={5:f}", treename, bin, e, diff, pull, error);

        if(UnbinnedFit){
            //assign pull or residual to 'x' and save in TTree
            x = doPulls ? pull : diff; //x, pull and diff is never used? //TODO            
            unbinnedTree->Fill();
        }
        else{//Binned fit
            if(TMath::Abs(diff) <= xrange){
                if(doPulls){
                    //fill pull to histogram:
                    h_pull[bin]->Fill(pull);
                    //determine range of pull values
                    max_value.at(bin) = std::max(max_value.at(bin),pull);
                    min_value.at(bin) = std::min(min_value.at(bin),pull);
                }
                else{
                    h_pull[bin]->Fill(diff);
                    //determine range of diff values
                    max_value.at(bin) = std::max(max_value.at(bin),diff);
                    min_value.at(bin) = std::min(min_value.at(bin),diff);
                }
            }
            else{
                oor_fits.at(bin)++;
            }
        }

    }

    spdlog::debug("Filled all histograms/TTrees with values of variable="+treename+".");
    spdlog::debug("Tree size="+treename+".");

    //vectors to store the measured mean and widths of the pull(residual) distributions
    std::vector<pullInfo> allInfo;

    //init some nullptr RooFit objects, to make the compiler happy. (These two are needed later in the plotting of the histograms)
    RooGaussian  * roogaus[bins.nBins];
    RooDataSet   * roodata[bins.nBins];
    RooRealVar   * roovalue[bins.nBins];

    for(UInt_t b = 0; b < bins.nBins; b++){ //loop over q2bins to fit all distributions
        if(UnbinnedFit){
            //initialise RooFit objects for the unbinned fit:
            RooRealVar   * roomean, * roosigma, * roobin;
            roovalue[b] = new RooRealVar("value", "value", pullRange_low, pullRange_high, "");
            roomean     = new RooRealVar("mean", "mean", bin_center(pullRange_low,pullRange_high), pullRange_low, pullRange_high);
            roosigma    = new RooRealVar("sigma", "sigma", doPulls ? 0.85: 0.1, 0., 2.);
            roobin      = new RooRealVar("bin", "q2 bin", -1., 10.0, "");
            RooArgSet rooarg   = RooArgSet(*roovalue[b]);
            rooarg.add(*roobin);
            roogaus[b]  = new RooGaussian("RooGaus", "RooGaus", *roovalue[b], *roomean, *roosigma);
            //RooFitResult * result   = new RooFitResult("FitResult","FitResult");

            spdlog::debug("Start fitting q2 bin {0:d} for par={1:s}.", b, treename);
            roodata[b]  = new RooDataSet("RooDataSet", "RooDataSet", unbinnedTree, rooarg,
                                         ("abs(bin-"+std::to_string(b)+") < 0.1").c_str());
            spdlog::debug("Fit {0:d} events", roodata[b]->numEntries());
            if(roodata[b]->numEntries() == 0){
                spdlog::critical("Empty fit not possible for var={0:s} in q2bin={1:d}", treename, b);
                assert(0);
            }
            int fitstatus = roogaus[b]->fitTo(*roodata[b], RooFit::Save(kTRUE),RooFit::PrintLevel(spdlog_trace()?1 : -1))->status();
            spdlog::debug("[{0:s}][BIN{1:d}]\tFitstatus={2:d}", treename, b, fitstatus);
            allInfo.push_back(pullInfo(roosigma->getVal(),roosigma->getError(),
                                       roomean->getVal(),roomean->getError(),fitstatus));
        }//end unbinned fit
        else{//binned fit
            double entries = h_pull[b]->Integral();
            bool dofit = entries > 10.0 && h_pull[b]->GetRMS(1) > 0.001;
            spdlog::debug("var={0:s}\t q2bin={1:d}\t DOFIT={2:s}", treename, b, (dofit ? "TRUE" : "FALSE"));

            if(entries < 0.0){
                spdlog::warn("Negative entries for: q2bin={0:d}\t par={1:s}\t entries={2:d}", b, treename, entries);
            }

            int fitstatus = 0.0;
            if(tot_fits.at(b) > 0) fitstatus = (100.0 * failed_fits.at(b) / tot_fits.at(b));

            if(dofit){
                double minfit = -xrange;
                double maxfit = +xrange;
                TF1 * fGauss = new TF1("theGaussian", "gaus(0)", minfit, maxfit);
                fGauss->SetParameter(0, entries);
                fGauss->SetParameter(1, h_pull[b]->GetXaxis()->GetBinCenter(h_pull[b]->GetMaximumBin()));
                if(!doPulls){
                    double mean_range = (treename.find("P") != std::string::npos || treename == "Fl") ? 0.05 : 0.01;
                    fGauss->SetParLimits(1, h_pull[b]->GetXaxis()->GetBinCenter(h_pull[b]->GetMaximumBin()) - mean_range, h_pull[b]->GetXaxis()->GetBinCenter(h_pull[b]->GetMaximumBin()) + mean_range);
                }
                fGauss->SetParameter(2, h_pull[b]->GetRMS(1));
                fGauss->SetParLimits(2, 0.0, doPulls ? 1.5 : 0.1);
                fGauss->SetLineStyle(kDashed);
                fGauss->SetLineColor(kMagenta - 3);
                h_pull[b]->Fit(fGauss, "RQM+");
                //save values in a vector
                allInfo.push_back(pullInfo(fGauss->GetParameter(2),fGauss->GetParError(2),
                                           fGauss->GetParameter(1),fGauss->GetParError(1),fitstatus));
            }
            else{//only use RMS and MEAN obtained by TH1F
                allInfo.push_back(pullInfo(h_pull[b]->GetRMS(1),h_pull[b]->GetRMSError(1),
                                           h_pull[b]->GetMean(1),h_pull[b]->GetMeanError(1),fitstatus));
            }
        }//end of binned fit
    }//end of loops of q2 bins


    //save histogram with pulls (or residuals) and the value distributions to file
    TCanvas* c1 = new TCanvas("c1", "c1", 1600, 1200);
    c1->cd()->SetMargin(0.1,0.05,0.1,0.05);
    for(UInt_t b = 0; b < bins.nBins; b++){ //loop over bins.nBins
        spdlog::debug("Plotting bin {0:b}.",b);

        c1->cd();
        if(UnbinnedFit){
            RooPlot * rooframe = roovalue[b]->frame();
            roodata[b]->plotOn(rooframe);
            roogaus[b]->plotOn(rooframe);
            rooframe->Draw();
        }
        else{
            h_pull[b]->Draw();
        }

        //initiliaze and format a legend:
        TLatex * leg = getPrettyTex(0.07,13);
        leg->SetTextColor(kBlack);

        //draw variable name
        leg->DrawLatex(0.13,0.89, treename.c_str());
        leg->SetTextSize(0.04);
        drawLegend(leg,bins.q2min.at(b) ,bins.q2max.at(b),
                   allInfo[b],tot_fits[b],oor_fits[b],failed_fits[b]);
        c1->Print((get_ToyPullPlot_tag(b,treename,params,false,doPulls) +".eps").c_str(), "eps");
        if(!UnbinnedFit) delete h_pull[b];

        double max = *max_element(all_Values[b].begin(), all_Values[b].end());
        double min = *min_element(all_Values[b].begin(), all_Values[b].end());

        h_values[b] = new TH1D(treename.c_str(),
                              (treename+" in q^{2} bin #"+std::to_string(b)+";"+treename+";Entries").c_str(),
                               nFiles/5, min, max);
        for (auto val: all_Values[b]){
            h_values[b]->Fill(val);
        }


        //save histogram with values to file
        plotAndSave(h_values[b],treename,get_ToyPullPlot_tag(b,treename,params,true, false),"eps");


    }//end loop over q2 bins

    delete c1;
    return allInfo;
}

//print the results of the toy studies to latex tables:
int print_latex_tables(bool Pprimes, bool doPulls, std::vector< std::string> LaTeXnames, std::vector< std::vector<pullInfo> > lePullInfo,  const q2Bins bins, int jobID){
    if(lePullInfo.size() == 0){
        spdlog::error("No results found for widths of pull/residual distributrionn");
        return 1;
    }

    //generate color palette:
    TPad foo;
    TColor::InitializeColors();
    Double_t stops[9] = { 0.0000, 0.1250, 0.2500, 0.3750, 0.5000, 0.6250, 0.7500, 0.8750, 1.0000};
    Double_t red[9]   = {  55./255.,  55./255.,  55./255.,  55./255.,  55./255., 105./255., 155./255., 205./255., 255./255.};
    Double_t green[9] = {  55./255., 105./255., 155./255., 205./255., 255./255., 205./255., 155./255., 105./255.,  55./255.};
    Double_t blue[9]  = { 255./255., 205./255., 155./255., 105./255.,  55./255.,  55./255.,  55./255.,  55./255.,  55./255.};
    Idx = TColor::CreateGradientColorTable(9, stops, red, green, blue, 255);

    //in what range should the colors be defined:
    double colorrange[] = {doPulls ? 0.2 : (Pprimes ? 0.2 : 0.05),
                           doPulls ? 0.2 : (Pprimes ? 0.2 : 0.05),
                           UnbinnedFit ? 2. : 20.};
    std::vector<std::string> tabnames = {"width", "mean"};//, (UnbinnedFit ? "fitstatus" : "failRate")};

    clear_Latex_noteFile(latex_toyFile()+"_"+std::to_string(jobID)+std::string(doPulls?"":"_res")); //First make sure you don't append stuff
    std::ofstream myFile; //TODO: fix the latex name file
    open_Latex_noteFile(latex_toyFile()+"_"+std::to_string(jobID)+std::string(doPulls?"":"_res"), myFile); //open latex file

    for(unsigned int i = 0; i < tabnames.size(); i++){
        myFile << "\\begin{frame} \\footnotesize \\centering" << std::endl;
        myFile << "\\begin{tabular}{|l|";
        for(unsigned int b = 0; b < bins.nBins; b++) myFile << "p{2.2cm}";
        myFile << "|}\\hline" << std::endl;
        myFile << "\\textbf{" << tabnames[i] << "}";
        //for(unsigned int b = 0; b < bins.nBins; b++)myFile << "\t& " << b;
        for(unsigned int b = 0; b < bins.nBins; b++)myFile << std::setprecision(2) << std::fixed << "\t&[" << bins.q2min.at(b) << "--" << bins.q2max.at(b) << "]";
        myFile << "\\\\" << std::endl;
        myFile << "\\hline\\hline" << std::endl;
        //TABLE CONTENT:
        for(unsigned int v = 0; v < lePullInfo.size(); v++){
            if (lePullInfo.at(v).size()==0) continue;
            myFile << "$" << LaTeXnames.at(v)  << "$ ";
            for(unsigned int b = 0; b < bins.nBins; b++){
                //get correct value for mean,sigma,failRate at q2bin and variable
                double levalue  = 0.0;
                double leerror  = 0.0;
                if(i == 0){
                    levalue = lePullInfo.at(v)[b].sigma;
                    leerror = lePullInfo.at(v)[b].sigmaErr;
                }
                else if(i == 1){
                    levalue = lePullInfo.at(v)[b].mean;
                    leerror = lePullInfo.at(v)[b].meanErr;
                }
                else{
                    levalue = lePullInfo.at(v)[b].failRate; //TODO
                }
                myFile << "\t&";
                //generate cell background color accordingly to the value:
                if(UseCellColor){
                    //TColor * colour = gROOT->GetColor(Idx + TMath::Min(254, (int) ((TMath::Abs((doPulls && i == 0 ? levalue - 1. :  levalue))/2./colorrange[i]+0.5)*255.)));
                    int coloridx = ((doPulls && i == 0 ? levalue - 1.0 : levalue)/colorrange[i])*127;
                    if(coloridx < -127)coloridx = -127;
                    if(coloridx >  127)coloridx = 127;
                    TColor * colour = gROOT->GetColor(Idx + 127 + coloridx);
                    if(colour==nullptr)spdlog::error("Color with idx={0:d} not found", Idx + 127 + coloridx);
                    else{
                        std::string leColour(colour->AsHexString());
                        std::transform(leColour.begin(), leColour.end(),leColour.begin(), ::toupper);
                        myFile << "\\cellcolor[HTML]{" << leColour.substr(1,6) << "} ";
                    }
                }
                //draw cell content
                myFile << "$";
                if(levalue>=0.0)myFile << "\\phantom{-}";//align columns by adding an invisible minus sign
                myFile << std::setprecision(3) << std::fixed << levalue << " \\pm " << leerror << "$";
            }
            myFile << "\\\\" << std::endl;
        }
        //END OF TABLE CONTENT
        myFile << "\\hline" << std::endl;
        myFile << "\\end{tabular}" << std::endl << std::endl;
        myFile << "\\end{frame}" << std::endl;
    }
    myFile.close();
    return 0;
}

void save2rootFile( basic_params params, std::vector< std::string> treeNames, std::vector< std::string> LaTeXnames, std::vector< std::vector<pullInfo> > lePullInfo, const q2Bins bins){
    TFile * file = new TFile((get_ToyPullPlot_folder(params)+"pullResults.root").c_str(), "RECREATE");
    spdlog::debug("Opening "+std::string(file->GetPath()));
    file->cd();
    for(UInt_t t = 0; t < treeNames.size(); t++){
        if (lePullInfo[t].size()==0) continue;
        TTree * tree = new TTree(treeNames[t].c_str(), LaTeXnames[t].c_str());
        spdlog::debug("Saving tree "+ treeNames[t]);
        double mean = DEFAULT_TREE_VAL;
        double width = DEFAULT_TREE_ERR;        
        tree->Branch("mean", &mean, "mean/D");
        tree->Branch("width", &width, "width/D");
        for(UInt_t b = 0; b < bins.nBins; b++){
            mean = lePullInfo[t][b].mean;
            width = lePullInfo[t][b].sigma;
            spdlog::debug("mean:\t{0:f}",mean);
            spdlog::debug("width:\t{0:f}",width);
            tree->Fill();
        }
        tree->Write();
        delete tree;
    }
    file->Close();
    delete file;
}

const std::vector<std::string>colorPallete =
                                    {"0d3c7b","2b6d97","499eb3","67cfcf","85ffeb","7ce9c8","72d2a5","69b282",
                                     "5FA55F",
                                     "84BC70", "A8D281","CCE892","F0FEA2","DABF7A","C47F51","AE4029","970000"};

/*
const std::vector<std::string>colorPallete =
                                    {"93006A","751778","562D86","374494","185AA2","2A6D92","3C8081","4E9370",
                                     "5FA55F",
                                     "84BC70", "A8D281","CCE892","F0FEA2","DABF7A","C47F51","AE4029","970000"};
*/
/*
const std::vector<std::string>colorPallete =
                                    {"0d3c7b","0e5d85","0e7d8e","0f9d97","0fbda0","0cae7b","089f56","049031",
                                     "00800B",
                                     "3B9714", "76ae1d","b1c526","EBDB2e","d9a523","c76e17","B5370c","A30000"};
*/

const std::vector<double> colStops = {-1.000,-0.8750,-0.7500,-0.6250,-0.5000,-0.3750,-0.2500,-0.1250,
                                      0.0000,
                                      0.1250, 0.2500, 0.3750, 0.5000, 0.6250, 0.7500, 0.8750, 1.0000};

std::string getCol(double val){
    int len = colStops.size();
    for (int i = 0; i < len; i++){
        if (colStops[i+1]>val) return colorPallete[i];
    }
    return colorPallete[len-1];
}

int save2texFileNew(std::vector< std::string> LaTeXnames, std::vector< std::vector<pullInfo> > lePullInfo,  const q2Bins bins, int jobID){
    if(lePullInfo.size() == 0){
        spdlog::error("No results found for widths of pull/residual distributrionn");
        return 1;
    }

     std::vector<std::string> tabnames = {"width", "mean"};//, (UnbinnedFit ? "fitstatus" : "failRate")};

    clear_Latex_noteFile(latex_toyFile()+"_"+std::to_string(jobID)); //First make sure you don't append stuff
    std::ofstream myFile; //TODO: fix the latex name file
    open_Latex_noteFile(latex_toyFile()+"_"+std::to_string(jobID), myFile); //open latex file

    for(unsigned int i = 0; i < tabnames.size(); i++){
        myFile << "\\begin{frame} \\footnotesize \\centering" << std::endl;
        myFile << "\\begin{tabular}{|l|";
        for(unsigned int b = 0; b < bins.nBins; b++) myFile << "p{2.2cm}";
        myFile << "|}\\hline" << std::endl;
        myFile << "\\textbf{" << tabnames[i] << "}";
        //for(unsigned int b = 0; b < bins.nBins; b++)myFile << "\t& " << b;
        for(unsigned int b = 0; b < bins.nBins; b++)myFile << std::setprecision(2) << std::fixed << "\t&[" << bins.q2min.at(b) << "--" << bins.q2max.at(b) << "]";
        myFile << "\\\\" << std::endl;
        myFile << "\\hline\\hline" << std::endl;
        //TABLE CONTENT:
        for(unsigned int v = 0; v < lePullInfo.size(); v++){
            if (lePullInfo.at(v).size()==0) continue;
            myFile << "$" << LaTeXnames.at(v)  << "$ ";
            for(unsigned int b = 0; b < bins.nBins; b++){
                //get correct value for mean,sigma,failRate at q2bin and variable
                double levalue  = 0.0;
                double leerror  = 0.0;
                if(i == 0){
                    levalue = lePullInfo.at(v)[b].sigma;
                    leerror = lePullInfo.at(v)[b].sigmaErr;
                }
                else if(i == 1){
                    levalue = lePullInfo.at(v)[b].mean;
                    leerror = lePullInfo.at(v)[b].meanErr;
                }
                else{
                    levalue = lePullInfo.at(v)[b].failRate; //TODO
                }
                myFile << "\t&";
                //generate cell background color accordingly to the value:
                if(UseCellColor){
                    if (i==0) myFile << "\\cellcolor[HTML]{" << getCol(levalue-1.0) << "} ";
                    else      myFile << "\\cellcolor[HTML]{" << getCol(levalue) << "} ";
                }
                //draw cell content
                myFile << "$";
                if(levalue>=0.0)myFile << "\\phantom{-}";//align columns by adding an invisible minus sign
                myFile << std::setprecision(3) << std::fixed << levalue << " \\pm " << leerror << "$";
            }
            myFile << "\\\\" << std::endl;
        }
        //END OF TABLE CONTENT
        myFile << "\\hline" << std::endl;
        myFile << "\\end{tabular}" << std::endl << std::endl;
        myFile << "\\end{frame}" << std::endl;
    }
    myFile.close();
    return 0;
}

//MAIN FUNCTION to run over all observables and q2bin.
//the function calls the loading and fitting function 'eval_toys()'
//then runs the print and save functions
int EvaluateToyStudy(const std::vector<std::string> obs, const std::vector<std::string> obs_latex, basic_params params, bool UseFolds, bool doPulls, bool Pprimes, bool onlySig, bool onlyBkg){

    //define names of observables and which foldings are taken for which observable
    std::vector<UInt_t> folding_idx;

    if(Pprimes) folding_idx = {3, 3, 0, 0, 1, 2, 3, 4};
    else        folding_idx = {3, 3, 1, 2, 0, 3, 4, 0};

    std::vector<int> weirdRangeJobs = {340, 343, 344, 349, 350, 353, 354, 355, 362, 379, 382, 497,558, 559, 560, 561, 562, 563}; //jobIDs with off pull ranges
    double lowRange = -5.0;
    double highRange = 5.0;

    std::vector<int> ctkRange = {497, 558, 559, 560, 561, 562, 563, 566, 567, 568, 569, 570, 571, 620, 621, 622, 623, 624, 625, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648};


    //configure the path and tags of the toy study result files
    std::string PPtag     = Pprimes ? "_Pprimes" : "";
    std::string suffix    = ""; //TODO
    makeFolder(get_ToyPullPlot_folder(params));
    const q2Bins bins(params);

    //using this status for error propagation
    std::vector< std::vector<pullInfo>> leInfo = {};

    for(UInt_t p = 0; p < obs.size(); p++){
        lowRange = -5.0; //Reset the ranges again in case
        highRange = 5.0;
        if(params.reference && ( isInVec(obs.at(p),ANG_OBS) || isInVec(obs.at(p),SWAVE))){
            lowRange = -1.0; //Reset the ranges again in case
            highRange = 1.0;
        }
        if(params.reference && params.folding>-1 && ( isInVec(obs.at(p),ANG_OBS) || isInVec(obs.at(p),SWAVE))){
            lowRange = -0.5; //Reset the ranges again in case
            highRange = 0.5;
        }
        if(params.reference && params.folding==4 && ( isInVec(obs.at(p),ANG_OBS) || isInVec(obs.at(p),SWAVE))){
            lowRange = -5.0; //TODO
            highRange = 5.0;
        }
        if(params.reference && params.folding>-1 && obs.at(p)=="FS"){
            lowRange = -0.25; //Reset the ranges again in case
            highRange = 0.25;
        }
        if(UseFolds)params.folding = folding_idx.at(p);

        //get a filename accordingly to the basic_params settings, then replace the number 1111 by the wildcard
        //and then replace the folder ToysFit by the subfolder created by condor
        std::string files = final_result_name_toys(1111, params.reference, params.nBins, true, params, params.Run, false, false, onlySig, onlyBkg, false, true);                
        replace(files, "1111", "*"); //integer replaced by a string *
        replace(files, "ToysFit/", "ToysFit/"+std::to_string(params.jobID)+"/");
        spdlog::info("Read pdfs from file='"+files+"'");

        //Set the range for weird pulls in ctk
        if (isInVec(params.jobID, weirdRangeJobs) && p>7 && p<15){ //This is a dirty hack
            lowRange = -50.0;
            highRange = +50.0;
        }
        if ((params.jobID==503 || (params.jobID>=509 && params.jobID<=512)) && (p==11 || p ==12)){ //This is a dirty hack
            lowRange = -50.0;
            highRange = +50.0;

        }

        if ((params.jobID==551 && (p==1))){ //This is a dirty hack
           leInfo.push_back({});
           continue;
        }
        if ((params.jobID==551 && (p==3))){
            lowRange = -10.0;
            highRange = 0.0;
        }

        if (params.jobID==566 && (p==9)){ //This is a dirty hack
           leInfo.push_back({});
           continue;
        }

        if (params.jobID==567 && (p==9)){ //This is a dirty hack
           leInfo.push_back({});
           continue;
        }
        if (isInVec(params.jobID,ctkRange) && (p==8|| p==9)){
            lowRange = -5.0;
            highRange = 15.0;
        }
        if (isInVec(params.jobID,ctkRange) && params.jobID>619 && (obs.at(p)=="cbkgctk1")){
            leInfo.push_back({});
            continue; //TODO WTF
            lowRange = 10.0;
            highRange = 30.0;
        }
        if (isInVec(params.jobID,ctkRange) && params.jobID>619 && (obs.at(p)=="cbkgctk2")){
            leInfo.push_back({});
            continue;//TODO WTF
            lowRange = 20.0;
            highRange = 45.0;
        }
        if (params.jobID==571 && (p==9)){ //This is a dirty hack
            leInfo.push_back({});
            continue;
        }

        if (params.jobID>619 && (obs.at(p)=="cbkgctl2")){ //This is a dirty hack
            leInfo.push_back({});
            continue;//TODO, actually anythig below zero possible depending on the bin
            lowRange = -15.0;
            highRange = -5.0;
        }
        //if (params.jobID==637 && (obs.at(p)=="SS5")){ //This is a dirty hack
        //    continue;
        //}
        if (params.jobID==630 && (p==9)){ //This is a dirty hack
            leInfo.push_back({});
            continue;
        }
        if (params.jobID==650 && (p==9)){
            leInfo.push_back({});
            continue;
        }
        //evalute the files for this observable and check the status
        leInfo.push_back(eval_toys(files, obs.at(p), params, doPulls, lowRange, highRange, 1));
    }

    if (doPulls){
        //print results to latex table into 'cout' and save to a root file
        if (params.reference) save2rootFile(params,obs, obs_latex,leInfo, bins); //This could be done for each value
        return save2texFileNew(obs_latex,leInfo,bins,params.jobID);
    }
    else return  print_latex_tables(params.usePprime,false,obs_latex,leInfo, bins, params.jobID);
}


