#include <TSystem.h>

#include "MParList.h"
#include "MTaskList.h"
#include "MEvtLoop.h"
/*'
#include "MLog.h"
 */
#include "MLogManip.h"

#include "MArgs.h"

#include "MArray.h"
#include "MReadMarsFile.h"
#include "MGeomApply.h"
#include "MMcPedestalCopy.h"
#include "MMcPedestalNSBAdd.h"
#include "MCerPhotCalc.h"
#include "MCerPhotAnal2.h"
#include "MSigmabarCalc.h"
#include "MImgCleanStd.h"
#include "MHillasCalc.h"
#include "MHillasSrcCalc.h"
#include "MWriteRootFile.h"
#include "MFDataMember.h"
#include "MFillH.h"
#include "MReportDrive.h"    // PRELIMINARY:
//#include "MPointingPos.h"    // PRELIMINARY:
//#include "MPointingPosCalc.h"

using namespace std;

//////////////////////////////////////////////////////////////////////////////
//
// This is an easy implementation of the Star process
// (as compilable prog)
//
//////////////////////////////////////////////////////////////////////////////

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

    //                1         2         3         4         5
    //       12345678901234567890123456789012345678901234567890
    gLog << "==================================================" << endl;
    gLog << "                 STAR - MARS V" << MARSVER          << endl;
    gLog << "   MARS - STandard Analysis and Reconstruction"     << 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 << "   star [-a0] [-vn] [-cn] inputfile[.root] outputfile[.root]" << endl << endl;
    gLog << "     input file: Merpped or MC root file." << endl;
    gLog << "     ouput file: Star-file (image parameter 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 << "     -u1: Update File (instead of Recreate)" << endl;
    gLog << "     -tn: Telescope Serial Number n [default=0]" << endl << endl;
    gLog << " -> Further setup is not possible at the moment, please use" << endl;
    gLog << "    the star.C root macro instead. Using an input card will" << endl;
    gLog << "    be implemented in the future." << endl << endl;
}

class MJStar : public MParContainer
{
private:
    TString fInputFile;
    TString fOutputFile;

    Bool_t fIsUpdate;
    Byte_t fComprLevel;

    Byte_t fSerialIdx;

    Bool_t CheckFiles()
    {
        if (fOutputFile.IsNull())
        {
            fOutputFile = fInputFile;

            if (fOutputFile.EndsWith(".raw"))
                fOutputFile = fOutputFile(0, fOutputFile.Length()-4);

            if (fOutputFile.EndsWith(".rep"))
                fOutputFile = fOutputFile(0, fOutputFile.Length()-4);

            if (fOutputFile.EndsWith(".txt"))
                fOutputFile = fOutputFile(0, fOutputFile.Length()-4);
        }

        if (fInputFile.IsNull())
        {
            *fLog << err << "Sorry, no input file." << endl;
            return kFALSE;
        }

        if (fOutputFile.IsNull())
        {
            *fLog << err << "Sorry, no output file." << endl;
            return kFALSE;
        }

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

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

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

        if (!gSystem->AccessPathName(fOutputFile, kFileExists))
        {
            if (fIsUpdate)
                gLog << warn << "Warning: File doesn't '" << fOutputFile << "' exist... recreating." << endl;
        }

        if (fIsUpdate || gSystem->AccessPathName(fOutputFile, kFileExists))
            if (!gSystem->AccessPathName(fOutputFile, kWritePermission))
        {
            gLog << err << "Sorry, you don't have write permission for '" << fOutputFile << "'." << endl;
            return kFALSE;
        }
        return kTRUE;
    }

public:
    MJStar(const char *name=0, const char *title=0)
        : fIsUpdate(kFALSE), fComprLevel(1), fSerialIdx(0)
    {
        fName  = name;
        fTitle = title;
    }

    void SetInputFile(const char *f)  { fInputFile  = f; }
    void SetOutputFile(const char *f) { fOutputFile = f; }
    void SetSerialIdx(Byte_t i)       { fSerialIdx  = i; }
    void SetComprLevel(Byte_t l)      { fComprLevel = l; }
    void SetUpdate(Bool_t u=kTRUE)    { fIsUpdate   = u; }

