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

//////////////////////////////////////////////////////////////////////////////
//                                                                          //
// MParContainer                                                            //
//                                                                          //
// The MParContainer class is the base class for all MARS parameter         //
// containers. At the moment it is almost the same than ROOT's TNamed.      //
// A TNamed contains the essential elements (name, title)                   //
// to identify a derived object in lists like our MParList or MTaskList.    //
// The main difference is that the name and title isn't stored and read     //
// to and from root files ("//!")                                           //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////
#include "MParContainer.h"

#include <fstream.h>     // ofstream, AsciiWrite

#include <TClass.h>      // IsA
#include <TBaseClass.h>  // GetClassPointer
#include <TROOT.h>       // TROOT::Identlevel
#include <TMethodCall.h> // TMethodCall, AsciiWrite
#include <TDataMember.h> // TDataMember, AsciiWrite
#include <TVirtualPad.h> // gPad

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

ClassImp(MParContainer);

// --------------------------------------------------------------------------
//
//  MParContainer copy ctor
//
MParContainer::MParContainer(const MParContainer &named)
{
    fName  = named.fName;
    fTitle = named.fTitle;

    fLog = named.fLog;

    fReadyToSave = named.fReadyToSave;
}

// --------------------------------------------------------------------------
//
//  MParContainer assignment operator.
//
MParContainer& MParContainer::operator=(const MParContainer& rhs)
{
    if (this == &rhs)
        return *this;

    TObject::operator=(rhs);

    fName  = rhs.fName;
    fTitle = rhs.fTitle;

    fLog         = rhs.fLog;
    fReadyToSave = rhs.fReadyToSave;

    return *this;
}

// --------------------------------------------------------------------------
//
// Make a clone of an object using the Streamer facility.
// If newname is specified, this will be the name of the new object
//
TObject *MParContainer::Clone(const char *newname) const
{

   MParContainer *named = (MParContainer*)TObject::Clone();
   if (newname && strlen(newname)) named->SetName(newname);
   return named;
}

// --------------------------------------------------------------------------
//
//  Compare two MParContainer objects. Returns 0 when equal, -1 when this is
//  smaller and +1 when bigger (like strcmp).
//
Int_t MParContainer::Compare(const TObject *obj) const
{
    if (this == obj) return 0;
    return fName.CompareTo(obj->GetName());
}

// --------------------------------------------------------------------------
//
//  Copy this to obj.
//
void MParContainer::Copy(TObject &obj)
{
    MParContainer &cont = (MParContainer&)obj;

    TObject::Copy(obj);

    cont.fName  = fName;
    cont.fTitle = fTitle;

    cont.fLog         = fLog;
    cont.fReadyToSave = fReadyToSave;
}

// --------------------------------------------------------------------------
//
//  Encode MParContainer into output buffer.
//
void MParContainer::FillBuffer(char *&buffer)
{
    fName.FillBuffer(buffer);
    fTitle.FillBuffer(buffer);
}

// --------------------------------------------------------------------------
//
//  List MParContainer name and title.
//
void MParContainer::ls(Option_t *) const
{
    TROOT::IndentLevel();
    *fLog << all << GetDescriptor() << " " << GetTitle() << ": kCanDelete=";
    *fLog << Int_t(TestBit(kCanDelete)) << endl;
}

// --------------------------------------------------------------------------
//
//  Print MParContainer name and title.
//
void MParContainer::Print(Option_t *) const
{
    *fLog << all << GetDescriptor() << " " << GetTitle() << endl;
}

// --------------------------------------------------------------------------
//
//  Change (i.e. set) the name of the MParContainer.
//  WARNING !!
//  If the object is a member of a THashTable, THashList container
//  The HashTable must be Rehashed after SetName
//  For example the list of objects in the current directory is a THashList
//
void MParContainer::SetName(const char *name)
{
    fName = name;
    if (gPad && TestBit(kMustCleanup)) gPad->Modified();
}

// --------------------------------------------------------------------------
//
//  Change (i.e. set) all the MParContainer parameters (name and title).
//  See also WARNING in SetName
//
void MParContainer::SetObject(const char *name, const char *title)
{
    fName  = name;
    fTitle = title;
    if (gPad && TestBit(kMustCleanup)) gPad->Modified();
}

// --------------------------------------------------------------------------
//
//  Change (i.e. set) the title of the MParContainer.
//
void MParContainer::SetTitle(const char *title)
{
    fTitle = title;
    if (gPad && TestBit(kMustCleanup)) gPad->Modified();
}

// --------------------------------------------------------------------------
//
//  Return size of the MParContainer part of the TObject.
//
Int_t MParContainer::Sizeof() const
{
    Int_t nbytes = fName.Sizeof() + fTitle.Sizeof();
    return nbytes;
}

