//Renata Kopecna

#include <backgroundfit.hh>

#include <fstream>
#include <sstream>      // std::istringstream

#include <bu2kstarmumu_parameters.hh>
#include <bu2kstarmumu_plotter.hh>
#include <bu2kstarmumu_pdf.hh>
#include <folder.hh>
#include <fitter.hh>
#include <paths.hh>
#include <design.hh>
#include <helpers.hh>
#include <event.hh>

#include <spdlog.h>

#include <TStyle.h>
#include <TH2D.h>
#include <TLatex.h>
#include <TCanvas.h>
#include <TROOT.h>

//Do the actuall background fit
int backgroundfit(fcnc::options opts,
                  bool fitReference, bool LowMassFit, bool HighMassFit, bool fitKpi,
                  bool Use2DAngularBins, basic_params params){
    //params now only used to tag nBins in the names

    //Technically, this should be simFit between Run 1 and 2, but since we need it only for the toys atm, no need for it

    gROOT->SetBatch(kTRUE);
    gROOT->SetStyle("Plain");
    set_gStyle();

    //Open texFile
    std::ofstream myFile;
    open_Latex_noteFile(latex_bkgFit(), myFile);

    spdlog::info("[FIT]\t\tFit only angular and m(Kpi) background");


    //Used to check whether the background is "correlated" between each other
    //TODO: not needed atm, maybe fix later
    /* 1D angular bins
      1:        ctl < -0.5
      2: -0.5 < ctl <  0.0
      3:  0.0 < ctl <  0.5
      4:  0.5 < ctl
      5:        ctk < -0.5
      6: -0.5 < ctk <  0.0
      7:  0.0 < ctk <  0.5
      8:  0.5 < ctk
      */
    /* 2D angular bins
      1: ctl < 0.0 && ctk < 0.0
      2: ctl < 0.0 && ctk > 0.0
      3: ctl > 0.0 && ctk < 0.0
      4: ctl > 0.0 && ctk > 0.0
      */

    double HighBpeakCut = fitReference ? B_MASS_HIGH_BKG_REF : B_MASS_HIGH_BKG;
    double LowBpeakCut  = fitReference ? B_MASS_LOW_BKG_REF : B_MASS_LOW_BKG;

    assert(!(HighMassFit && LowMassFit));

    opts.fit_full_angular_bkg = true;

    opts.only_angles = !(HighMassFit || LowMassFit); //Fit mass only if one sideband is selected
    opts.only_Bmass = false;
    opts.only_mkpi = false;

    opts.swave = false;

    opts.bkg_order_costhetak = 5;
    opts.shift_lh = false; //keep it, it was there before
    opts.weighted_fit = true;
    opts.squared_hesse = true;
    opts.minos_errors = false;
    opts.asymptotic = false;

    opts.flat_bkg = false;

    opts.fit_mkpi = false; //set to true only later
    opts.use_mkpi = false;
    opts.simple_mkpi = false; //S-wave model
    opts.isobar = false; //S-wave model

    //Plotting options
    opts.plot_chi2 = false;
    opts.plots_m_bins = 20;
    opts.plots_mkpi_bins = 20;

    //load events from data tuple
    std::vector<std::vector<fcnc::event>>events;
    std::vector<int> run_idx;

    if (opts.run == 1 || opts.run == 12){
        std::vector<fcnc::event> tmp = fcnc::load_events(get_theFCNCpath(0,1), "Events", -1);
        if (!fitReference) events.push_back(fcnc::filterResonances(tmp));
        else               events.push_back(tmp);
        run_idx.push_back(1);
    }
    if (opts.run == 2 || opts.run == 12){
        std::vector<fcnc::event> tmp = fcnc::load_events(get_theFCNCpath(0,2), "Events", -1);
        if (!fitReference) events.push_back(fcnc::filterResonances(tmp));
        else               events.push_back(tmp);
        run_idx.push_back(2);
    }

    std::vector<int> fitresults;

    //determine number of bins:
    const UInt_t nBins = opts.TheQ2binsmin.size();

    //current fitter, plotter, parameteres and pdfs:
    fcnc::fitter f(&opts);
    fcnc::folder fldr(&opts);
    fcnc::bu2kstarmumu_plotter thePlotter(&opts);

    fcnc::bu2kstarmumu_parameters * theParams[nBins];
    fcnc::bu2kstarmumu_pdf        * theProb[nBins];
    std::vector<fcnc::event> * theEvents[nBins];


    for(UInt_t b = 0; b < nBins; b++){

        theParams[b] = new fcnc::bu2kstarmumu_parameters(&opts);
        theProb[b]   = new fcnc::bu2kstarmumu_pdf(&opts, theParams[b]);
        theEvents[b] = new std::vector<fcnc::event>();

        //Init parameters
        theParams[b]->f_sig.init_fixed(0.0);
        theParams[b]->m_b.init(PDGMASS_B, HighMassFit ? HighBpeakCut : B_MASS_LOW, LowMassFit ? LowBpeakCut : B_MASS_HIGH, 0.0);
        //The rest of the mass parameters can be left to default values
        //They are defined in bu2kstamumu_parameters

        //Init the bkg shape
        theParams[b]->init_angular_background_parameters(fitReference,0.1);

        //Init the mass bkg shape
        theParams[b]->init_mass_background_parameters(nBins,b,true);
        //theParams[b]->cbkgctk3.init_fixed(-2.5);

        //Select events        
        for_indexed(auto evts: events){
            //update weights according to sub-set for all events
            opts.run  = run_idx.at(i);
            opts.update_efficiencies = true;

            theProb[b]->load_coeffs_eff_phsp_4d();
            theProb[b]->update_cached_normalization(theParams[b]);
            theProb[b]->update_cached_efficiencies(theParams[b], &evts);
            opts.update_angle_ranges(); //Update angles to get rid of events that cannot be folded
            //select events
            spdlog::debug("Loop over {0:d} events and select the suitable ones", evts.size());
            for(auto meas: evts){

                if(meas.q2 < opts.TheQ2binsmin.at(b) || meas.q2 > opts.TheQ2binsmax.at(b)) continue;
                if(meas.m < opts.m_low || meas.m > opts.m_high) continue;

                if(LowMassFit && meas.m > LowBpeakCut) continue;
                else if(HighMassFit && meas.m < HighBpeakCut) continue;
                else if(meas.m > LowBpeakCut && meas.m < HighBpeakCut) continue;

                if(meas.mkpi < opts.mkpi_min || meas.mkpi > opts.mkpi_max) continue;

                if(params.bin > 1){
                    bool keep_event = true;
                    if(Use2DAngularBins){
                        switch(params.bin){
                        case 2:
                            if(meas.costhetal > 0.0 || meas.costhetak > 0.0) keep_event = false;
                            break;
                        case 3:
                            if(meas.costhetal > 0.0 || meas.costhetak < 0.0) keep_event = false;
                            break;
                        case 4:
                            if(meas.costhetal < 0.0 || meas.costhetak > 0.0) keep_event = false;
                            break;
                        case 5:
                            if(meas.costhetal < 0.0 || meas.costhetak < 0.0) keep_event = false;
                            break;
                        }
                    }
                    else{ //1D angular bins
                        switch(params.bin){
                        case 2:
                            if(meas.costhetal > -0.5) keep_event = false;
                            break;
                        case 3:
                            if(meas.costhetal < -0.5 || meas.costhetal > 0.0) keep_event = false;;
                            break;
                        case 4:
                            if(meas.costhetal < 0.0 || meas.costhetal > 0.5) keep_event = false;;
                            break;
                        case 5:
                            if(meas.costhetal > 0.5) keep_event = false;;
                            break;
                        case 6:
                            if(meas.costhetak > -0.5) keep_event = false;;
                            break;
                        case 7:
                            if(meas.costhetak < -0.5 || meas.costhetak > 0.0) keep_event = false;;
                            break;
                        case 8:
                            if(meas.costhetak < 0.0 || meas.costhetak > 0.5) keep_event = false;;
                            break;
                        case 9:
                            if(meas.costhetak > 0.5) keep_event = false;;
                            break;
                        }
                    }
                    if(!keep_event) continue;
                }

                if(!filterFldFour(&meas, &opts)) continue;
                fldr.fold(&meas);

                //only one combined event vector
                theEvents[b]->push_back(meas);
            }
            spdlog::debug("Selected {0:d} events.", theEvents[b]->size());
        }
        spdlog::info("[BIN {0:d}]\tDone!",b);
    }//end loop over bins

    //do not update cached efficiencies anyomre, to keep individual ones for four sub-sets
    opts.update_efficiencies = false;

    //create 2D correlation plots for all angles (3) and all bins (8): 3! * 8 = 24
    const unsigned int nANGLES = 3;

    double hmin[nANGLES] = {CTL_MIN, CTK_MIN, PHI_MIN};
    double hmax[nANGLES] = {CTL_MAX, CTK_MAX, PHI_MAX};
    double corrfactor[nBins][nANGLES][nANGLES];
    const int nCorrBins = 10;
    for(UInt_t b = 0; b < nBins; b++){
        spdlog::info("[START]\tStart the background 2D correlation plotting for bin #{0:d}", b);
        for(UInt_t a = 0; a < nANGLES - 1; a++){
            for(UInt_t aa = a + 1; aa < nANGLES; aa++){
                std::string mainName = "bckgnd_correl_"+ANGLES[a]+"_"+ANGLES[aa]+"_q2bin"+std::to_string(b);
                TCanvas * cAngCorr = new TCanvas(("c"+mainName).c_str(),
                                                 ("c"+mainName).c_str(), 1400, 1200);
                cAngCorr->cd()->SetMargin(0.1,0.125,0.125,0.125);
                cAngCorr->cd();
                TH2D * h = new TH2D((mainName).c_str(),
                                    (mainName+";"+latex_angles[a]+";"+latex_angles[aa]).c_str(),
                                    nCorrBins, hmin[a], hmax[a], nCorrBins, hmin[aa], hmax[aa]);

                for(UInt_t e = 0; e < theEvents[b]->size(); e++){
                    fcnc::event meas = theEvents[b]->at(e);
                    double x=0.0, y=0.0;
                    switch(a){ //TODO: make a function in helpers
                    case 0:
                        x = meas.costhetal;
                        break;
                    case 1:
                        x = meas.costhetak;
                        break;
                    }
                    switch(aa){
                    case 1:
                        y = meas.costhetak;
                        break;
                    case 2:
                        y = meas.phi;
                        break;
                    }
                    h->Fill(x, y);
                }
                h->GetZaxis()->SetRangeUser(0, fitReference ? 25 : 5);
                h->GetZaxis()->SetTitle("Number of entries");

                h->GetXaxis()->SetTitleSize(0.05);
                h->GetXaxis()->SetLabelSize(0.05);
                h->GetXaxis()->SetTitleOffset(0.95);

                h->GetYaxis()->SetTitleSize(0.05);
                h->GetYaxis()->SetLabelSize(0.05);
                h->GetYaxis()->SetTitleOffset(0.75);

                h->GetZaxis()->SetTitleSize(0.05);
                h->GetZaxis()->SetLabelSize(0.05);
                h->GetZaxis()->SetTitleOffset(0.75);

                h->SetTitle("");
                h->Draw("COLZ");

                corrfactor[b][a][aa] = h->GetCorrelationFactor();

                TLatex * leg = getPrettyTex(0.06,13);
                leg->SetTextColor(kBlack);
                std::ostringstream sCorrout; //TODO: move the path
                sCorrout << std::fixed << std::setprecision(2) << "corr(" << latex_angles[a] << ", " << latex_angles[aa] <<") = " <<  corrfactor[b][a][aa]*100. << "\%";
                leg->DrawLatex(0.22,0.98, sCorrout.str().c_str());
                leg->SetTextSize(0.04);
                leg->DrawLatex(0.25,0.92, ("Events: "+std::to_string((int) h->GetEntries())).c_str());
                cAngCorr->Print(get_bkgCorrPlot_path(ANGLES[a],ANGLES[aa],b,nBins,fitReference, LowMassFit, HighMassFit,  fitKpi, params).c_str(), "eps");

            }
        }

        //Save the correlations s
        myFile << "\\begin{tabular}{r|ccc}\\hline" << std::endl;
        myFile << std::fixed << std::setprecision(2) << "[" << opts.TheQ2binsmin.at(b) << ", " << opts.TheQ2binsmax.at(b) << "]";
        for(UInt_t a = 0; a < nANGLES; a++)  myFile << "\t&" << latex_2angles[a];
        myFile << "\\\\" << std::endl;
        myFile << "\\hline\\hline" << std::endl;
        for(UInt_t a = 0; a < nANGLES; a++){
            myFile << latex_2angles[a];
            for(UInt_t aa = 0; aa < nANGLES; aa++){
                myFile << "\t&";
                if(aa > a)       myFile << std::setprecision(3) << std::fixed << corrfactor[b][a][aa];
                else if(aa == a) myFile << "1.00";
                else if(aa < a)  myFile << "    ";
            }
            myFile << "\\\\" << std::endl;
        }
        myFile << "\\hline" << std::endl;
        myFile << "\\end{tabular}"<< std::endl;
        myFile << std::endl;

    }

    //FIT ANGULAR BACKGROUND + B mass
    opts.update_angle_ranges(); //Just in case
    //fit all bins:
    for(UInt_t b = 0; b < nBins; b++){

        spdlog::info("[START]\tStart the fit for bin #{0:d}", b);

        //fit the events:
        int fitresult = f.fit(theProb[b], theParams[b], theEvents[b]);
        fitresults.push_back(fitresult);

        spdlog::info("[BIN{0:d}]:\tFitresult: {1:d}", b, fitresult);

        //plot pdf with the data points:
        opts.plot_label = "LHCb data";
        opts.q2_label = q2_label(opts.TheQ2binsmin.at(b), opts.TheQ2binsmax.at(b));

        if(opts.write_eps){
            std::string label = get_bkg_tag(b,nBins,fitReference, LowMassFit, HighMassFit,  false, params);
            spdlog::info("[PLOT]\t" + label);
            thePlotter.plot_data(theProb[b], theParams[b], theEvents[b], get_bkgFitPlot_path(), label, false);
        }
        //print all parameters
        std::string fitResultTxt = get_bkgFitResult_path()+ "fitresult_" + get_bkg_tag(b,nBins,fitReference, LowMassFit, HighMassFit,  false, params);
        spdlog::debug("Saving into "+fitResultTxt);
        print_all_parameters(nBins, theParams, 2, fitResultTxt);
    }

    //print all fitresults
    print_fit_results(nBins, fitresults);

    //Save results to root file
    std::string results_file = final_result_name_bkg(nBins, fitReference, LowMassFit, HighMassFit, false, params);
    save_results(results_file, nBins, params.Run, fitresults, theParams, &opts);

    //FIT M(KPI) BACKGROUND
    if (!fitKpi){ //first check if you really want to fit M(KPI) :)
        //Close Latex file
        myFile.close();
        return 0;
    }

    //Reset the options
    opts.only_angles = false;
    opts.only_mkpi = true;
    opts.fit_mkpi = true;


    //fit all bins:
    spdlog::info("[BKGFIT]\tFit the Kpi mass");
    for(UInt_t b = 0; b < nBins; b++){

        theParams[b]->use_default_bkg();
        //null the lambda(s) and taus
        theParams[b]->m_lambda.init_fixed(0.0);
        theParams[b]->m_lambda_2.init_fixed(0.0);
        theParams[b]->m_tau.init_fixed(0.0);
        theParams[b]->m_tau_2.init_fixed(0.0);
        //Init the floating parameters
        theParams[b]->init_kpi_background_parameters(fitReference,0.01);

        spdlog::info("[START]\tStart the fit for bin #{0:d}", b);

        //fit the events:
        fitresults.push_back(f.fit(theProb[b], theParams[b], theEvents[b]));
        spdlog::info("[BIN{0:d} ]:\tFitresult: {1:d}",b, fitresults.at(b));

        //plot pdf with the data points:
        opts.q2_label = q2_label(opts.TheQ2binsmin.at(b), opts.TheQ2binsmax.at(b));

        if(opts.write_eps){
            std::string label = get_bkg_tag(b,nBins,fitReference, LowMassFit, HighMassFit, false, params);
            spdlog::info("[PLOT]\t" + label);
            thePlotter.plot_data(theProb[b], theParams[b], theEvents[b], get_bkgFitPlot_path(), label, false);
        }               
        //Print all parameters, but don't save
        print_all_parameters(nBins, theParams, 2, "");

        //print all parameters to txt file
        std::string fitResultTxt = get_bkgFitResult_path()+ "fitresult_" + get_bkg_tag(b,nBins,fitReference, LowMassFit, HighMassFit,  true, params);
        spdlog::debug("Saving into "+fitResultTxt);
        print_all_parameters(nBins, theParams, 2, fitResultTxt);
    }
    spdlog::info("[BKGFIT]\tDone with Kpi fit.");
    //Save results to root file
    results_file = final_result_name_bkg(nBins, fitReference, LowMassFit, HighMassFit, true, params);
    save_results(results_file, nBins, params.Run, fitresults, theParams, &opts);

    //Close Latex file
    myFile.close();

    spdlog::info("[BKGFIT]\tFinished.");
    return 0;
}