    Bool_t Process()
    {
        if (!CheckFiles())
            return kFALSE;

        //
        // Create a empty Parameter List and an empty Task List
        // The tasklist is identified in the eventloop by its name
        //
        MParList plist;

        MTaskList tlist;
        plist.AddToList(&tlist);

        // PRELIMINARY:
        /*
         MReportDrive rep;
         plist.AddToList(&rep);
         MPointingPos pos;
         plist.AddToList(&pos);
         */

        //
        // Now setup the tasks and tasklist:
        // ---------------------------------
        //
        MReadMarsFile read("Events", fInputFile);
        read.DisableAutoScheme();

        MGeomApply        apply;
        MMcPedestalCopy   pcopy;
        MMcPedestalNSBAdd pnsb;

        //MPointingPosCalc  pcalc;

        MCerPhotCalc      ncalc;
        MCerPhotAnal2     nanal;

        MFDataMember f1("MRawRunHeader.fRunType", '>', 255.5);
        MFDataMember f2("MRawRunHeader.fRunType", '<', 255.5);

        ncalc.SetFilter(&f1);
        nanal.SetFilter(&f2);

        MSigmabarCalc     sgcal;
        //MFillH            fills("MHSigmaTheta",  "", "FillSigmaTheta");
        //MFillH            fillb("MHBlindPixels", "", "FillBlindPixels");
        MImgCleanStd      clean;
        MHillasCalc       hcalc;
        MHillasSrcCalc    scalc; // !!Preliminary!! Will be removed later!
        MWriteRootFile    write(fOutputFile, fIsUpdate?"UPDATE":"RECREATE", "Star output", fComprLevel);

        tlist.AddToList(&read);
        tlist.AddToList(&f1);
        tlist.AddToList(&f2);
        tlist.AddToList(&apply);
        tlist.AddToList(&pcopy);
        tlist.AddToList(&pnsb);
        //tlist.AddToList(&pcalc);
        tlist.AddToList(&ncalc);
        tlist.AddToList(&nanal);
        tlist.AddToList(&sgcal);
        //tlist.AddToList(&fills);
        //tlist.AddToList(&fillb);
        tlist.AddToList(&clean);
        tlist.AddToList(&hcalc);
        tlist.AddToList(&scalc);
        tlist.AddToList(&write);

        //
        // Set the serial number for all tasks in the current tasklist
        //
        tlist.SetSerialNumber(fSerialIdx);

        //
        // Setup tasks
        //
        write.AddContainer(write.AddSerialNumber("MMcEvt"),       "Events", kFALSE);
        write.AddContainer(write.AddSerialNumber("MSigmabar"),    "Events");
        write.AddContainer(write.AddSerialNumber("MHillas"),      "Events");
        write.AddContainer(write.AddSerialNumber("MHillasExt"),   "Events");
        write.AddContainer(write.AddSerialNumber("MHillasSrc"),   "Events");
        write.AddContainer(write.AddSerialNumber("MNewImagePar"), "Events");
        write.AddContainer(write.AddSerialNumber("MSrcPosCam"),   "RunHeaders");
        //write.AddContainer(write.AddSerialNumber("MHSigmaTheta"), "RunHeaders");
        if (!fIsUpdate)
        {
            write.AddContainer("MRawRunHeader", "RunHeaders");
            write.AddContainer("MMcRunHeader",  "RunHeaders", kFALSE);
        }

        //
        // Create and set up the eventloop
        //
        MEvtLoop evtloop;
        evtloop.SetParList(&plist);

        //
        // Execute your analysis
        //
        if (!evtloop.Eventloop())
        {
            gLog << err << "ERROR: Star eventloop failed!" << endl;
            return kFALSE;
        }

        tlist.PrintStatistics();

        //plist.FindObject("MHSigmaTheta")->Write();

        gLog << all << "Star finished successfull!" << endl;

        return kTRUE;
    }
//    ClassDef(MJStar, 0)
};

