    // @(#)root/treeplayer:$Name:  $:$Id: TBranchProxy.h,v 1.2 2004/06/25 22:45:41 rdm Exp $

    // Author: Philippe Canal 01/06/2004

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

    #ifndef ROOT_TBranchProxy
    #define ROOT_TBranchProxy

    #ifndef ROOT_TBranchProxyDirector
    #include "TBranchProxyDirector.h"
    #endif
    #ifndef ROOT_TTree
    #include "TTree.h"
    #endif
    #ifndef ROOT_TString
    #include "TString.h"
    #endif
    #ifndef ROOT_TBranchElement
    #include "TBranchElement.h"
    #endif
    #ifndef ROOT_TStreamerInfo
    #include "TStreamerInfo.h"
    #endif
    #ifndef ROOT_TStreamerElement
    #include "TStreamerElement.h"
    #endif
    #ifndef ROOT_Riostream
    #include "Riostream.h"
    #endif
    #ifndef ROOT_TError
    #include "TError.h"
    #endif

    #include <list>
    #include <algorithm>

    // Note we could protect the arrays more by introducing a class TArrayWrapper<class T> which somehow knows

    // its internal dimensions and check for them ...

    // template <class T> TArrayWrapper {

    // public:

    //    TArrayWrapper(void *where, int dim1);

    //    const T operator[](int i) {

    //       if (i>=dim1) return 0;

    //       return where[i];

    //    };

    // };

    // 2D array would actually be a wrapper of a wrapper i.e. has a method TArrayWrapper<T> operator[](int i);

    namespace ROOT {

       class TBranchProxyHelper {
       public:
          TString name;
          TBranchProxyHelper(const char *left,const char *right = 0) :
             name() {
             if (left) {
                name = left;
                if (strlen(left)&&right) name += ".";
             }
             if (right) {
                name += right;
             }
          }
          operator const char*() { return name.Data(); };
       };

       class TBranchProxy {
       protected:
          TBranchProxyDirector *fDirector; // contain pointer to TTree and entry to be read

          Bool_t   fInitialized;

          const TString fBranchName;  // name of the branch to read
          TBranchProxy *fParent;      // Proxy to a parent object

          const TString fDataMember;  // name of the (eventual) data member being proxied

          const Bool_t  fIsMember;    // true if we proxy an unsplit data member
          Bool_t        fIsClone;     // true if we proxy the inside of a TClonesArray
          Bool_t        fIsaPointer;  // true if we proxy a data member of pointer type

          TString           fClassName;     // class name of the object pointed to by the branch
          TClass           *fClass;         // class name of the object pointed to by the branch
          TStreamerElement *fElement;
          Int_t             fMemberOffset;
          Int_t             fOffset;        // Offset inside the object

          TBranch *fBranch;       // branch to read
          TBranch *fBranchCount;  // eventual auxiliary branch (for example holding the size)

          TTree   *fLastTree; // TTree containing the last entry read
          Long64_t fRead;     // Last entry read

          void    *fWhere;    // memory location of the data

       public:
          virtual void Print();

          TBranchProxy();
          TBranchProxy(TBranchProxyDirector* boss, const char* top, const char* name = 0);
          TBranchProxy(TBranchProxyDirector* boss, const char *top, const char *name, const char *membername);
          TBranchProxy(TBranchProxyDirector* boss, TBranchProxy *parent, const char* membername);
          virtual ~TBranchProxy();

          TBranchProxy* proxy() { return this; }

          void Reset();

          Bool_t Setup();

          Bool_t IsInitialized() {
             return (fLastTree == fDirector->GetTree()) && (fLastTree);
          }

          Bool_t IsaPointer() const {
             return fIsaPointer;
          }

          Bool_t Read() {
             if (fDirector==0) return false;

             if (fDirector->GetReadEntry()!=fRead) {
                if (!IsInitialized()) {
                   if (!Setup()) {
    		 Error("Read",Form("Unable to initialize %s\n",fBranchName.Data()));
                      return false;
                   }
                }
                if (fParent) fParent->Read();
                else {
                   if (fBranchCount) {
                      fBranchCount->GetEntry(fDirector->GetReadEntry());
                   }
                   fBranch->GetEntry(fDirector->GetReadEntry());
                }
                fRead = fDirector->GetReadEntry();
             }
             return IsInitialized();
          }

          TClass *GetClass() {
             if (fDirector==0) return 0;
             if (fDirector->GetReadEntry()!=fRead) {
                if (!IsInitialized()) {
                   if (!Setup()) {
                      return 0;
                   }
                }
             }
             return fClass;
          }

          // protected:

          virtual  void *GetStart(int /*i*/=0) {
             // return the address of the start of the object being proxied. Assumes

             // that Setup() has been called.

             if (fParent) {
                fWhere = ((unsigned char*)fParent->GetStart()) + fMemberOffset;
             }
             if (IsaPointer()) {
                if (fWhere) return *(void**)fWhere;
                else return 0;
             } else {
                return fWhere;
             }
          }

          virtual void *GetClaStart(int i=0) {
             // return the address of the start of the object being proxied. Assumes

             // that Setup() has been called.  Assumes the object containing this data

             // member is held in TClonesArray.

             void *tcaloc;
             char *location;

             if (fIsClone) {

                TClonesArray *tca;
                tca = (TClonesArray*)GetStart();

                if (tca->GetLast()<i) return 0;

                location = (char*)tca->At(i);

                return location;

             } else if (fParent) {

                tcaloc = ((unsigned char*)fParent->GetStart());
                location = (char*)fParent->GetClaStart(i);

             } else {

                tcaloc = fWhere;
                TClonesArray *tca;
                tca = (TClonesArray*)tcaloc;

                if (tca->GetLast()<i) return 0;

                location = (char*)tca->At(i);
             }

             if (location) location += fOffset;
             else return 0;

             if (IsaPointer()) {
                return *(void**)(location);
             } else {
                return location;
             }

          }
       };

       class TArrayCharProxy : public TBranchProxy {
       public:
          void Print() {
             TBranchProxy::Print();
             cout << "fWhere " << fWhere << endl;
             if (fWhere) cout << "value? " << *(unsigned char*)GetStart() << endl;
          }

          TArrayCharProxy() : TBranchProxy() {}
          TArrayCharProxy(TBranchProxyDirector *director, const char *name) : TBranchProxy(director,name) {};
          TArrayCharProxy(TBranchProxyDirector *director, const char *top, const char *name) :
             TBranchProxy(director,top,name) {};
          TArrayCharProxy(TBranchProxyDirector *director, const char *top, const char *name, const char *data) :
             TBranchProxy(director,top,name,data) {};
          TArrayCharProxy(TBranchProxyDirector *director, TBranchProxy *parent, const char *name) : TBranchProxy(director,parent, name) {};
          ~TArrayCharProxy() {};

          unsigned char at(int i) {
             static unsigned char default_val;
             if (!Read()) return default_val;
             // should add out-of bound test

             unsigned char* str = (unsigned char*)GetStart();
             return str[i];
          }

          unsigned char operator [](int i) {
             return at(i);
          }

          const char* c_str() {
             if (!Read()) return "";
             return (const char*)GetStart();
          }

          operator std::string() {
             if (!Read()) return "";
             return std::string((const char*)GetStart());
          }

       };

       class TClaProxy : public TBranchProxy {
       public:
          void Print() {
             TBranchProxy::Print();
             cout << "fWhere " << fWhere << endl;
             if (fWhere) {
                if (IsaPointer()) {
                   cout << "location " << *(TClonesArray**)fWhere << endl;
                } else {
                   cout << "location " << fWhere << endl;
                }
             }
          }

          TClaProxy() : TBranchProxy() {}
          TClaProxy(TBranchProxyDirector *director, const char *name) : TBranchProxy(director,name) {};
          TClaProxy(TBranchProxyDirector *director, const char *top, const char *name) :
             TBranchProxy(director,top,name) {};
          TClaProxy(TBranchProxyDirector *director, const char *top, const char *name, const char *data) :
             TBranchProxy(director,top,name,data) {};
          TClaProxy(TBranchProxyDirector *director, TBranchProxy *parent, const char *name) : TBranchProxy(director,parent, name) {};
          ~TClaProxy() {};

          const TClonesArray* ptr() {
             if (!Read()) return 0;
             return (TClonesArray*)GetStart();
          }

          const TClonesArray* operator->() { return ptr(); }

       };

       template <class T>
       class TImpProxy : public TBranchProxy {
       public:
          void Print() {
             TBranchProxy::Print();
             cout << "fWhere " << fWhere << endl;
             if (fWhere) cout << "value? " << *(T*)GetStart() << endl;
          }

          TImpProxy() : TBranchProxy() {};
          TImpProxy(TBranchProxyDirector *director, const char *name) : TBranchProxy(director,name) {};
          TImpProxy(TBranchProxyDirector *director, const char *top, const char *name) :
             TBranchProxy(director,top,name) {};
          TImpProxy(TBranchProxyDirector *director, const char *top, const char *name, const char *data) :
             TBranchProxy(director,top,name,data) {};
          TImpProxy(TBranchProxyDirector *director, TBranchProxy *parent, const char *name) : TBranchProxy(director,parent, name) {};
          ~TImpProxy() {};

          operator T() {
             if (!Read()) return 0;
             return *(T*)GetStart();
          }

          // Make sure that the copy methods are really private

    #ifdef private
    #undef private
    #define private_was_replaced
    #endif
          // For now explicitly disable copying into the value (i.e. the proxy is read-only).

       private:
          TImpProxy(T);
          TImpProxy &operator=(T);
    #ifdef private_was_replaced
    #define private public
    #endif

       };

       template <class T>
       class TArrayProxy : public TBranchProxy {
       public:
          void Print() {
             TBranchProxy::Print();
             cout << "fWhere " << fWhere << endl;
             if (fWhere) cout << "value? " << *(T*)GetStart() << endl;
          }

          TArrayProxy() : TBranchProxy() {}
          TArrayProxy(TBranchProxyDirector *director, const char *name) : TBranchProxy(director,name) {};
          TArrayProxy(TBranchProxyDirector *director, const char *top, const char *name) :
             TBranchProxy(director,top,name) {};
          TArrayProxy(TBranchProxyDirector *director, const char *top, const char *name, const char *data) :
             TBranchProxy(director,top,name,data) {};
          TArrayProxy(TBranchProxyDirector *director, TBranchProxy *parent, const char *name) : TBranchProxy(director,parent, name) {};
          ~TArrayProxy() {};

          const T& at(int i) {
             static T default_val;
             if (!Read()) return default_val;
             // should add out-of bound test

             return ((T*)GetStart())[i];
          }

          const T& operator [](int i) {
             return at(i);
          }

       };

       template <class T, int d2, int d3 >
       class TArray3Proxy : public TBranchProxy {
       public:
          typedef T array_t[d2][d3];

          void Print() {
             TBranchProxy::Print();
             cout << "fWhere " << fWhere << endl;
             if (fWhere) cout << "value? " << *(T*)GetStart() << endl;
          }

          TArray3Proxy() : TBranchProxy() {}
          TArray3Proxy(TBranchProxyDirector *director, const char *name) : TBranchProxy(director,name) {};
          TArray3Proxy(TBranchProxyDirector *director, const char *top, const char *name) :
             TBranchProxy(director,top,name) {};
          TArray3Proxy(TBranchProxyDirector *director, const char *top, const char *name, const char *data) :
             TBranchProxy(director,top,name,data) {};
          TArray3Proxy(TBranchProxyDirector *director, TBranchProxy *parent, const char *name) : TBranchProxy(director,parent, name) {};
          ~TArray3Proxy() {};

          const array_t* at(int i) {
             static array_t default_val;
             if (!Read()) return &default_val;
             // should add out-of bound test

             return ((array_t**)GetStart())[i];
          }

          const array_t* operator [](int i) {
             return at(i);
          }

       };

       template <class T>
       class TClaImpProxy : public TBranchProxy {
       public:

          void Print() {
             TBranchProxy::Print();
             cout << "fWhere " << fWhere << endl;
             if (fWhere) cout << "value? " << *(T*)GetStart() << endl;
          }

          TClaImpProxy() : TBranchProxy() {};
          TClaImpProxy(TBranchProxyDirector *director, const char *name) : TBranchProxy(director,name) {};
          TClaImpProxy(TBranchProxyDirector *director,  const char *top, const char *name) :
             TBranchProxy(director,top,name) {};
          TClaImpProxy(TBranchProxyDirector *director,  const char *top, const char *name, const char *data) :
             TBranchProxy(director,top,name,data) {};
          TClaImpProxy(TBranchProxyDirector *director, TBranchProxy *parent, const char *name) : TBranchProxy(director,parent, name) {};
          ~TClaImpProxy() {};

          const T& at(int i) {
             static T default_val;
             if (!Read()) return default_val;
             if (fWhere==0) return default_val;

             T *temp = (T*)GetClaStart(i);

             if (temp) return *temp;
             else return default_val;

          }

          const T& operator [](int i) { return at(i); }

          // Make sure that the copy methods are really private

    #ifdef private
    #undef private
    #define private_was_replaced
    #endif
          // For now explicitly disable copying into the value (i.e. the proxy is read-only).

       private:
          TClaImpProxy(T);
          TClaImpProxy &operator=(T);
    #ifdef private_was_replaced
    #define private public
    #endif

       };

       template <class T>
       class TClaArrayProxy : public TBranchProxy {
       public:
          typedef T* array_t;

          void Print() {
             TBranchProxy::Print();
             cout << "fWhere " << fWhere << endl;
             if (fWhere) cout << "value? " << *(T*)GetStart() << endl;
          }

          TClaArrayProxy() : TBranchProxy() {}
          TClaArrayProxy(TBranchProxyDirector *director, const char *name) : TBranchProxy(director,name) {};
          TClaArrayProxy(TBranchProxyDirector *director, const char *top, const char *name) :
             TBranchProxy(director,top,name) {};
          TClaArrayProxy(TBranchProxyDirector *director, const char *top, const char *name, const char *data) :
             TBranchProxy(director,top,name,data) {};
          TClaArrayProxy(TBranchProxyDirector *director, TBranchProxy *parent, const char *name) : TBranchProxy(director,parent, name) {};
          ~TClaArrayProxy() {};

          /* const */  array_t at(int i) {
             static T default_val;
             if (!Read()) return &default_val;
             if (fWhere==0) return &default_val;

             return (array_t)GetClaStart(i);
          }

          /* const */ array_t operator [](int i) { return at(i); }

       };

    #if !defined(_MSC_VER) || (_MSC_VER>1300)
       template <class T, const int d2 >
       class TClaArray2Proxy : public TBranchProxy {
       public:
          typedef T array_t[d2];

          void Print() {
             TBranchProxy::Print();
             cout << "fWhere " << fWhere << endl;
             if (fWhere) cout << "value? " << *(T*)GetStart() << endl;
          }

          TClaArray2Proxy() : TBranchProxy() {}
          TClaArray2Proxy(TBranchProxyDirector *director, const char *name)
             : TBranchProxy(director,name) {};
          TClaArray2Proxy(TBranchProxyDirector *director, const char *top,
                          const char *name)
             : TBranchProxy(director,top,name) {};
          TClaArray2Proxy(TBranchProxyDirector *director, const char *top,
                          const char *name, const char *data)
             : TBranchProxy(director,top,name,data) {};
          TClaArray2Proxy(TBranchProxyDirector *director, TBranchProxy *parent,
                          const char *name)
             : TBranchProxy(director,parent, name) {};
          ~TClaArray2Proxy() {};

          const array_t &at(int i) {
             // might need a second param or something !?

             static array_t default_val;
             if (!Read()) return &default_val;
             if (fWhere==0) return &default_val;

             T *temp = (T*)GetClaStart(i);
             if (temp) return *temp;
             else return default_val;

             // T *temp = *(T**)location;

             // return ((array_t**)temp)[i];

          }

          const array_t &operator [](int i) { return at(i); }

       };

       template <class T, int d2, int d3 >
       class TClaArray3Proxy : public TBranchProxy {
       public:
          typedef T array_t[d2][d3];

          void Print() {
             TBranchProxy::Print();
             cout << "fWhere " << fWhere << endl;
             if (fWhere) cout << "value? " << *(T*)GetStart() << endl;
          }

          TClaArray3Proxy() : TBranchProxy() {}
          TClaArray3Proxy(TBranchProxyDirector *director, const char *name) : TBranchProxy(director,name) {};
          TClaArray3Proxy(TBranchProxyDirector *director, const char *top, const char *name) :
             TBranchProxy(director,top,name) {};
          TClaArray3Proxy(TBranchProxyDirector *director, const char *top, const char *name, const char *data) :
             TBranchProxy(director,top,name,data) {};
          TClaArray3Proxy(TBranchProxyDirector *director, TBranchProxy *parent, const char *name) : TBranchProxy(director,parent, name) {};
          ~TClaArray3Proxy() {};

          const array_t* at(int i) {
             static array_t default_val;
             if (!Read()) return &default_val;
             if (fWhere==0) return &default_val;

             T *temp = (T*)GetClaStart(i);
             if (temp) return (array_t*)temp;
             else return default_val;

             // T *temp = *(T**)location;

             // return ((array_t**)temp)[i];

          }

          const array_t* operator [](int i) { return at(i); }

       };
    #endif

       //TImpProxy<TObject> d;

       typedef TImpProxy<Double_t>   TDoubleProxy;
       typedef TImpProxy<Double32_t> TDouble32Proxy;
       typedef TImpProxy<Float_t>    TFloatProxy;
       typedef TImpProxy<UInt_t>     TUIntProxy;
       typedef TImpProxy<ULong_t>    TULongProxy;
       typedef TImpProxy<ULong64_t>  TULong64Proxy;
       typedef TImpProxy<UShort_t>   TUShortProxy;
       typedef TImpProxy<UChar_t>    TUCharProxy;
       typedef TImpProxy<Int_t>      TIntProxy;
       typedef TImpProxy<Long_t>     TLongProxy;
       typedef TImpProxy<Long64_t>   TLong64Proxy;
       typedef TImpProxy<Short_t>    TShortProxy;
       typedef TImpProxy<Char_t>     TCharProxy;

       typedef TArrayProxy<Double_t>   TArrayDoubleProxy;
       typedef TArrayProxy<Double32_t> TArrayDouble32Proxy;
       typedef TArrayProxy<Float_t>    TArrayFloatProxy;
       typedef TArrayProxy<UInt_t>     TArrayUIntProxy;
       typedef TArrayProxy<ULong_t>    TArrayULongProxy;
       typedef TArrayProxy<ULong64_t>  TArrayULong64Proxy;
       typedef TArrayProxy<UShort_t>   TArrayUShortProxy;
       typedef TArrayProxy<UChar_t>    TArrayUCharProxy;
       typedef TArrayProxy<Int_t>      TArrayIntProxy;
       typedef TArrayProxy<Long_t>     TArrayLongProxy;
       typedef TArrayProxy<Long64_t>   TArrayLong64Proxy;
       typedef TArrayProxy<UShort_t>   TArrayShortProxy;
       //specialized ! typedef TArrayProxy<Char_t>  TArrayCharProxy;

       typedef TClaImpProxy<Double_t>   TClaDoubleProxy;
       typedef TClaImpProxy<Double32_t> TClaDouble32Proxy;
       typedef TClaImpProxy<Float_t>    TClaFloatProxy;
       typedef TClaImpProxy<UInt_t>     TClaUIntProxy;
       typedef TClaImpProxy<ULong_t>    TClaULongProxy;
       typedef TClaImpProxy<ULong64_t>  TClaULong64Proxy;
       typedef TClaImpProxy<UShort_t>   TClaUShortProxy;
       typedef TClaImpProxy<UChar_t>    TClaUCharProxy;
       typedef TClaImpProxy<Int_t>      TClaIntProxy;
       typedef TClaImpProxy<Long_t>     TClaLongProxy;
       typedef TClaImpProxy<Long64_t>   TClaLong64Proxy;
       typedef TClaImpProxy<Short_t>    TClaShortProxy;
       typedef TClaImpProxy<Char_t>     TClaCharProxy;

       typedef TClaArrayProxy<Double_t>    TClaArrayDoubleProxy;
       typedef TClaArrayProxy<Double32_t>  TClaArrayDouble32Proxy;
       typedef TClaArrayProxy<Float_t>     TClaArrayFloatProxy;
       typedef TClaArrayProxy<UInt_t>      TClaArrayUIntProxy;
       typedef TClaArrayProxy<ULong_t>     TClaArrayULongProxy;
       typedef TClaArrayProxy<ULong64_t>   TClaArrayULong64Proxy;
       typedef TClaArrayProxy<UShort_t>    TClaArrayUShortProxy;
       typedef TClaArrayProxy<UChar_t>     TClaArrayUCharProxy;
       typedef TClaArrayProxy<Int_t>       TClaArrayIntProxy;
       typedef TClaArrayProxy<Long_t>      TClaArrayLongProxy;
       typedef TClaArrayProxy<Long64_t>    TClaArrayLong64Proxy;
       typedef TClaArrayProxy<UShort_t>    TClaArrayShortProxy;
       //specialized ! typedef TClaArrayProxy<Char_t>  TClaArrayCharProxy;

    } // namespace ROOT

    #endif