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

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

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

#include "MStatusDisplay.h"

#include "MSequence.h"
#include "MJStar.h"

using namespace std;

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

    //                1         2         3         4         5         6
    //       123456789012345678901234567890123456789012345678901234567890
    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()
{
    //                1         2         3         4         5         6         7         8
    //       12345678901234567890123456789012345678901234567890123456789012345678901234567890
    gLog << all << endl;
    gLog << "Sorry the usage is:" << endl;
    gLog << " star [options] sequence.txt" << endl << endl;
    gLog << " Arguments:" << endl;
    gLog << "   sequence.txt:             An ascii file defining a sequence of runs" << 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 << endl;
    gLog << endl;
    gLog << " Input Options:" << endl;
    gLog << "   -mc                       You must use this for MC files (PRELIMINARY)" << endl << endl;
    gLog << " Output options:" << endl;
    gLog << "   -q                        Quit when job is finished" << endl;
    gLog << "   -f                        Force overwrite of existing files" << endl;
    gLog << "   -ff                       Force execution if not all files found" << endl;
    gLog << "   --ind=path                Path where to search for the calibrated data (Y)" << endl;
    gLog << "                             [default=standard path in datacenter]" << endl;
    gLog << "   --out=path                Path to write the all results to [def=local path]" << endl;
    gLog << "                             (overwrites --outc and --outy)" << endl;
    gLog << "   --print-seq               Print Sequence information" << endl;
    gLog << "   --print-files             Print Files taken from Sequence" << endl;
    gLog << "   --print-only              Do not excute anything except print" << endl;
    gLog << "   --config=star.rc          Resource file [default=star.rc]" << endl;
    gLog << endl;
    gLog << "   -?, -h, --help            This help" << endl << endl;
}

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

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

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

    gLog.Setup(arg);

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

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

    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 Bool_t  kIsMC       = arg.HasOnlyAndRemove("-mc");

    const TString kInpath     = arg.GetStringAndRemove("--ind=", "");
    const TString kOutpath    = arg.GetStringAndRemove("--out=", ".");

    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 kSequence = arg.GetArgumentStr(0);

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

    //
    // Setup sequence and check its validity
    //
    MSequence seq(kSequence);
    if (kPrintSeq)
    {
        gLog << all;
        gLog.Separator(kSequence);
        seq.Print();
        gLog << endl;
    }
    if (!seq.IsValid())
    {
        gLog << err << "Sequence read but not valid!" << endl << endl;
        return -1;
    }

    //
    // Process print options
    //
    MDirIter iter;

    const Int_t n0 = seq.SetupDatRuns(iter, kInpath);
    const Int_t n1 = seq.GetNumDatRuns();

    if (kPrintFiles)
    {
        gLog << all;
        gLog.Separator("Data Files");
        iter.Print("all");
        gLog << endl;
    }

    //
    // Check for existance of all files
    //
    if (n0 != n1)
    {
        if (kForceExec)
            gLog << warn << "WARNING";
        else
            gLog << err << "ERROR";
        gLog << " - " << n1 << " files in sequence defined, but " << n0 << " found in ";
        gLog << (kInpath.IsNull() ? "<defaultpath>" : kInpath.Data()) << endl;
        if (!kForceExec)
            return -1;
    }

    if (kPrintOnly)
        return 0;

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

    TApplication app("Star", &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(kSequence);

    //
    // Do calibration
    //
    MJStar job(Form("MJStar #%d", seq.GetSequence()));
    job.SetSequence(seq);
    job.SetEnv(kConfig);
    job.SetEnvDebug(kDebugEnv);
    job.SetDisplay(d);;
    job.SetOverwrite(kOverwrite);
    job.SetPathOut(kOutpath);
    job.SetPathData(kInpath);
    // job.SetPathIn(kInpath); // not yet needed

    if (!job.ProcessFile(kIsMC))
    {
        gLog << err << "Calculation of image parameters 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;
        return 0;
    }

    // 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);
    return 0;
}
