/* ======================================================================== *\
!
! *
! * This file is part of MARS, the MAGIC Analysis and Reconstruction
! * Software. It is distributed to you in the hope that it can be a useful
! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
! * It is distributed WITHOUT ANY WARRANTY.
! *
! * Permission to use, copy, modify and distribute this software and its
! * documentation for any purpose is hereby granted without fee,
! * provided that the above copyright notice appear in all copies and
! * that both that copyright notice and this permission notice appear
! * in supporting documentation. It is provided "as is" without express
! * or implied warranty.
! *
!
!
!   Author(s): Thomas Bretz, 8/2004 <mailto:tbretz@astro.uni-wuerzburg.de>
!
!   Copyright: MAGIC Software Development, 2000-2004
!
!
\* ======================================================================== */

/////////////////////////////////////////////////////////////////////////////
//
//  MJob
//
// A base class for jobs
//
// SetDebugEnv(0) // switch off debugging
// SetDebugEnv(1) // reserved
// SetDebugEnv(2) // print untouched resources after evtloop resources setup
// SetDebugEnv(3) // do 2) and debug setting env completely
//
/////////////////////////////////////////////////////////////////////////////
#include "MJob.h"

#include "MEnv.h"
#include <TFile.h>
#include <TSystem.h>
#include <TObjArray.h>

#include "MIter.h"

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

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

ClassImp(MJob);

using namespace std;

// --------------------------------------------------------------------------
//
// Default constructor. 
//
// Sets fDataFlag to 0
//
MJob::MJob(const char *name, const char *title) : fEnv(0), fEnvDebug(0), fOverwrite(kFALSE), fMaxEvents(0)
{
    fName  = name  ? name  : "MJob";
    fTitle = title ? title : "Base class for jobs";
}

void MJob::ClearEnv()
{
    if (fEnv && TestBit(kIsOwner))
        delete fEnv;
    ResetBit(kIsOwner);
    fEnv=0;
}

MJob::~MJob()
{
    ClearEnv();
}

Bool_t MJob::SetEnv(const char *env, const char *prefix)
{
    ClearEnv();

    const Bool_t fileexist = !gSystem->AccessPathName(env, kFileExists);
    if (!fileexist)
    {
        *fLog << err << "ERROR - Resource file '" << env << "' not found... no resources applied." << endl;
        return kFALSE;
    }

    fEnv = new MEnv(env);
    SetBit(kIsOwner);

    fEnvPrefix = prefix;
    if (!prefix)
        fEnvPrefix = fName.First(' ')>0 ? fName(0, fName.First(' ')) : fName;

    if (fEnvPrefix.EndsWith("."))
        fEnvPrefix.Remove(fEnvPrefix.Length()-1);

    return kTRUE;
}

void MJob::SetEnv(MEnv *env, const char *prefix)
{
    ClearEnv();

    fEnv = env;

    fEnvPrefix = prefix;
    if (!prefix)
        fEnvPrefix = fName.First(' ')>0 ? fName(0, fName.First(' ')) : fName;

    if (fEnvPrefix.EndsWith("."))
        fEnvPrefix.Remove(fEnvPrefix.Length()-1);
}

void MJob::FixPath(TString &path) const
{
    path.ReplaceAll("\015", "");

    if (path==(TString)"/")
        return;

    if (path.EndsWith("/"))
        path.Remove(path.Length()-1);
}

void MJob::SetPathOut(const char *path)
{
    fPathOut = path;
    FixPath(fPathOut);
}

void MJob::SetPathIn(const char *path)
{
    fPathIn = path;
    FixPath(fPathIn);
}

void MJob::SetPathData(const char *path)
{
    fPathData = path;
    FixPath(fPathData);
}

const TEnv *MJob::GetEnv() const
{
    return static_cast<const TEnv *const>(fEnv);
}

Int_t MJob::GetEnv(const char *name, Int_t dflt) const
{
    return GetEnvValue(*fEnv, fEnvPrefix, name, dflt); //    return fEnv->GetValue(Form("%s%s", fEnvPrefix.Data(), name), dflt);
}

Double_t MJob::GetEnv(const char *name, Double_t dflt) const
{
    return GetEnvValue(*fEnv, fEnvPrefix, name, dflt); //    return fEnv->GetValue(Form("%s%s", fEnvPrefix.Data(), name), dflt);
}

const char *MJob::GetEnv(const char *name, const char *dflt) const
{
    return GetEnvValue(*fEnv, fEnvPrefix, name, dflt); //fEnv->GetValue(Form("%s%s", fEnvPrefix.Data(), name), dflt);
}

Bool_t MJob::HasEnv(const char *name) const
{
    return IsEnvDefined(*fEnv, fEnvPrefix, name, fEnvDebug>2);//fEnv->Lookup(Form("%s%s", fEnvPrefix.Data(), name));
}

Bool_t MJob::CheckEnv()
{
    if (!fEnv)
        return kTRUE;

    TString p;
    p = GetEnv("PathOut", "");
    if (!p.IsNull())
        SetPathOut(p);

    p = GetEnv("PathIn", "");
    if (!p.IsNull())
        SetPathIn(p);

    p = GetEnv("PathData", "");
    if (!p.IsNull())
        SetPathData(p);

    SetMaxEvents(GetEnv("MaxEvents", fMaxEvents));
    SetOverwrite(GetEnv("Overwrite", fOverwrite));
    SetEnvDebug( GetEnv("EnvDebug",  fEnvDebug));

    return CheckEnvLocal();
}

