/* ======================================================================== *\
!
! *
! * 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  12/2000 (tbretz@uni-sw.gwdg.de)
!
!   Copyright: MAGIC Software Development, 2000-2001
!
!
\* ======================================================================== */

/////////////////////////////////////////////////////////////////////////////
//                                                                         //
// MReadTree                                                               //
//                                                                         //
// This tasks opens all branches in a specified tree and creates the       //
// corresponding parameter containers if not already existing in the       //
// parameter list.                                                         //
//                                                                         //
// The Process function reads one events from the tree. To go through the  //
// events of one tree make sure that the event number is increased from    //
// outside. It makes also possible to go back by decreasing the number.    //
//                                                                         //
// If you don't want to start reading the first event you have to call     //
// MReadTree::SetEventNum after instantiating your MReadTree-object.       //
//                                                                         //
// To make reading much faster (up to a factor of 10 to 20) you can        //
// ensure that only the data you are really processing is enabled by       //
// calling MReadTree::UseLeaf.                                             //
//                                                                         //
// FIXME: An automatic enabeling scheme would be nice.                     //
//                                                                         //
// Later we'll use TChain::SetNotify to notify MReadTree if the TChain     //
// starts to read a new file.                                              //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////

#include "MReadTree.h"

#include <fstream.h>

#include <TChain.h>
#include <TChainElement.h>
#include <TOrdCollection.h>

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

#include "MTime.h"
#include "MParList.h"
#include "MTaskList.h"

ClassImp(MReadTree);

// --------------------------------------------------------------------------
//
//  Default constructor. It creates an TChain instance which represents the
//  the Tree you want to read and adds the given file (if you gave one).
//  More files can be added using MReadTree::AddFile.
//  Also an empty veto list is created. This list is used if you want to
//  veto (disable or "don't enable") a branch in the tree.
//
MReadTree::MReadTree(const char *tname, const char *fname,
                     const char *name, const char *title)
    : fNumEntry(0), fBranchChoosing(kFALSE), fAutoEnable(kTRUE)
{
    fName  = name  ? name  : "MReadTree";
    fTitle = title ? title : "Task to loop over all events in one single tree";

    fVetoList = new TOrdCollection;
    fVetoList->SetOwner();

    //
    // open the input stream
    //
    fChain = new TChain(tname);

    if (fname)
        fChain->Add(fname);
}

// --------------------------------------------------------------------------
//
// Destructor. It deletes the TChain and veto list object
//
MReadTree::~MReadTree()
{
    //
    // Delete all the pointers to pointers to the objects where the
    // branche data gets stored.
    //
    TIter Next(fChain->GetStatus());

    TChainElement *element = NULL;
    while ((element=(TChainElement*)Next()))
        delete (MParContainer**)element->GetBaddress();

    //
    // Delete the chain and the veto list
    //
    delete fChain;
    delete fVetoList;
}

// --------------------------------------------------------------------------
//
//  If you want to read the given tree over several files you must add
//  the files here before PreProcess is called. Be careful: If the tree
//  doesn't have the same contents (branches) it may confuse your
//  program (trees which are are not existing in later files are not read
//  anymore, tree wich are not existing in the first file are never read)
//
//  Name may use the wildcarding notation, eg "xxx*.root" means all files
//  starting with xxx in the current file system directory.
//
Int_t MReadTree::AddFile(const char *fname)
{
    //
    // FIXME! A check is missing whether the file already exists or not.
    //
    //
    // returns the number of file which were added
    //
    return fChain->Add(fname);
}

void MReadTree::EnableBranchChoosing()
{
    if (fBranchChoosing)
        return;

    *fLog << "Branch choosing method enabled (only enabled branches are read)." << endl;
    fChain->SetBranchStatus("*", kFALSE);
    fBranchChoosing = kTRUE;
}

// --------------------------------------------------------------------------
//
// The first time this function is called all branches are disabled.
// The given branch is enabled. By enabling only the branches you
// are processing you can speed up your calculation many times (up to
// a factor of 10 or 20)
//
void MReadTree::EnableBranch(const char *name)
{
    EnableBranchChoosing();
    fChain->SetBranchStatus(name, kTRUE);
}

// --------------------------------------------------------------------------
//
// This is the implementation of the Auto Enabling Scheme.
// For more information see MTask::AddBranchToList.
// This function loops over all tasks in the tasklist and enables
// all branches which are requested by the tasks.
//
// To enable 'unknown' branches which are not in the Branchlist of
// the tasks you can call EnableBranch
//
void MReadTree::EnableBranches(MParList *plist)
{
    //
    // if auto enabling is off reject the request
    //
    if (!fAutoEnable)
        return;

    //
    // check whether branch choosing must be switched on
    //
    EnableBranchChoosing();

    //
    // request the tasklist from the parameter list.
    // FIXME: Tasklist can have a different name
    //
    const MTaskList *tlist = (MTaskList*)plist->FindObject("MTaskList");
    if (!tlist)
    {
        *fLog << "Cannot use auto enabeling scheme for branches. 'MTaskList' not found." << endl;
        return;
    }

    //
    // Loo over all tasks in the task list.
    //
    MTask *task;
    TIter NextTask(tlist->GetList());
    while ((task=(MTask*)NextTask()))
    {
        //
        // Loop over all branches of this task
        //
        TNamed *branch;
        TIter NextBranch(task->GetListOfBranches());
        while ((branch=(TNamed*)NextBranch()))
        {
            //
            // Get branch name to enable and enable the branch
            //
            const char *name = branch->GetName();

            *fLog << "Enabeling '" << name << "'." << endl;

            fChain->SetBranchStatus(name, kTRUE);
        }
    }
}

