#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 "MHTriggerLvl0.h"
#include "MHCamera.h"         // ::Class()

#include "MStatusDisplay.h"

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

#include "MArgs.h"

// --------------------------------------------------------------------

#include "MTime.h"
#include "MRawEvtHeader.h"       // MRawEvtHeader
#include "MRawRunHeader.h"       // MRawRunHeader

class MOnlineDump : public MTask
{
private:
    MFillH *fFill1;
    MFillH *fFill2;
    MFillH *fFill3;
    MFRealTimePeriod *fFilter;

    UInt_t fRunNumber;
    UInt_t fEvtNumber;

    MRawEvtHeader *fRawEvtHeader;
    MRawRunHeader *fRawRunHeader;

    MTime *fEvtTime;
    Double_t fTime;

    void SetNoContextMenu(TObject *c)
    {
        if (fDisplay->HasCanvas((TCanvas*)c))
            c->ResetBit(kNoContextMenu);
    }
    void DisplayTriggerRate()
    {
        if (*fEvtTime==0)
            fEvtTime->SetTime(gSystem->Now());
    
        if (fEvtNumber==0 || fRawRunHeader->GetRunNumber()!=fRunNumber)
        {
            fEvtNumber = fRawEvtHeader->GetDAQEvtNumber();
            fRunNumber = fRawRunHeader->GetRunNumber();
            fTime      = *fEvtTime;
            return;
        }
    
        const UInt_t  evts = fRawEvtHeader->GetDAQEvtNumber() - fEvtNumber;
        const Double_t sec = *fEvtTime-fTime;
    
        if (evts>0 && sec>0 && fDisplay)
            fDisplay->SetStatusLine2(Form("Trigger Rate: %.1fHz", evts/sec));
    }

public:
    MOnlineDump() { fName = "MOnlineDump"; }
    Int_t PreProcess(MParList *pList)
    {
        if (gROOT->IsBatch())
        {
            *fLog << err << "We are in batch mode!" << endl;
            return kFALSE;
        }
        if (!fDisplay)
        {
            *fLog << err << "fDisplay not set." << endl;
            return kFALSE;
        }
    
        MTaskList *tasks = (MTaskList*)pList->FindObject("MTaskList");
        if (!tasks)
        {
            *fLog << err << "MTaskList not found... abort." << endl;
            return kFALSE;
        }
    
        fFill1 = (MFillH*)tasks->FindObject("MFillCamEvent");
        if (!fFill1)
        {
            *fLog << err << "MFillCamEvent not found... abort." << endl;
            return kFALSE;
        }
        fFill2 = (MFillH*)tasks->FindObject("MFillEvent");
        if (!fFill2)
        {
            *fLog << err << "MFillEvent not found... abort." << endl;
            return kFALSE;
        }
        fFill3 = (MFillH*)tasks->FindObject("MFillTriggerLvl0");
        if (!fFill3)
        {
            *fLog << err << "MFillTriggerLvl0 not found... abort." << endl;
            return kFALSE;
        }
        fRawEvtHeader = (MRawEvtHeader*)pList->FindObject("MRawEvtHeader");
        if (!fRawEvtHeader)
        {
            *fLog << err << "MRawEvtHeader not found... abort." << endl;
            return kFALSE;
        }
    
        fRawRunHeader = (MRawRunHeader*)pList->FindObject("MRawRunHeader");
        if (!fRawRunHeader)
        {
            *fLog << err << "MRawRunHeader not found... abort." << endl;
            return kFALSE;;
        }
    
        fEvtTime = (MTime*)pList->FindObject("MRawEvtTime");
        if (!fEvtTime)
        {
            *fLog << err << "MRawEvtTime not found... abort." << endl;
            return kFALSE;
        }
    
        fRunNumber = 0xffffffff;
        fEvtNumber = 0;
    
        SetNoContextMenu((TObject*)fFill1->GetCanvas());
        SetNoContextMenu((TObject*)fFill2->GetCanvas());
        SetNoContextMenu((TObject*)fFill3->GetCanvas());
    
        return kTRUE;
    }
    Int_t Process()
    {
        DisplayTriggerRate();
    
        if (fDisplay)
            fDisplay->SetProgressBarPosition((Float_t)fEvtNumber/fRawRunHeader->GetNumEvents());
    
        return kTRUE;
    } 
    Int_t PostProcess()
    {
        if (fDisplay)
            fDisplay->SetProgressBarPosition(1);
    
        return kTRUE;
    }
//    ClassDef(MOnlineDump, 0) // Task to fill a histogram with data from a parameter container
};
//ClassImp(MOnlineDump);
// --------------------------------------------------------------------

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, Int_t port)
{
    //
    // 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(port);

    //
    // 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);

    MOnlineDump 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)
{
    const Int_t port = *(Int_t*)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, port);

    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
*/

static void Usage()
{
    gLog << all << endl;
    gLog << "Sorry the usage is:" << endl;
    gLog << "   mona [-?] [-h] [-a0] [-h] [-pn] [-vn]" << endl << endl;
    gLog << "     -a0: Do not use Ansii codes." << endl;
    gLog << "     -pn: TCP/IP port n [default=7000]" << endl;
    gLog << "     -vn: Verbosity level n [default=2]" << endl << endl;
    gLog << "     -?/-h: This help" << endl << endl;
}


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

    //
    // Evaluate arguments
    //
    MArgs arg(argc, (const char**)argv);

    if (arg.HasOption("-?") || arg.HasOption("-h"))
    {
        Usage();
        return -1;
    }

    //
    // Set verbosity to highest level.
    //
    gLog.SetDebugLevel(arg.HasOption("-v") ? arg.GetIntAndRemove("-v") : 2);

    if (arg.HasOption("-a") && arg.GetIntAndRemove("-a")==0)
        gLog.SetNoColors();

    const Int_t port = arg.HasOption("-p") ? arg.GetIntAndRemove("-p") : 7000;

    //
    // check for the right usage of the program
    //
    if (arg.GetNumArguments()>0)
    {
        Usage();
        return -1;
    }

    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, (void*)&port);
    t.Run();

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

    return 0;
}