//------------------------------------------------------------------------
//
// Returns the result of c.ReadEnv(*fEnv, fEnvPrefix, fEnvDebug>2)
// By adding the container first to a MParList it is ensured that
// all levels are checked.
//
Bool_t MJob::CheckEnv(MParContainer &c) const
{
    if (!fEnv)
        return kTRUE;

    // Make sure that all levels are checked
    MParList l;
    l.AddToList(&c);
    return l.ReadEnv(*fEnv, fEnvPrefix+".", fEnvDebug>2);
}

Bool_t MJob::SetupEnv(MEvtLoop &loop) const
{
    if (!fEnv)
        return kTRUE;

    if (!loop.ReadEnv(*fEnv, fEnvPrefix, fEnvDebug>2))
        return kFALSE;

    if (fEnvDebug>1)
        fEnv->PrintUntouched();

    return kTRUE;
}

//------------------------------------------------------------------------
//
// Write containers in list to gFile. Returns kFALSE if no gFile or any
// container couldn't be written. kTRUE otherwise.
//
Bool_t MJob::WriteContainer(TCollection &list) const
{
    if (!gFile)
    {
        *fLog << err << dbginf << "ERROR - No file open (gFile==0)" << endl;
        return kFALSE;
    }

    TIter Next(&list);
    TObject *o=0;
    while ((o=Next()))
    {
        *fLog << inf << " - Writing " << MParContainer::GetDescriptor(*o) << "..." << flush;
        if (o->Write(o->GetName())<=0)
        {
            *fLog << err << dbginf << "ERROR - Writing " << MParContainer::GetDescriptor(*o) << " to file " << gFile->GetName() << endl;
            return kFALSE;
        }
        *fLog << "ok." << endl;
    }
    return kTRUE;
}

//------------------------------------------------------------------------
//
// Read containers in list into list from gFile
// Returns kFALSE if no gFile or any container couldn't be read.
//
Bool_t MJob::ReadContainer(TCollection &list) const
{
    if (!gFile)
    {
        *fLog << err << dbginf << "ERROR - No file open (gFile==0)" << endl;
        return kFALSE;
    }

    MIter Next(&list);
    MParContainer *o=0;
    while ((o=Next()))
    {
        *fLog << inf << " - Reading " << o->GetDescriptor() << "..." << flush;
        if (o->Read(o->GetName())<=0)
        {
            *fLog << err << dbginf << "ERROR - Reading " << o->GetDescriptor() << " from file " << gFile->GetName() << endl;
            return kFALSE;
        }
        *fLog << "ok." << endl;
    }
    return kTRUE;
}

//------------------------------------------------------------------------
//
// Write containers in cont (and - if available) the status display to
// fPathOut+"/"+name
//
Bool_t MJob::WriteContainer(TCollection &cont, const char *name, const char *option, const int compr) const
{
    if (fPathOut.IsNull())
    {
        *fLog << inf << "No output path specified via SetPathOut - no output written." << endl;
        return kTRUE;
    }

    TString oname(fPathOut);
    oname += "/";
    oname += name;

    *fLog << inf << "Writing to file: " << oname << endl;

    TString title("File Written by ");
    title += fName;

    TFile file(oname, option, title, compr);
    if (!file.IsOpen())
    {
        *fLog << err << "ERROR - Couldn't open file " << oname << " for writing..." << endl;
        return kFALSE;
    }

    if (fDisplay)
        cont.Add((TObject*)(fDisplay));

    return WriteContainer(cont);
}

TString MJob::ExpandPath(TString fname)
{
    // empty
    if (fname.IsNull())
        return "";

    // Expand path using environment
    gSystem->ExpandPathName(fname);

    // Absolute path
    if (fname[0]=='/')
    {
        gLog << dbg << "MJob::ExpandPath - Path " << fname << " is absolute." << endl;
        return fname;
    }

    // relative path to file and file could be found
    if (!gSystem->AccessPathName(fname, kFileExists))
    {
        gLog << dbg << "MJob::ExpandPath - Relative path " << fname << " found..." << endl;
        return fname;
    }

    // Now check gEnv and MARSSYS. gEnv can overwrite MARSSYS
    TString path(gEnv ? gEnv->GetValue("Mars.Path", "$MARSSYS") : "$MARSSYS");

    // Expand path using environment
    gSystem->ExpandPathName(path);

    // check if path ends with a slash
    if (!path.EndsWith("/"))
        path += "/";

    // compile full qualified path
    path += fname;

    gLog << dbg << "MJob::ExpandPath - Filename expanded to " << path << endl;

    // return new path
    return path;
}

void MJob::SortArray(TArrayI &arr)
{
    TArrayI idx(arr.GetSize());
    TArrayI srt(arr);

    TMath::Sort(arr.GetSize(), srt.GetArray(), idx.GetArray(), kFALSE);

    for (int i=0; i<arr.GetSize(); i++)
        arr[i] = srt[idx[i]];
}
