/* ======================================================================== *\
!
! *
! * This file is part of MARS, the MAGIC Analysis and Reconstruction
! * Software. It is distributed to you in the hope that it can be a useful
! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
! * It is distributed WITHOUT ANY WARRANTY.
! *
! * Permission to use, copy, modify and distribute this software and its
! * documentation for any purpose is hereby granted without fee,
! * provided that the above copyright notice appear in all copies and
! * that both that copyright notice and this permission notice appear
! * in supporting documentation. It is provided "as is" without express
! * or implied warranty.
! *
!
!
!   Author(s): Thomas Bretz 11/2005 <mailto:tbretz@astro.uni-wuerzburg.de>
!
!   Copyright: MAGIC Software Development, 2005
!
!
\* ======================================================================== */

/////////////////////////////////////////////////////////////////////////////
//
// MJTrainSeparation
//
////////////////////////////////////////////////////////////////////////////
#include "MJTrainSeparation.h"

#include <TH2.h>
#include <TGraph.h>
#include <TVirtualPad.h>

#include "MHMatrix.h"

#include "MLog.h"
#include "MLogManip.h"

// tools
#include "MMath.h"
#include "MDataSet.h"
#include "MTFillMatrix.h"
#include "MChisqEval.h"
#include "MStatusDisplay.h"

// eventloop
#include "MParList.h"
#include "MTaskList.h"
#include "MEvtLoop.h"

// tasks
#include "MReadMarsFile.h"
#include "MContinue.h"
#include "MFillH.h"
#include "MRanForestCalc.h"
#include "MParameterCalc.h"

// container
#include "MMcEvt.hxx"
#include "MParameters.h"

// histograms
#include "MBinning.h"
#include "MH3.h"
#include "MHHadronness.h"

// filter
#include "MF.h"
#include "MFEventSelector.h"
#include "MFilterList.h"

ClassImp(MJTrainSeparation);

using namespace std;

void MJTrainSeparation::DisplayResult(MH3 &h31, MH3 &h32)
{
    TH2 &g = (TH2&)h32.GetHist();
    TH2 &h = (TH2&)h31.GetHist();

    h.SetMarkerColor(kRed);
    g.SetMarkerColor(kGreen);

    const Int_t nx = h.GetNbinsX();
    const Int_t ny = h.GetNbinsY();

    gROOT->SetSelectedPad(NULL);

    TGraph gr1;
    TGraph gr2;
    for (int x=0; x<nx; x++)
    {
        TH1 *hx = h.ProjectionY("H_py", x+1, x+1);
        TH1 *gx = g.ProjectionY("G_py", x+1, x+1);

        Double_t max1 = -1;
        Double_t max2 = -1;
        Int_t maxy1 = 0;
        Int_t maxy2 = 0;
        for (int y=0; y<ny; y++)
        {
            const Float_t s = gx->Integral(1, y+1);
            const Float_t b = hx->Integral(1, y+1);
            const Float_t sig1 = MMath::SignificanceLiMa(s+b, b);
            const Float_t sig2 = s<1 ? 0 : MMath::SignificanceLiMa(s+b, b)*TMath::Log10(s);
            if (sig1>max1)
            {
                maxy1 = y;
                max1 = sig1;
            }
            if (sig2>max2)
            {
                maxy2 = y;
                max2 = sig2;
            }
        }

        gr1.SetPoint(x, h.GetXaxis()->GetBinCenter(x+1), h.GetYaxis()->GetBinCenter(maxy1+1));
        gr2.SetPoint(x, h.GetXaxis()->GetBinCenter(x+1), h.GetYaxis()->GetBinCenter(maxy2+1));

        delete hx;
        delete gx;
    }

    fDisplay->AddTab("OptCut");
    gPad->SetLogx();
    h.DrawCopy();
    g.DrawCopy("same");
    gr1.SetMarkerStyle(kFullDotMedium);
    gr1.DrawClone("LP")->SetBit(kCanDelete);
    gr2.SetLineColor(kBlue);
    gr2.SetMarkerStyle(kFullDotMedium);
    gr2.DrawClone("LP")->SetBit(kCanDelete);
}