// --------------------------------------------------------------------------
//
//  The PreProcess loops (till now) over the branches in the given tree.
//  It checks if the corresponding containers (containers with the same
//  name than the branch name) are existing in the Parameter Container List.
//  If not, a container of objec type 'branch-name' is created (everything
//  after the last semicolon in the branch name is stripped). Only
//  branches which don't have a veto (see VetoBranch) are enabled If the
//  object isn't found in the root dictionary (a list of classes known by the
//  root environment) the branch is skipped and an error message is printed
//  out.
//
Bool_t MReadTree::PreProcess(MParList *pList)
{
    //
    // get number of events in this tree
    //
    fNumEntries = (UInt_t)fChain->GetEntries();

    if (!fNumEntries)
    {
        *fLog << dbginf << "No entries found in chain (file/s)." << endl;
        return kFALSE;
    }

    //
    // output logging information
    //
    *fLog << fNumEntries << " entries found in file(s)." << endl;

    //
    // Get all branches of this tree and
    // create the Iterator to loop over all branches
    //
    TIter Next(fChain->GetListOfBranches());
    TBranch *branch=NULL;

    Int_t num=0;
    //
    // loop over all tasks for processing
    //
    while ( (branch=(TBranch*)Next()) )
    {
        //
        // Get Name of Branch
        //
        const char *name = branch->GetName();

        //
        // Check if enabeling the branch is allowed
        //
        if (fVetoList->FindObject(name))
        {
            *fLog << "Branch " << name << " has veto... skipped." << endl;
            continue;
        }

        //
        // Create a pointer to the pointer to the object in which the
        // branch data is stored. The pointers are stored in the TChain
        // object and we get the pointers from there to delete it.
        //
        MParContainer **pcont = new MParContainer*;

        //
        // check if object is existing in the list
        //
        *pcont=pList->FindCreateObj(name);

        if (!*pcont)
        {
            //
            // if class is not existing in the (root) environment
            // we cannot proceed reading this branch
            //
            *fLog << dbginf << "Warning: Class '" << name << "' not existing in dictionary. Branch skipped." << endl;
            continue;
        }

        //
        // here pcont is a pointer the to container in which the data from
        // the actual branch should be stored - enable branch.
        //
        fChain->SetBranchAddress(name, pcont);

        *fLog << "Branch " << name << " setup for reading." << endl;

        num++;
    }

    *fLog << "MReadTree setup " << num << " branches." << endl;

    EnableBranches(pList);

    return kTRUE;
}

// --------------------------------------------------------------------------
//
//  The Process-function reads one event from the tree (this contains all
//  enabled branches) and increases the position in the file by one event.
//  (Remark: The position can also be set by some member functions
//  If the end of the file is reached the Eventloop is stopped.
//
Bool_t MReadTree::Process()
{
    return fChain->GetEntry(fNumEntry++) != 0;
}

// --------------------------------------------------------------------------
//
// Get the Event with the current EventNumber fNumEntry
//
Bool_t MReadTree::GetEvent()
{
    return fChain->GetEntry(fNumEntry) != 0;
}

// --------------------------------------------------------------------------
//
// Decrease the number of the event which is read by Process() next
// by one or more
//
Bool_t MReadTree::DecEventNum(UInt_t dec)
{
    //!
    //! this function makes Process() read the event one (or more) before
    //! the actual position (event) in the tree
    //!
    if (fNumEntry < dec/*+1*/)
    {
        *fLog << "MReadTree::SetPrevEvent: WARNING: " << fNumEntry/*-1*/ << "-" << dec << " out of Range." << endl;
        return kFALSE;
    }

    fNumEntry -= dec/*+1*/;
    return kTRUE;
}

// --------------------------------------------------------------------------
//
// Increase the number of the event which is read by Process() next
// by one or more
//
Bool_t MReadTree::IncEventNum(UInt_t inc)
{
    //!
    //! this function makes Process() read the next (or more) after
    //! the actual position (event) in the tree
    //! (Be careful: IncEventNum() or IncEventNum(1) does not chenge anything
    //!  in the standard behaviour of the task)
    //!
    if (fNumEntry+inc/*-1*/ >= fNumEntries)
    {
        *fLog << "MReadTree::SkipEvents: WARNING: " << fNumEntry/*-1*/ << "+" << inc << " out of Range." << endl;
        return kFALSE;
    }

    fNumEntry += inc/*-1*/;
    return kTRUE;
}

// --------------------------------------------------------------------------
//
// this function makes Process() read event number nr next
//
// Remark: You can use this function after instatiating you MReadTree-object
//         to set the event number from which you want to start reading.
//
Bool_t MReadTree::SetEventNum(UInt_t nr)
{
    if (nr>=fNumEntries)
    {
        *fLog << "MReadTree::SetEventNum: WARNING: " << nr << " out of Range." << endl;
        return kFALSE;
    }

    fNumEntry = nr;
    return kTRUE;
}

// --------------------------------------------------------------------------
//
// If you don't want that an object is created for this branch
// and/or the branch address is set in the Preprocessing of
// MReadTree use VetoBranch. This saves mainly memory space.
//
void MReadTree::VetoBranch(const char *name)
{
    TNamed *branch = new TNamed(name, "");
    fVetoList->Add(branch);
}
