#include <TSystem.h>

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

#include "MRawFileRead.h"
#include "MRawFileWrite.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] inputfile[.raw] [outputfile[.root]]" << endl << endl;
    gLog << "     input file:   Magic DAQ binary file." << endl;
    gLog << "     ouput file:   Merpped root file." << endl;
    gLog << "     -a0: Do not use Ansii codes." << 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;
}

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 kComprlvl = arg.HasOption("-c") ? arg.GetIntAndRemove("-c") : 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);

    if (!kNamein.EndsWith(".raw"))
        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;
    }

    if (!gSystem->AccessPathName(kNameout, kFileExists))
        gLog << warn << "Warning: A file '" << kNameout << "' exists." << endl;
    else
        if (!gSystem->AccessPathName(kNameout, kWritePermission))
        {
            gLog << err << "Sorry, you don't have write permission for '" << kNameout << "'." << endl;
            return -1;
        }

    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;
    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("MRawEvtTime");
    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
    //
    MRawFileRead  reader(kNamein);
    MRawFileWrite writer(kNameout, "RECREATE", "Magic root-file", kComprlvl);
    tasks.AddToList(&reader);
    tasks.AddToList(&writer);

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