Bool_t MJTrainSeparation::Train(const char *out)
{
    if (!fDataSetTrain.IsValid())
    {
        *fLog << err << "ERROR - DataSet for training invalid!" << endl;
        return kFALSE;
    }
    if (!fDataSetTest.IsValid())
    {
        *fLog << err << "ERROR - DataSet for testing invalid!" << endl;
        return kFALSE;
    }

    // --------------------- Setup files --------------------
    MReadMarsFile read1("Events");
    MReadMarsFile read2("Events");
    MReadMarsFile read3("Events");
    MReadMarsFile read4("Events");
    read1.DisableAutoScheme();
    read2.DisableAutoScheme();
    read3.DisableAutoScheme();
    read4.DisableAutoScheme();

    fDataSetTrain.AddFilesOn(read1);
    fDataSetTrain.AddFilesOff(read3);

    fDataSetTest.AddFilesOff(read2);
    fDataSetTest.AddFilesOn(read4);

    // ----------------------- Setup RF ----------------------
    MHMatrix train("Train");
    train.AddColumns(fRules);
    train.AddColumn("MHadronness.fVal");

    // ----------------------- Fill Matrix RF ----------------------

    MParameterD had("MHadronness");

    MParList plistx;
    plistx.AddToList(&had);
    plistx.AddToList(this);

    MTFillMatrix fill;
    fill.SetLogStream(fLog);
    fill.SetDisplay(fDisplay);
    fill.AddPreCuts(fPreCuts);
    fill.AddPreCuts(fTrainCuts);

    // Set classifier for gammas
    had.SetVal(0);
    fill.SetName("FillGammas");
    fill.SetDestMatrix1(&train, fNumTrainOn);
    fill.SetReader(&read1);
    if (!fill.Process(plistx))
        return kFALSE;

    // Set classifier for hadrons
    had.SetVal(1);
    fill.SetName("FillBackground");
    fill.SetDestMatrix1(&train, fNumTrainOff);
    fill.SetReader(&read3);
    if (!fill.Process(plistx))
        return kFALSE;

    // ------------------------ Train RF --------------------------

    MRanForestCalc rf;
    rf.SetNumObsoleteVariables(1);
    rf.SetDebug(fDebug);
    rf.SetDisplay(fDisplay);
    rf.SetLogStream(fLog);
    rf.SetFileName(out);
    rf.SetNameOutput("MHadronness");

    //MBinning b(2, -0.5, 1.5, "BinningHadronness", "lin");

    //if (!rf.TrainMultiRF(train, b.GetEdgesD()))    // classification
    //    return;

    //if (!rf.TrainSingleRF(train, b.GetEdgesD()))   // classification
    //    return;

    if (!rf.TrainSingleRF(train))                  // regression
        return kFALSE;

    //fDisplay = rf.GetDisplay();

    // --------------------- Display result ----------------------
    gLog.Separator("Test");

    MParList  plist;
    MTaskList tlist;
    plist.AddToList(this);
    plist.AddToList(&tlist);

    MMcEvt mcevt;
    plist.AddToList(&mcevt);

    // ----- Setup histograms -----
    MBinning binsy(100, 0 , 1,      "BinningMH3Y", "lin");
    MBinning binsx( 50, 10, 100000, "BinningMH3X", "log");

    plist.AddToList(&binsx);
    plist.AddToList(&binsy);

    MH3 h31("MHillas.fSize", "MHadronness.fVal");
    MH3 h32("MHillas.fSize", "MHadronness.fVal");
    h31.SetTitle("Background probability vs. Size:Size [phe]:Hadronness");
    h32.SetTitle("Background probability vs. Size:Size [phe]:Hadronness");

    MHHadronness hist;

    // ----- Setup tasks -----
    MFillH fillh0(&hist, "", "FillHadronness");
    MFillH fillh1(&h31);
    MFillH fillh2(&h32);
    fillh1.SetNameTab("Background");
    fillh2.SetNameTab("Gammas");
    fillh0.SetBit(MFillH::kDoNotDisplay);

    // ----- Setup filter -----
    MFilterList precuts;
    precuts.AddToList(fPreCuts);
    precuts.AddToList(fTestCuts);

    MContinue c0(&precuts);
    c0.SetName("PreCuts");
    c0.SetInverted();

    MFEventSelector sel;
    sel.SetNumSelectEvts(fNumTestOn);

    MContinue c1(&sel);
    c1.SetInverted();

    // ----- Setup tasklist -----
    tlist.AddToList(&read2);
    tlist.AddToList(&c0);
    tlist.AddToList(&c1);
    tlist.AddToList(&rf);
    tlist.AddToList(&fillh0);
    tlist.AddToList(&fillh1);

    // ----- Run eventloop on gammas -----
    MEvtLoop loop;
    loop.SetDisplay(fDisplay);
    loop.SetLogStream(fLog);
    loop.SetParList(&plist);

    if (!loop.Eventloop())
        return kFALSE;

    // ----- Setup and run eventloop on background -----
    sel.SetNumSelectEvts(fNumTestOff);
    fillh0.ResetBit(MFillH::kDoNotDisplay);

    tlist.Replace(&read4);
    tlist.Replace(&fillh2);

    if (!loop.Eventloop())
        return kFALSE;

    DisplayResult(h31, h32);

    if (!WriteDisplay(out))
        return kFALSE;

    return kTRUE;
}