//ClassImp(MJStar);


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

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

    //
    // 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  kComprlvl = arg.HasOption("-c") ? arg.GetIntAndRemove("-c") : 1;
    const int  kTelIndex = arg.HasOption("-t") ? arg.GetIntAndRemove("-t") : 0;
    const bool kUpdate   = arg.HasOption("-u") && arg.GetIntAndRemove("-u")==1;

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

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

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

    MJStar star;
    star.SetInputFile(arg.GetArgumentStr(0));
    star.SetOutputFile(arg.GetArgumentStr(1));
    star.SetComprLevel(kComprlvl);
    star.SetSerialIdx(kTelIndex);
    star.SetUpdate(kUpdate);

    if (!star.Process())
    {
        gLog << err << "Star failed!" << endl;
        return -1;
    }

    gLog << inf << "Star finished successfull!" << endl;

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

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

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

    //
    // 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))
    {
        if (kUpdate)
            gLog << warn << "Warning: File doesn't '" << kNameout << "' exist... recreating." << endl;
    }

    if (kUpdate || gSystem->AccessPathName(kNameout, kFileExists))
        if (!gSystem->AccessPathName(kNameout, kWritePermission))
        {
            gLog << err << "Sorry, you don't have write permission for '" << kNameout << "'." << endl;
            return -1;
        }

    //
    // Create a empty Parameter List and an empty Task List
    // The tasklist is identified in the eventloop by its name
    //
    MParList plist;

    MTaskList tlist;
    plist.AddToList(&tlist);

    // PRELIMINARY:
    // MReportDrive rep;
    // plist.AddToList(&rep);
    // MPointingPos pos;
    // plist.AddToList(&pos);

    //
    // Now setup the tasks and tasklist:
    // ---------------------------------
    //
    MReadMarsFile read("Events", kNamein);
    read.DisableAutoScheme();

    MGeomApply        apply;
    MMcPedestalCopy   pcopy;
    MMcPedestalNSBAdd pnsb;

    //MPointingPosCalc  pcalc;

    MCerPhotCalc      ncalc;
    MCerPhotAnal2     nanal;

    MFDataMember f1("MRawRunHeader.fRunType", '>', 255.5);
    MFDataMember f2("MRawRunHeader.fRunType", '<', 255.5);

    ncalc.SetFilter(&f1);
    nanal.SetFilter(&f2);

    MSigmabarCalc     sgcal;
    //MFillH            fills("MHSigmaTheta",  "", "FillSigmaTheta");
    MImgCleanStd      clean;
    MHillasCalc       hcalc;
    MHillasSrcCalc    scalc; // !!Preliminary!! Will be removed later!
    MWriteRootFile    write(kNameout, kUpdate?"UPDATE":"RECREATE", "Star output", kComprlvl);

    tlist.AddToList(&read);
    tlist.AddToList(&f1);
    tlist.AddToList(&f2);
    tlist.AddToList(&apply);
    tlist.AddToList(&pcopy);
    tlist.AddToList(&pnsb);
    //tlist.AddToList(&pcalc);
    tlist.AddToList(&ncalc);
    tlist.AddToList(&nanal);
    tlist.AddToList(&sgcal);
    //tlist.AddToList(&fills);
    //tlist.AddToList(&fillb);
    tlist.AddToList(&clean);
    tlist.AddToList(&hcalc);
    tlist.AddToList(&scalc);
    tlist.AddToList(&write);

    //
    // Set the serial number for all tasks in the current tasklist
    //
    tlist.SetSerialNumber(kTelIndex);

    //
    // Setup tasks
    //

    write.AddContainer(write.AddSerialNumber("MMcEvt"),       "Events", kFALSE);
    write.AddContainer(write.AddSerialNumber("MSigmabar"),    "Events");
    write.AddContainer(write.AddSerialNumber("MHillas"),      "Events");
    write.AddContainer(write.AddSerialNumber("MHillasExt"),   "Events");
    write.AddContainer(write.AddSerialNumber("MHillasSrc"),   "Events");
    write.AddContainer(write.AddSerialNumber("MNewImagePar"), "Events");
    write.AddContainer(write.AddSerialNumber("MSrcPosCam"),   "RunHeaders");
    //write.AddContainer(write.AddSerialNumber("MHSigmaTheta"), "RunHeaders");
    if (!kUpdate)
    {
        write.AddContainer("MRawRunHeader", "RunHeaders");
        write.AddContainer("MMcRunHeader",  "RunHeaders", kFALSE);
    }

    //
    // Create and set up the eventloop
    //
    MEvtLoop evtloop;
    evtloop.SetParList(&plist);

    //
    // Execute your analysis
    //
    if (!evtloop.Eventloop())
    {
        gLog << err << "ERROR: Star eventloop failed!" << endl;
        return -1;
    }

    tlist.PrintStatistics();

    //plist.FindObject("MHSigmaTheta")->Write();

    gLog << all << "Star finished successfull!" << endl;
    return 0;
    */
}
