#include <TROOT.h>
#include <TClass.h>
#include <TSystem.h>
#include <TGClient.h>
#include <TApplication.h>
#include <TObjectTable.h>

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

#include "MArgs.h"
#include "MArray.h"
#include "MDirIter.h"

#include "MStatusDisplay.h"

#include "MSequences.h"
#include "MJCut.h"

using namespace std;

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

    //                1         2         3         4         5
    //       12345678901234567890123456789012345678901234567890
    gLog << "========================================================" << endl;
    gLog << "                  Ganymed - MARS V" << MARSVER            << endl;
    gLog << "   MARS -- Gammas Are Now Your Most Exciting Discovery"   << endl;
    gLog << "               Compiled on <" << __DATE__ << ">"          << endl;
    gLog << "                  Using ROOT v" << ROOTVER                << endl;
    gLog << "========================================================" << endl;
    gLog << endl;
}

static void Usage()
{
    //                1         2         3         4         5         6         7         8
    //       12345678901234567890123456789012345678901234567890123456789012345678901234567890
    gLog << all << endl;
    gLog << "Sorry the usage is:" << endl;
    gLog << " ganymed [-c] [-y] [options] sequences.txt" << endl << endl;
    gLog << " Arguments:" << endl;
    gLog << "   sequences.txt:            An ascii file defining a collection of sequences" << endl;
    gLog << " Root Options:" << endl;
    gLog << "   -b                        Batch mode (no graphical output to screen)" << endl<<endl;
    gLog << " Options:" << endl;
    gLog.Usage();
    gLog << "   --debug-env               Debug setting resources from file" << endl;
    gLog << "   --debug-mem               Debug memory usage" << endl << endl;
    gLog << endl;
    gLog << "   -q                        Quit when job is finished" << endl;
    gLog << "   -f                        Force overwrite of existing files" << endl;
    gLog << "   --n=[n]                   Analysis number" << endl;
    gLog << "   --out=path                Path to write the all output to [def=local path]" << endl;
    gLog << "   --outf=filename           Filename for output file (eg. status display)" << endl;
    gLog << "   --sum[=filename]          Enable writing of summary file (events after cut0)" << endl;
    gLog << "   --res[=filename]          Enable writing of result file (surviving events)" << endl;
    gLog << "   --write-only              Only write output files. No histograms filled." << endl;
    gLog << "   --print-seq               Print Sequences information" << endl;
    gLog << "   --print-files             Print Files taken from Sequences ('+' found, '-' missing)" << endl;
    gLog << "   --config=ganymed.rc       Resource file [default=ganymed.rc]" << endl;
    gLog << endl;
    gLog << "   --version, -V             Show startup message with version number" << endl;
    gLog << "   -?, -h, --help            This help" << endl;
    gLog << endl;
}

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

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

    if (arg.HasOnly("-V") || arg.HasOnly("--version"))
        return 0;

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

    gLog.Setup(arg);

    const TString kConfig       = arg.GetStringAndRemove("--config=", "ganymed.rc");

    const Bool_t  kPrintSeq     = arg.HasOnlyAndRemove("--print-seq");
    const Bool_t  kPrintFiles   = arg.HasOnlyAndRemove("--print-files");
    const Bool_t  kDebugEnv     = arg.HasOnlyAndRemove("--debug-env");
    const Bool_t  kDebugMem     = arg.HasOnlyAndRemove("--debug-mem");
    const Bool_t  kWriteOnly    = arg.HasOnlyAndRemove("--write-only");

    const Bool_t  kQuit         = arg.HasOnlyAndRemove("-q");
    const Bool_t  kBatch        = arg.HasOnlyAndRemove("-b");
    const Bool_t  kOverwrite    = arg.HasOnlyAndRemove("-f");
    //const Bool_t  kForceExec  = arg.HasOnlyAndRemove("-ff");

    const Int_t   kNumAnalysis  = arg.GetIntAndRemove("--n=", -1);
    const TString kOutpath      = arg.GetStringAndRemove("--out=",  ".");
    const TString kOutfile      = arg.GetStringAndRemove("--outf=",  "");
    const Bool_t  kWriteSummary = arg.HasOnlyAndRemove("--sum");
    const TString kNameSummary  = arg.GetStringAndRemove("--sum=");
    const Bool_t  kWriteResult  = arg.HasOnlyAndRemove("--res");
    const TString kNameResult   = arg.GetStringAndRemove("--res=");

    if (arg.GetNumOptions()>0)
    {
        gLog << warn << "WARNING - Unknown commandline options..." << endl;
        arg.Print("options");
        gLog << endl;
    }

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

    //
    // Setup sequence file and check for its existance
    //
    const TString kSequences = arg.GetArgumentStr(0);

    if (gSystem->AccessPathName(kSequences, kFileExists))
    {
        gLog << err << "Sorry, sequences file '" << kSequences << "' doesn't exist." << endl;
        return -1;
    }

    if (kDebugMem)
        TObject::SetObjectStat(kTRUE);

    //
    // Setup sequence and check its validity
    //
    MSequences seq(kSequences);
    if (kNumAnalysis>=0)
        seq.SetNumAnalysis(kNumAnalysis);
    if (kPrintSeq || kPrintFiles)
    {
        gLog << all;
        gLog.Separator(kSequences);
        seq.Print(kPrintFiles?"files":"");
        gLog << endl;
    }
    if (!seq.IsValid())
    {
        gLog << err << "Sequences read but not valid!" << endl << endl;
        return -1;
    }

    //
    // Initialize root
    //
    MArray::Class()->IgnoreTObjectStreamer();
    MParContainer::Class()->IgnoreTObjectStreamer();

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

    //
    // Update frequency by default = 1Hz
    //
    MStatusDisplay *d = new MStatusDisplay;

    // From now on each 'Exit' means: Terminate the application
    d->SetBit(MStatusDisplay::kExitLoopOnExit);
    d->SetTitle(kSequences);

    //
    // Calculate pedestal for pedestal-calculation and calibration
    //
    MJCut job(Form("MJCut #%d", seq.GetNumAnalysis()));
    job.SetEnv(kConfig);
    job.SetEnvDebug(kDebugEnv);
    job.SetDisplay(d);;
    job.SetOverwrite(kOverwrite);
    job.SetPathOut(kOutpath);
    job.SetNameOutFile(kOutfile);
    job.SetNameSummaryFile(kNameSummary);
    job.SetNameResultFile(kNameResult);
    job.EnableWriteOnly(kWriteOnly);
    if (kWriteSummary) // Don't change flag set in SetNameSummaryFile
        job.EnableStorageOfSummary();
    if (kWriteResult)  // Don't change flag set in SetNameSummaryFile
        job.EnableStorageOfResult();

    if (!job.ProcessFile(seq))
    {
        gLog << err << "Calculation of cuts failed." << endl << endl;
        return -1;
    }

    if (!job.GetDisplay())
    {
        gLog << warn << "Display closed by user... execution aborted." << endl << endl;
        return 1;
    }

    if (kBatch || kQuit)
        delete d;
    else
    {
        // From now on each 'Close' means: Terminate the application
        d->SetBit(MStatusDisplay::kExitLoopOnClose);

        // Wait until the user decides to exit the application
        app.Run(kFALSE);
    }

    if (TObject::GetObjectStat())
    {
        TObject::SetObjectStat(kFALSE);
        gObjectTable->Print();
    }

    return 0;
}
