    // @(#)root/tree:$Name:  $:$Id: TBranchObject.cxx,v 1.27 2003/12/30 13:16:51 brun Exp $
    // Author: Rene Brun   11/02/96

    /*************************************************************************
     * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
     * All rights reserved.                                                  *
     *                                                                       *
     * For the licensing terms see $ROOTSYS/LICENSE.                         *
     * For the list of contributors see $ROOTSYS/README/CREDITS.             *
     *************************************************************************/

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    // TBranchObject                                                        //
    //                                                                      //
    // A Branch for the case of an object                                   //                                                                      //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    #include "TROOT.h"
    #include "TFile.h"
    #include "TBranchObject.h"
    #include "TBranchClones.h"
    #include "TTree.h"
    #include "TBasket.h"
    #include "TLeafObject.h"
    #include "TVirtualPad.h"
    #include "TClass.h"
    #include "TRealData.h"
    #include "TDataType.h"
    #include "TDataMember.h"
    #include "TStreamerInfo.h"
    #include "TBrowser.h"

    R__EXTERN  TTree *gTree;

    ClassImp(TBranchObject)

    //______________________________________________________________________________
     TBranchObject::TBranchObject(): TBranch()
    {
    //*-*-*-*-*-*Default constructor for BranchObject*-*-*-*-*-*-*-*-*-*
    //*-*        ====================================

       fNleaves   = 1;
       fOldObject = 0;
    }

    //______________________________________________________________________________
     TBranchObject::TBranchObject(const char *name, const char *classname, void *addobj, Int_t basketsize, Int_t splitlevel, Int_t compress)
        :TBranch()
    {
    //*-*-*-*-*-*-*-*-*-*-*-*-*Create a BranchObject*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
    //*-*                      =====================
    //
       TClass *cl      = gROOT->GetClass(classname);
       if (!cl) {
          Error("TBranchObject","Cannot find class:%s",classname);
          return;
       }
       char **apointer = (char**)(addobj);
       TObject *obj = (TObject*)(*apointer);
       Bool_t delobj = kFALSE;
       if (!obj) {
          obj = (TObject*)cl->New();
          delobj = kTRUE;
       }
       gTree->BuildStreamerInfo(cl,obj);
       if (delobj) delete obj;

       SetName(name);
       SetTitle(name);
       fCompress = compress;
       if (compress == -1 && gTree->GetDirectory()) {
          TFile *bfile = gTree->GetDirectory()->GetFile();
          if (bfile) fCompress = bfile->GetCompressionLevel();
       }
       if (basketsize < 100) basketsize = 100;
       fBasketSize     = basketsize;
       fAddress        = (char*)addobj;
       fClassName      = classname;
       fBasketEntry    = new Int_t[fMaxBaskets];
       fBasketBytes    = new Int_t[fMaxBaskets];
       fBasketSeek     = new Long64_t[fMaxBaskets];
       fOldObject      = 0;

       for (Int_t i=0;i<fMaxBaskets;i++) {
          fBasketBytes[i] = 0;
          fBasketEntry[i] = 0;
          fBasketSeek[i]  = 0;
       }

       TLeaf *leaf     = new TLeafObject(name,classname);
       leaf->SetBranch(this);
       leaf->SetAddress(addobj);
       fNleaves = 1;
       fLeaves.Add(leaf);
       gTree->GetListOfLeaves()->Add(leaf);

    // Set the bit kAutoDelete to specify that when reading
    // in TLeafObject::ReadBasket, the object should be deleted
    // before calling Streamer.
    // It is foreseen to not set this bit in a future version.
       SetAutoDelete(kTRUE);

       fTree       = gTree;
       fDirectory  = fTree->GetDirectory();
       fFileName   = "";

    //*-*-  Create the first basket
       if (splitlevel) return;
       TBasket *basket = new TBasket(name,fTree->GetName(),this);
       fBaskets.Add(basket);
    }

    //______________________________________________________________________________
     TBranchObject::~TBranchObject()
    {
    //*-*-*-*-*-*Default destructor for a BranchObject*-*-*-*-*-*-*-*-*-*-*-*
    //*-*        =====================================

       fBranches.Delete();
    }

    //______________________________________________________________________________
     void TBranchObject::Browse(TBrowser *b)
    {

       Int_t nbranches = fBranches.GetEntriesFast();
       if (nbranches > 1) {
          fBranches.Browse( b );
       }
    }

    //______________________________________________________________________________
     Int_t TBranchObject::Fill()
    {
    //*-*-*-*-*-*-*-*Loop on all leaves of this branch to fill Basket buffer*-*-*
    //*-*            =======================================================

       Int_t nbytes = 0;
       Int_t i;
       Int_t nbranches = fBranches.GetEntriesFast();
       if (nbranches) {
          fEntries++;
          UpdateAddress();
          for (i=0;i<nbranches;i++)  {
             TBranch *branch = (TBranch*)fBranches[i];
             if (!branch->TestBit(kDoNotProcess)) nbytes += branch->Fill();
          }
       } else {
          if (!TestBit(kDoNotProcess)) nbytes += TBranch::Fill();
       }
       return nbytes;
    }

    //______________________________________________________________________________
     Int_t TBranchObject::GetEntry(Int_t entry, Int_t getall)
    {
    //*-*-*-*-*Read all branches of a BranchObject and return total number of bytes
    //*-*      ====================================================================
    //   If entry = 0 take current entry number + 1
    //   If entry < 0 reset entry number to 0
    //
    //  The function returns the number of bytes read from the input buffer.
    //  If entry does not exist  the function returns 0.
    //  If an I/O error occurs,  the function returns -1.

       if (TestBit(kDoNotProcess) && !getall) return 0;
       Int_t nbytes;
       Int_t nbranches = fBranches.GetEntriesFast();

       if (nbranches) {
          if (fAddress == 0) { // try to create object
             if (!TestBit(kWarn)) {
                TClass *cl = gROOT->GetClass(fClassName);
                if (cl) {
                   TObject** voidobj = (TObject**) new Long_t[1];
                   *voidobj = (TObject*)cl->New();
                   SetAddress(voidobj);
                } else {
                   Warning("GetEntry","Cannot get class: %s",fClassName.Data());
                   SetBit(kWarn);
                }
             }
          }
          nbytes = 0;
          Int_t nb;
          for (Int_t i=0;i<nbranches;i++)  {
             TBranch *branch = (TBranch*)fBranches[i];
             if (branch) {
                nb = branch->GetEntry(entry, getall);
                if (nb < 0) return nb;
                nbytes += nb;
             }
          }
       } else {
          nbytes = TBranch::GetEntry(entry, getall);
       }
       return nbytes;
    }

    //______________________________________________________________________________
     Bool_t TBranchObject::IsFolder() const
    {
    //*-*-*-*-*Return TRUE if more than one leaf, FALSE otherwise*-*
    //*-*      ==================================================

       Int_t nbranches = fBranches.GetEntriesFast();
       if (nbranches >= 1) return kTRUE;
       else                return kFALSE;
    }

    //______________________________________________________________________________
     void TBranchObject::Print(Option_t *option) const
    {
    //*-*-*-*-*-*-*-*-*-*-*-*Print TBranch parameters*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
    //*-*                    ========================

       Int_t i;
       Int_t nbranches = fBranches.GetEntriesFast();
       if (nbranches) {
          Printf("*Branch  :%-9s : %-54s *",GetName(),GetTitle());
          Printf("*Entries : %8d : BranchObject (see below)                               *",Int_t(fEntries));
          Printf("*............................................................................*");
          for (i=0;i<nbranches;i++)  {
             TBranch *branch = (TBranch*)fBranches.At(i);
             if (branch) branch->Print(option);
          }
       } else {
          TBranch::Print(option);
       }
    }

    //______________________________________________________________________________
     void TBranchObject::Reset(Option_t *option)
    {
    //*-*-*-*-*-*-*-*Reset a Branch*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
    //*-*            ====================
    //
    //    Existing buffers are deleted
    //    Entries, max and min are reset
    //

       TBranch::Reset(option);

       Int_t i;
       Int_t nbranches = fBranches.GetEntriesFast();
       for (i=0;i<nbranches;i++)  {
          TBranch *branch = (TBranch*)fBranches[i];
          branch->Reset(option);
       }
    }

    //______________________________________________________________________________
     void TBranchObject::SetAddress(void *add)
    {
    //*-*-*-*-*-*-*-*Set address of this branch*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
    //*-*            ====================
    //

       if (TestBit(kDoNotProcess)) return;

       //special case when called from code generated by TTree::MakeClass
       if (Long_t(add) == -1) {
          SetBit(kWarn);
          return;
       }
       fReadEntry = -1;
       Int_t nbranches = fBranches.GetEntriesFast();
       TLeaf *leaf = (TLeaf*)fLeaves.UncheckedAt(0);
       if (leaf) leaf->SetAddress(add);
       TBranch *branch;
       fAddress = (char*)add;
       char *pointer   = fAddress;
       void **ppointer = (void**)add;
       TObject *obj = 0;
       if (add) obj = (TObject*)(*ppointer);
       TClass *cl = gROOT->GetClass(fClassName.Data());
       if (!obj && cl) {
          obj = (TObject*)cl->New();
          *ppointer = (void*)obj;
       }
       //fOldObject = obj;
       Int_t i, offset;
       if (!cl) {
          for (i=0;i<nbranches;i++)  {
             branch  = (TBranch*)fBranches[i];
             pointer = (char*)obj;
             branch->SetAddress(pointer);
          }
          return;
       }
       if (!cl->GetListOfRealData())  cl->BuildRealData(obj);
       if (cl->InheritsFrom("TClonesArray")) {
          char **clpointer = (char**)add;
          char *objadd = *clpointer;
          TClonesArray *clones = (TClonesArray*)objadd;
          if (!clones) {
             Error("SetAddress","Pointer to TClonesArray is null");
             return;
          }
          TClass *clm = clones->GetClass();
          if (clm) {
    			clm->BuildRealData(); //just in case clm derives from an abstract class
    			clm->GetStreamerInfo();
    		}
       }
       char *fullname = new char[200];
       const char *bname = GetName();
       Int_t lenName = strlen(bname);
       Int_t isDot = 0;
       if (bname[lenName-1] == '.') isDot = 1;
       const char *rdname;
       TRealData *rd;
       TIter      next(cl->GetListOfRealData());
       while ((rd = (TRealData *) next())) {
          TDataMember *dm = rd->GetDataMember();
          if (!dm || !dm->IsPersistent()) continue;
          rdname = rd->GetName();
          TDataType *dtype = dm->GetDataType();
          Int_t code = 0;
          if (dtype) code = dm->GetDataType()->GetType();
          offset  = rd->GetThisOffset();
          pointer = (char*)obj + offset;
          branch  = 0;
          if (dm->IsaPointer()) {
             TClass *clobj = 0;
             if (!dm->IsBasic()) clobj = gROOT->GetClass(dm->GetTypeName());
             if (clobj && clobj->InheritsFrom("TClonesArray")) {
                if (isDot) sprintf(fullname,"%s%s",bname,&rdname[1]);
                else       sprintf(fullname,"%s",&rdname[1]);
                branch = (TBranch*)fBranches.FindObject(fullname);
             } else {
                if (!clobj) {
                   // this is a basic type we can handle only if
                   // he has a dimension:
                   const char * index = dm->GetArrayIndex();
                   if (strlen(index)==0) {
                      if (code==1) {
                         // Case of a string ... we do not need the size
                         if (isDot) sprintf(fullname,"%s%s",bname,&rdname[0]);
                         else       sprintf(fullname,"%s",&rdname[0]);
                      } else {
                         continue;
                      }
                   }
                   if (isDot) sprintf(fullname,"%s%s",bname,&rdname[0]);
                   else       sprintf(fullname,"%s",&rdname[0]);
                   // let's remove the stars!
                   UInt_t cursor,pos;
                   for( cursor = 0, pos = 0;
                        cursor < strlen(fullname);
                        cursor ++ ) {
                      if (fullname[cursor]!='*') {
                         fullname[pos++] = fullname[cursor];
                      };
                   };
                   fullname[pos] = '\0';
                   branch = (TBranch*)fBranches.FindObject(fullname);
                } else {
                   if (!clobj->InheritsFrom(TObject::Class())) continue;
                   if (isDot) sprintf(fullname,"%s%s",bname,&rdname[1]);
                   else       sprintf(fullname,"%s",&rdname[1]);
                   branch = (TBranch*)fBranches.FindObject(fullname);
                }
             }
          } else {
             if (dm->IsBasic()) {
                if (isDot) sprintf(fullname,"%s%s",bname,&rdname[0]);
                else       sprintf(fullname,"%s",&rdname[0]);
                branch = (TBranch*)fBranches.FindObject(fullname);
             }
          }
          if(branch) branch->SetAddress(pointer);
       }
       delete [] fullname;
    }

    //______________________________________________________________________________
     void TBranchObject::SetAutoDelete(Bool_t autodel)
    {
    //*-*-*-*-*-*-*-*Set the AutoDelete bit
    //*-*            ====================
    //  This function can be used to instruct Root in TBranchObject::ReadBasket
    //  to not delete the object referenced by a branchobject before reading a
    //  new entry. By default, the object is deleted.
    //  If autodel is kTRUE, this existing object will be deleted, a new object
    //    created by the default constructor, then object->Streamer called.
    //  If autodel is kFALSE, the existing object is not deleted. Root assumes
    //    that the user is taking care of deleting any internal object or array
    //    This can be done in Streamer itself.
    //  If this branch has sub-branches, the function sets autodel for these
    //  branches as well.
    //  We STRONGLY suggest to activate this option by default when you create
    //  the top level branch. This will make the read phase more efficient
    //  because it minimizes the numbers of new/delete operations.
    //  Once this option has been set and the Tree is written to a file, it is
    //  not necessary to specify the option again when reading, unless you
    //  want to set the opposite mode.
    //

       TBranch::SetAutoDelete(autodel);

       Int_t nbranches = fBranches.GetEntriesFast();
       for (Int_t i=0;i<nbranches;i++)  {
          TBranch *branch = (TBranch*)fBranches[i];
          branch->SetAutoDelete(autodel);
       }
    }

    //______________________________________________________________________________
     void TBranchObject::SetBasketSize(Int_t buffsize)
    {
    //*-*-*-*-*-*-*-*Reset basket size for all subbranches of this branchobject
    //*-*            ==========================================================
    //

       TBranch::SetBasketSize(buffsize);

       Int_t i;
       Int_t nbranches = fBranches.GetEntriesFast();
       for (i=0;i<nbranches;i++)  {
          TBranch *branch = (TBranch*)fBranches[i];
          branch->SetBasketSize(fBasketSize);
       }
    }

    //______________________________________________________________________________
     void TBranchObject::Streamer(TBuffer &R__b)
    {
       // Stream an object of class TBranchObject.

       if (R__b.IsReading()) {
          TBranchObject::Class()->ReadBuffer(R__b, this);
       } else {
          TDirectory *dirsav = fDirectory;
          fDirectory = 0;  // to avoid recursive calls

          TBranchObject::Class()->WriteBuffer(R__b, this);

          // make sure that all TStreamerInfo objects referenced by
          // this class are written to the file
          gROOT->GetClass(fClassName.Data())->GetStreamerInfo()->ForceWriteInfo((TFile *)R__b.GetParent(), kTRUE);

             // if branch is in a separate file save this branch
             // as an independent key
          if (!dirsav) return;
          if (!dirsav->IsWritable()) {fDirectory = dirsav; return;}
          TDirectory *pdirectory = fTree->GetDirectory();
          if (!pdirectory) {fDirectory = dirsav; return;}
          const char *treeFileName = pdirectory->GetFile()->GetName();
          TBranch *mother = GetMother();
          const char *motherFileName = treeFileName;
          if (mother && mother != this) {
             motherFileName = mother->GetFileName();
          }
          if (fFileName.Length() > 0 && strcmp(motherFileName,fFileName.Data())) {
             TDirectory *cursav = gDirectory;
             dirsav->cd();
             Write();
             cursav->cd();
          }
          fDirectory = dirsav;
       }
    }

    //______________________________________________________________________________
     void TBranchObject::UpdateAddress()
    {
    //*-*-*-*-*-*-*-*Update branch addresses if a new object was created*-*-*
    //*-*            ===================================================
    //

       void **ppointer = (void**)fAddress;
       if (ppointer == 0) return;
       TObject *obj = (TObject*)(*ppointer);
       if (obj != fOldObject) {
          fOldObject = obj;
          SetAddress(fAddress);
       }
    }