/* ======================================================================== *\
!
! *
! * 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.       //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////

#include "MReadTree.h"

#include <fstream.h>

#include <TFile.h>
#include <TChain.h>
#include <TArrayC.h>
#include <TObjArray.h>
#include <TChainElement.h>

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

#include "MTime.h"
#include "MParList.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)
{
    *fName  = name  ? name  : "MReadTree";
    *fTitle = title ? title : "Task to loop over all events in one single tree";

    fVetoList = new TArrayC;
    //
    // 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)
//
/*Int_t*/ void 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  root >3.0*/ fChain->Add(fname);
}

// --------------------------------------------------------------------------
//
//  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;
    
    //
    // 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 (HasVeto(name))
            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);
    }

    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()
{
    //
    // check for end of file
    //
    if (fNumEntry>=fNumEntries)
        return kFALSE;

    //
    // get entry
    //
    fChain->GetEntry(fNumEntry);
    cout << " " << fChain->LoadTree(fNumEntry) << " "<<flush;

    fNumEntry++;

    return kTRUE;
}

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

    return kTRUE;
}

// --------------------------------------------------------------------------
//
// 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;
}

// --------------------------------------------------------------------------
//
// This function checks if the Branch Name is part of the Veto List.
// This means, that the preprocess doesn't enable the branch.
//
Bool_t MReadTree::HasVeto(const char *name) const
{
    const size_t nlen = strlen(name)+1;

    char  *pos = fVetoList->GetArray();
    size_t len = fVetoList->GetSize();

    //
    // now we compare the 'strings' in the list word by word
    // (string or word in this context means a zero terminated
    // array of chars
    //

    for (;;)
    {
        //
        // Search for the first byte of the name
        //
        char *c = (char*)memchr(pos, name[0], len);

        //
        // if we don't find the first byte, the list cannot contain 'name'
        //
        if (!c)
            return kFALSE;

        //
        // calculate and check how many bytes remains in the list
        //
        len -= c-pos;

        //
        // if there are not enough bytes to match the query we are done
        //
        if (len<nlen)
            return kFALSE;

        //
        // check if the next 'nlen' byte (including the trailing '\0'
        // are matching
        //
        if (!memcmp(c, name, nlen))
            return kTRUE;

        //
        // we didn't find the string, goto the next 'string'
        //
        pos = (char*)memchr(c, '\0', len);

        //
        // check if there is a 'next' string really
        //
        if (!pos)
            return kFALSE;

        //
        // calculate the remaining length
        //
        len -= pos-c;

        //
        // if there are not enough bytes to match the query we are done
        //
        if (len<nlen)
            return kFALSE;
    }
}


// --------------------------------------------------------------------------
//
// If you don't want that a branch is enabled within the PreProcess you
// can set a veto for enabeling the branch. (This means also the
// corresponding object won't be created automatically)
//
// This functionality is for experienced users which don't want to
// read in branches which are not processed in the program (for
// speed reasons)
//
void MReadTree::VetoBranch(const char *name)
{
    //
    // Add this file as the last entry of the list
    // (including the trailing '\0')
    //
    const int sz  = fVetoList->GetSize();
    const int tsz = strlen(name)+1;

    fVetoList->Set(sz+tsz);

    memcpy(fVetoList->GetArray()+sz, name, tsz);
}
