#include <TSystem.h>

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

#include "MRawFileRead.h"
#include "MRawFileWrite.h"

#include "MReportFileRead.h"
#include "MWriteRootFile.h"

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

#include "MArgs.h"
#include "MTime.h"
#include "MArray.h"
#include "MRawEvtData.h"
#include "MRawRunHeader.h"
#include "MRawEvtHeader.h"
#include "MRawCrateArray.h"

using namespace std;

//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// This is an easy implementation of the Merging process                    //
// (as compilable prog)                                                     //
//                                                                          //
// at the moment it reads a binary file ("rawtest.bin") which was written   //
// in the DAQ raw format.                                                   //
//                                                                          //
// The data are stored in root container objects (classes derived from      //
// TObject like MRawRunHeader)                                              //
//                                                                          //
// This containers are written to a root file ("rawtest.root")              //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

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

    //                1         2         3         4         5
    //       12345678901234567890123456789012345678901234567890
    gLog << "==================================================" << endl;
    gLog << "                MERPP - MARS V" << MARSVER          << endl;
    gLog << "     MARS - Merging and Preprocessing Program"      << endl;
    gLog << "            Compiled on <" << __DATE__ << ">"       << endl;
    gLog << "               Using ROOT v" << ROOTVER             << endl;
    gLog << "==================================================" << endl;
    gLog << endl;
}

static void Usage()
{
    gLog << all << endl;
    gLog << "Sorry the usage is:" << endl;
    gLog << "   merpp [-h] [-?] [-a0] [-vn] [-cn] [-u1]" << endl;
    gLog << "         inputfile[.rep,[.raw]] [outputfile[.root]]" << endl << endl;
    gLog << "     inputfile.raw:  Magic DAQ binary file." << endl;
    gLog << "     inputfile.rep:  Magic Central Control report file." << endl;
    gLog << "     ouputfile.root: Merpped root file." << endl;
    gLog << "     -a0: Do not use Ansii codes." << endl;
    gLog << "     -u1: Update file." << endl;
    gLog << "     -cn: Compression level n=1..9 [default=2]" << endl;
    gLog << "     -vn: Verbosity level n [default=2]" << endl;
    gLog << "     -?/-h: This help" << endl << endl;
    gLog << "   REMARK: At the moment you can process a .raw _or_ a .rep file, only!" << endl << endl;
}

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

    //
    // Evaluate arguments
    //
    MArgs arg(argc, argv);

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

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

    const Int_t kComprlvl = arg.HasOption("-c") ? arg.GetIntAndRemove("-c") : 1;
    Bool_t kUpdate = arg.HasOption("-u") && arg.GetIntAndRemove("-u")==1;

    gLog.SetDebugLevel(arg.HasOption("-v") ? arg.GetIntAndRemove("-v") : 2);

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

    //
    // This is to make argv[i] more readable insidethe code
    //
    TString kNamein  = arg.GetArgumentStr(0);
    TString kNameout = arg.GetArgumentStr(1);

    const Bool_t isreport = kNamein.EndsWith(".rep");

    if (!kNamein.EndsWith(".raw") && !isreport)
        kNamein += ".raw";

    if (kNameout.IsNull())
        kNameout = kNamein(0, kNamein.Last('.'));

    if (!kNameout.EndsWith(".root"))
        kNameout += ".root";

    //
    // Initialize Non-GUI (batch) mode
    //
    gROOT->SetBatch();

    //
    // check whether the given files are OK.
    //
    if (gSystem->AccessPathName(kNamein, kFileExists))
    {
        gLog << err << "Sorry, the input file '" << kNamein << "' doesn't exist." << endl;
        return -1;
    }

    const Bool_t fileexist = !gSystem->AccessPathName(kNameout, kFileExists);
    const Bool_t writeperm = !gSystem->AccessPathName(kNameout, kWritePermission);

    if (fileexist && !writeperm)
    {
        gLog << err << "Sorry, you don't have write permission for '" << kNameout << "'." << endl;
        return -1;
    }

    if (fileexist && !kUpdate)
    {
        gLog << err << "Sorry, file '" << kNameout << "' already existing." << endl;
        return -1;
    }

    if (!fileexist && kUpdate)
    {
        gLog << warn << "File '" << kNameout << "' doesn't yet exist." << endl;
        kUpdate=kFALSE;
    }

    MArray::Class()->IgnoreTObjectStreamer();
    MParContainer::Class()->IgnoreTObjectStreamer();

    //
    // 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;
    tasks.SetOwner();
    plist.AddToList(&tasks);

    //
    // ---- The following is only necessary to supress some output ----
    //
    MRawRunHeader runheader;
    plist.AddToList(&runheader);

    MRawEvtHeader evtheader;
    plist.AddToList(&evtheader);

    MRawEvtData evtdata;
    plist.AddToList(&evtdata);

    MRawCrateArray cratearray;
    plist.AddToList(&cratearray);

    MTime evttime;
    plist.AddToList(&evttime);

    //
    // 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
    //
    MTask *read  = 0;
    MTask *write = 0;

    const TString option(kUpdate ? "UPDATE" : "RECREATE");
    if (isreport)
    {
        MReportFileRead *r = new MReportFileRead(kNamein);
        r->AddToList("MReportDAQ");
        r->AddToList("MReportDrive");
        r->AddToList("MReportCamera");
        r->AddToList("MReportTrigger");
        read = r;

        MWriteRootFile *w = new MWriteRootFile(kNameout, option, "Magic root-file", kComprlvl);
        /*
         w->AddContainer("MReportDAQ",         "DAQ");
         w->AddContainer("MTimeDAQ",           "DAQ");
         w->AddContainer("MReportDrive",       "Drive");
         w->AddContainer("MTimeDrive",         "Drive");
         */
        w->AddContainer("MReportCamera",      "Camera");
        w->AddContainer("MTimeCamera",        "Camera");
        w->AddContainer("MCameraAUX",         "Camera");
        w->AddContainer("MCameraCalibration", "Camera");
        w->AddContainer("MCameraCooling",     "Camera");
        w->AddContainer("MCameraHV",          "Camera");
        w->AddContainer("MCameraLV",          "Camera");
        w->AddContainer("MCameraLids",        "Camera");
        w->AddContainer("MReportTrigger",     "Trigger");
        w->AddContainer("MTimeTrigger",       "Trigger");
        w->AddContainer("MReportDrive",       "Drive");
        w->AddContainer("MTimeDrive",         "Drive");
        write = w;
    }
    else
    {
        read  = new MRawFileRead(kNamein);
        write = new MRawFileWrite(kNameout, option, "Magic root-file", kComprlvl);
    }
    tasks.AddToList(read);
    tasks.AddToList(write);

    //
    // create the looping object and tell it about the parameters to use
    // and the tasks to execute
    //
    MEvtLoop magic;
    magic.SetParList(&plist);

    //
    // Start the eventloop which reads the raw file (MRawFileRead) and
    // write all the information into a root file (MRawFileWrite)
    //
    // between reading and writing we can do, transformations, checks, etc.
    // (I'm think of a task like MRawDataCheck)
    //
    if (!magic.Eventloop())
    {
        gLog << err << "ERROR: Merging and preprocessing failed!" << endl;
        return -1;
    }

    gLog << all << "Merpp finished successfull!" << endl;
    return 0;
}


