#include <TApplication.h>

#include <TThread.h>
#include <TMethod.h>          // GetMenuItems

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

#include "MLogManip.h"

#include "MRawSocketRead.h"
#include "MGeomApply.h"
#include "MCerPhotAnal2.h"
#include "MFillH.h"
#include "MFRealTimePeriod.h"
#include "MOnlineDump2.h"
#include "MHTriggerLvl0.h"
#include "MHCamera.h"         // ::Class()

#include "MStatusDisplay.h"

#include "MImgCleanStd.h"
#include "MHillasCalc.h"
#include "MHillasSrcCalc.h"

void StartUpMessage()
{
    gLog << all << endl;

    //                1         2         3         4         5
    //       12345678901234567890123456789012345678901234567890
    gLog << "==================================================" << endl;
    gLog << "                    MONA V" << MARSVER << "                      " << endl;
    gLog << "              Magic Online Analysis               " << endl;
    gLog << "            Compiled on <" << __DATE__ << ">"       << endl;
    gLog << "               Using ROOT v" << ROOTVER             << endl;
    gLog << "==================================================" << endl;
    gLog << endl;
}

void Remove(TMethod *m, const char *name)
{
    if (TString(m->GetName()).BeginsWith(name))
        m->SetMenuItem(kMenuNoMenu);
}

void ChangeContextMenus()
{
    TList l;
    MHCamera::Class()->GetMenuItems(&l);
    TIter Next(&l);
    TMethod *m=NULL;
    while ((m=(TMethod*)Next()))
    {
        Remove(m, "DrawClone");
        Remove(m, "SetName");
        Remove(m, "Delete");
        Remove(m, "DrawClass");
        Remove(m, "Dump");
        Remove(m, "Inspect");
        Remove(m, "Smooth");
        Remove(m, "Fit");
        Remove(m, "Divide");
        Remove(m, "Add");
        Remove(m, "Multiply");
        Remove(m, "Delete");
        Remove(m, "SetLineAttributes");
        Remove(m, "SetFillAttributes");
        Remove(m, "SetMarkerAttributes");
        Remove(m, "SetDrawOption");
    }
}

Bool_t Loop(MStatusDisplay *display)
{
    //
    // create the tasks which should be executed and add them to the list
    // in the case you don't need parameter containers, all of them can
    // be created by MRawFileRead::PreProcess
    //
    MRawSocketRead reader;
    reader.SetPort(7000);

    //
    // create a (empty) list of parameters which can be used by the tasks
    // and an (empty) list of tasks which should be executed
    //
    MParList plist;

    MTaskList tasks;
    plist.AddToList(&tasks);
    tasks.AddToList(&reader);

    MGeomApply geomapl;
    MCerPhotAnal2 ncalc;
    tasks.AddToList(&geomapl);
    tasks.AddToList(&ncalc);

    MFillH fill1("MHEvent",    "MCerPhotEvt", "MFillEvent");
    MFillH fill2("MHCamEvent", "MCerPhotEvt", "MFillCamEvent");

    MFRealTimePeriod filter;
    fill1.SetFilter(&filter);

    MHTriggerLvl0 trigmap(128, "Above 128");
    MFillH fill3(&trigmap, "MRawEvtData", "MFillTriggerLvl0");

    tasks.AddToList(&filter);
    tasks.AddToList(&fill1);
    tasks.AddToList(&fill2);
    tasks.AddToList(&fill3);

    MOnlineDump2 dump;
    dump.SetFilter(&filter);
    tasks.AddToList(&dump);

    MImgCleanStd      clean;
    MHillasCalc       hcalc;
    MHillasSrcCalc    scalc; // !!Preliminary!! Will be removed later!
    MFillH hfill3("MHHillas",   "MHillas",    "MFillHillas");
    MFillH hfill4("MHHillasSrc","MHillasSrc", "MFillHillasSrc");
    tasks.AddToList(&clean);
    tasks.AddToList(&hcalc);
    tasks.AddToList(&scalc);
    tasks.AddToList(&hfill3);
    tasks.AddToList(&hfill4);

    MEvtLoop magic;
    magic.SetParList(&plist);

    magic.SetDisplay(display);
    magic.SetProgressBar((TGProgressBar*)NULL);

    if (!magic.Eventloop())
    {
        gLog << err << "Mona Eventloop error..." << endl;
        return kFALSE;
    }

    tasks.PrintStatistics();
    return kTRUE;
}

//
// By defining the function return type 'void' instead of
// 'void*' we tell TThread to create a detached thread,
// but calling Exit() in a detached thread results in a crash
// when the TGApplication is terminated.
//
void *thread(void *ptr)
{
    MStatusDisplay d;
    d.Resize(740, 600);

    //
    // If a loop is stopped reinitialize a new loop until the
    // user requested to exit the program.
    //
    while (d.CheckStatus()!=MStatusDisplay::kFileExit)
        Loop(&d);

    gLog << dbg << "Exit System Loop... " << flush;
    gSystem->ExitLoop();
    gLog << "done." << endl;

    TThread::Self()->Exit();
    return 0;
}

/*
#if ROOT_VERSION_CODE < ROOT_VERSION(3,10,02)
#include <TRootGuiFactory.h>
#include <TPluginManager.h>
void InitGuiFactory()
{
   if (gROOT->IsBatch())
       gROOT->SetBatch(kFALSE);

    //
    // Must be loaded by hand, because it is not loaded by TGApplication.
    // We could also use TApplication instead, but TApplication doesn't
    // deal with the DISPLAY variable in a convient way.
    //
    TPluginHandler *h;
    if ((h = gROOT->GetPluginManager()->FindHandler("TGuiFactory", "root")))
    {
        if (h->LoadPlugin() == -1)
            return;
        gGuiFactory = (TGuiFactory*)h->ExecPlugin(0);
    }
}
#endif
*/

int main(int argc, char **argv)
{
    StartUpMessage();

    TApplication app("Mars", &argc, argv);
    if (gROOT->IsBatch() || !gClient)
    {
        gLog << "Bombing... maybe your DISPLAY variable is not set correctly!" << endl;
        return 1;
    }
    /*
    TGApplication app("Mona", &argc, argv);

#if ROOT_VERSION_CODE < ROOT_VERSION(3,10,02)
    InitGuiFactory();
#endif
    */

    ChangeContextMenus();

    //
    // Starting MStatusDisplay in the thread results in hangs
    // if the thread is terminated (by return)
    //
    gLog << dbg << "Starting Thread..." << endl;
    TThread t(thread);
    t.Run();

    gLog << dbg << "Starting System loop... " << endl;
    app.Run(kTRUE);
    gLog << dbg << "System loop stopped." << endl;

    return 0;
}
