/* ======================================================================== *\
!
! *
! * 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 07/2008 <mailto:tbretz@astro.uni-wuerzburg.de>
!
!   Copyright: MAGIC Software Development, 2000-2008
!
!
\* ======================================================================== */

/////////////////////////////////////////////////////////////////////////////
//
// MParEmulated
//
// Storage Container for emulated branches
//
// Thanks to roots streaming mechanism simple branches can be recreated
// from a file. To read these kind of foreign branches the root system
// allocates memory. We can get a pointer to this memory and the
// offsets to the data, thus allowing to use this data and ecapsulate
// it into the MARS environment. This is done using MParEmulated.
//
/////////////////////////////////////////////////////////////////////////////
#include "MParEmulated.h"

#include <TClass.h>   // root >=5.20/00
#include <TPRegexp.h>
#include <TMethodCall.h>
#include <TStreamerElement.h>
#if ROOT_VERSION_CODE>ROOT_VERSION(5,12,00)
#include <TVirtualStreamerInfo.h>
#endif

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

ClassImp(MParEmulated);

using namespace std;

// --------------------------------------------------------------------------
//
// Default constructor.
//
MParEmulated::MParEmulated(const char *name, const char *title)
    : fPtr(0)
{
    fName  = name  ? name  : "MParEmulated";
    fTitle = title ? title : "Parameter container emulating a class";
}

// --------------------------------------------------------------------------
//
// The default is to print all emulated data members. If an option is
// given it is interpreted as TPRegexp (regular expression) and only
// data members matching this regex are printed.
//
void MParEmulated::Print(Option_t *o) const
{
    TString opt(o);
    if (opt.IsNull())
        opt = ".*";

    TPRegexp regex(opt);
    Print(regex, fClassName, "", 0);
}

// --------------------------------------------------------------------------
//
// Get the class with name clsname and its corresponding streamer info
//
#if ROOT_VERSION_CODE<ROOT_VERSION(5,18,00)
TStreamerInfo *MParEmulated::GetStreamerInfo(const TString &clsname) const
#else
TVirtualStreamerInfo *MParEmulated::GetStreamerInfo(const TString &clsname) const
#endif
{
    TClass *cls = gROOT->GetClass(clsname);
    if (!cls)
    {
        *fLog << err << dbginf << "ERROR - Class " << clsname << " not in dictionary." << endl;
        return 0;
    }

#if ROOT_VERSION_CODE<ROOT_VERSION(5,18,00)
    TStreamerInfo *info = cls->GetStreamerInfo();
#else
    TVirtualStreamerInfo *info = cls->GetStreamerInfo();
#endif
    if (!info)
    {
        *fLog << err << dbginf << "ERROR - No TStreamerInfo for class " << clsname << "." << endl;
        return 0;
    }

    return info;
}

// --------------------------------------------------------------------------
//
// Get the method call for the given method and offset, add method to
// MParContainer::fgListmethodCall
//
TMethodCall *MParEmulated::GetMethodCall(const char *get, Int_t offset) const
{
    TMethodCall *call = new TMethodCall(MParEmulated::Class(), get, Form("%d", offset));;
    fgListMethodCall.Add(call);
    return call;
}

// --------------------------------------------------------------------------
//
// Get the getter method for the given data member. Since we have no real
// getter methods and no real data members we have to fake the TMethodCall.
//
TMethodCall *MParEmulated::GetterMethod(const char *name, TString clsname, Int_t offset) const
{
#if ROOT_VERSION_CODE<ROOT_VERSION(5,18,00)
    TStreamerInfo *info = GetStreamerInfo(clsname);
#else
    TVirtualStreamerInfo *info = GetStreamerInfo(clsname);
#endif
    if (!info)
        return 0;

    const TString arg(name);

    const Ssiz_t p = arg.First('.');

    const TString nam = p<0 ? arg : arg(0, p);

    Int_t off;
    TStreamerElement *el = info->GetStreamerElement(nam, off);
    if (!el)
    {
        *fLog << err << dbginf << "ERROR - No TStreamerInfo for " << nam << " [" << clsname << "]" << endl;
        return 0;
    }

    const TString type = el->GetTypeNameBasic();

    if (type=="int")
        return GetMethodCall("GetInt", offset+off);

    if (type=="double")
        return GetMethodCall("GetDouble", offset+off);

    if (p<0)
    {
        *fLog << err << dbginf << "ERROR - No TStreamerInfo for " << nam << "." << type << " [" << clsname << "]" << endl;
        return 0;
    }

    const TString var = arg(p+1, arg.Length());
    return GetterMethod(var, type, offset+off);
}

// --------------------------------------------------------------------------
//
//  Print the requested data from our memory using the streamer info.
//
void MParEmulated::Print(TPRegexp &regex, TString clsname, TString prefix, Int_t offset) const
{
#if ROOT_VERSION_CODE<ROOT_VERSION(5,18,00)
    TStreamerInfo *info = GetStreamerInfo(clsname);
#else
    TVirtualStreamerInfo *info = GetStreamerInfo(clsname);
#endif
    if (!info)
        return;

    TIter Next(info->GetElements());
    TStreamerElement *el = 0;
    while ((el=(TStreamerElement*)Next()))
    {
        const TString str = prefix+el->GetName();

        if (str(regex).IsNull())
            continue;

        if (el->InheritsFrom(TStreamerBasicType::Class()))
        {
            const TString type(el->GetTypeNameBasic());

            cout << fName << "." << str << "[" << type << "] \t";
            if (type=="int")
                cout << GetInt(el->GetOffset()+offset);

            if (type=="double")
                cout << GetDouble(el->GetOffset()+offset);

            cout << endl;
            continue;
        }

        if (el->InheritsFrom(TStreamerObjectAny::Class()))
        {
            Print(regex, el->GetTypeNameBasic(), str+".",
                  el->GetOffset()+offset);
            continue;
        }
    }
}