// --------------------------------------------------------------------------
//
//  If you want to use Ascii-Input/-Output (eg. MWriteAsciiFile) of a
//  container, overload this function.
//
void MParContainer::AsciiRead(ifstream &fin)
{
    *fLog << warn << "To use the the ascii input of " << GetName();
    *fLog << " you have to overload " << ClassName() << "::AsciiRead." << endl;
}

// --------------------------------------------------------------------------
//
//  Write out a data member given as a TDataMember object to an output stream.
//
Bool_t MParContainer::WriteDataMember(ostream &out, const TDataMember *member, Double_t scale) const
{
    if (!member)
        return kFALSE;

    if (!member->IsPersistent() || member->Property()&kIsStatic)
        return kFALSE;

    /*const*/ TMethodCall *call = ((TDataMember*)member)->GetterMethod(); //FIXME: Root
    if (!call)
    {
        *fLog << warn << "Sorry, no getter method found for " << member->GetName() << endl;
        return kFALSE;
    }

    // For debugging: out << member->GetName() << ":";

    switch (call->ReturnType())
    {
    case TMethodCall::kLong:
        Long_t l;
        call->Execute((void*)this, l); // FIXME: const, root
        out << l << " ";
        return kTRUE;

    case TMethodCall::kDouble:
        Double_t d;
        call->Execute((void*)this, d); // FIXME: const, root
        out << (scale*d) << " ";
        return kTRUE;

    default:
    //case TMethodCall::kString:
    //case TMethodCall::kOther:
        /* someone may want to enhance this? */
        return kFALSE;
    }
}

// --------------------------------------------------------------------------
//
//  Write out a data member given by name to an output stream.
//
Bool_t MParContainer::WriteDataMember(ostream &out, const char *member, Double_t scale) const
{
    /*const*/ TClass *cls = IsA()->GetBaseDataMember(member);
    if (!cls)
        return kFALSE;

    return WriteDataMember(out, cls->GetDataMember(member), scale);
}

// --------------------------------------------------------------------------
//
//  Write out a data member from a given TList of TDataMembers.
//  returns kTRUE when at least one member was successfully written
//
Bool_t MParContainer::WriteDataMember(ostream &out, const TList *list) const
{
    Bool_t rc = kFALSE;

    TDataMember *data = NULL;

    TIter Next(list);
    while ((data=(TDataMember*)Next()))
        rc |= WriteDataMember(out, data);

    return rc;
}

// --------------------------------------------------------------------------
//
//  If you want to use Ascii-Input/-Output (eg. MWriteAsciiFile) of a
//  container, you may overload this function. If you don't overload it
//  the data member of a class are written to the file in the order of
//  appearance in the class header (be more specfic: root dictionary)
//  Only data members which are of integer (Bool_t, Int_t, ...) or
//  floating point (Float_t, Double_t, ...) type are written.
//  returns kTRUE when at least one member was successfully written
//
Bool_t MParContainer::AsciiWrite(ostream &out) const
{
    // *fLog << warn << "To use the the ascii output of " << GetName();
    // *fLog << " you have to overload " << ClassName() << "::AsciiWrite." << endl;

    Bool_t rc = WriteDataMember(out, IsA()->GetListOfDataMembers());

    TIter NextBaseClass(IsA()->GetListOfBases());
    TBaseClass *base;
    while ((base = (TBaseClass*) NextBaseClass()))
    {
        /*const*/ TClass *cls = base->GetClassPointer();

        if (!cls)
            continue;

        if (cls->GetClassVersion())
            rc |= WriteDataMember(out, cls->GetListOfDataMembers());
    }

    return rc;
}

TMethodCall *MParContainer::GetterMethod(const char *name) const
{
    TClass *cls = IsA()->GetBaseDataMember(name);
    if (!cls)
    {
        *fLog << err << "'" << name << "' is neither a member of ";
        *fLog << GetDescriptor() << " nor one of its base classes." << endl;
        return NULL;
    }

    TDataMember *member = cls->GetDataMember(name);
    if (!member)
    {
        *fLog << err << "Datamember '" << name << "' not in " << GetDescriptor() << endl;
        return NULL;
    }

    TMethodCall *call = member->GetterMethod();
    if (!call)
    {
        *fLog << err << "Sorry, no getter method found for " << name << endl;
        return NULL;
    }

    return call;
}

void MParContainer::SavePrimitive(ofstream &out, Option_t *o="")
{
    out << "   // SavePrimitive not overloaded... using default." << endl;
    out << "   " << ClassName() << " " << ToLower(fName) << "(\"";
    out << fName << "\", \"" << fTitle << "\");" << endl;
}

