////////////////////////////////////////////////////////////////////////
// $Id: CandHandle.cxx,v 1.38 2006/07/24 17:57:59 boehm Exp $
//
// CandHandle.cxx
//
// CandHandle is the base class for specialized access handles to
// CandBase derived objects.
//
// Each concrete CandHandle must define a DupHandle function.
//
// Adapted from Babar's BtaCandidate (written by Gautier Hamel de
// Monchenault and Bob Jacobsen).
//
// Author:  G. Irwin 2/2000
////////////////////////////////////////////////////////////////////////

#include <cassert>
#include <iostream>

#include "TString.h"

#include "Candidate/CandHandle.h"
#include "CandData/CandHeader.h"
#include "CandData/CandRecord.h"
#include "Candidate/CandUid.h"
#include "MessageService/MsgService.h"

ClassImp(CandHandle)

// Define static members
Int_t CandHandle::fsNAlloc = 0;          // Initializes fsNAlloc to zero
Bool_t CandHandle::fsSlushyEnabled = kFALSE;// kTRUE=delayed cand freeze  

//______________________________________________________________________
CVSID("$Id: CandHandle.cxx,v 1.38 2006/07/24 17:57:59 boehm Exp $");

std::ostream& operator<<(std::ostream& os, const CandHandle& ch)
{ return ch.FormatToOStream(os); }

//______________________________________________________________________
  CandHandle::CandHandle() :
  fCandRefer(0)
, fMother(0)
, fLocked(kFALSE)
, fViewableNotifier(0)
{
   fsNAlloc++;
}

//______________________________________________________________________
CandHandle::CandHandle(const CandHandle &ch) :
  TObject(ch)
, fCandRefer(ch.fCandRefer)
, fMother(0)
, fLocked(kFALSE)
, fViewableNotifier(0)
{
   fsNAlloc++;
}

//______________________________________________________________________
CandHandle::CandHandle(CandBase *cb) :
  fCandRefer(cb)
, fMother(0)
, fLocked(kFALSE)
, fViewableNotifier(0)
{
   fsNAlloc++;
}

//______________________________________________________________________
CandHandle::~CandHandle()
{
   DropMotherLink();
   fsNAlloc--;
}

//______________________________________________________________________
CandHandle &CandHandle::operator=(const CandHandle &ch)
{
  if (IsLocked()) {
    MSG("Cand", Msg::kWarning) << endl
       << "Attempt to reset Candidate reference in a locked CandHandle:"
        << endl << "Class = " << ClassName() << "  Name = " << GetName()
                << endl << "This CandHandle is owned by the I/O system."
                   << endl << "Please create your own CandHandle first."
         << endl << "Then reset the Candidate reference in the new one."
                                      << endl << "This attempt ignored."
                                                        << endl << endl;
    return *this;
  }

  if (this != &ch) {

// Assign CandRefer
    if (fCandRefer != ch.GetCandRefer()) fCandRefer=ch.GetCandRefer();

//gmi The following statement from original Babar code looks like a bug.
//gmi Why should the mother link get zeroed if the CandBase is changed?
//gmi fMother = (CandHandle *) 0;              // Mother link not copied
  }
  return *this;
}

//______________________________________________________________________
Bool_t CandHandle::operator==(const CandHandle &ch) const
{

// CandHandle object == operator compares Candidate addresses.
// Pointer == just compares CandHandle addresses.
  return (GetCandBase() == ch.GetCandBase());
}

//______________________________________________________________________
Bool_t CandHandle::operator!=(const CandHandle &ch) const
{

// CandHandle object != operator compares Candidate addresses.
// Pointer != just compares CandHandle addresses
  return !(*this == ch);
}

//______________________________________________________________________
const CandHandle *CandHandle::AddDaughterLink(CandHandle &ch,
                                                           Bool_t check)
{

// Prevent duplication of pre-existing redundant daughter if check TRUE.
  if (check && FindDaughter(&ch)) {
    MSG("Cand", Msg::kVerbose)
     << "Attempt to add redundant CandHandle to daughter list: ignored."
     << endl;
    return 0;
  }
  return GetOwnedCandBase()->AddDaughterLink(ch);   // Added CandHandle*
}

//______________________________________________________________________
const CandHandle *CandHandle::CloneInTree(const CandHandle &ch) const
{
   if (IsCloneOf(ch)) return this;
   TIter iterdau = GetDaughterIterator();
   CandHandle *dau;
   while ((dau=(CandHandle *) iterdau())) {
     const CandHandle *chh = dau->CloneInTree(ch);
     if (chh != 0) return chh;
   }
   return 0;
}

//______________________________________________________________________
void CandHandle::DropMotherLink()
{
   if (fMother == 0) return;                        // No mother to drop

// Remove mother's daughter link to this
   if (!IsLocal()) fMother->GetCandBase()->fDaughters.Remove(this);

   fMother = (CandHandle *) 0;           // Set this mother link to zero
}

//______________________________________________________________________
CandHandle *CandHandle::DupHandle() const
{
   return (new CandHandle(*this));
}

//______________________________________________________________________
CandHandle *CandHandle::FindDaughter(const CandHandle *ch) const
{

// Finds the first daughter encountered with the same Candidate
// object referenced, not the same CandHandle object.

   TIter chli(&GetCandBase()->fDaughters);
   CandHandle *chi;

   while ((chi = dynamic_cast<CandHandle *>(chli()))) {

// CandHandle object == operator is defined to compare Candidates
     if (*chi == *ch) return chi;

   }
   return 0;
}

//______________________________________________________________________
const CandHandle *CandHandle::FindDaughter(const char *classname,
                                           const char *objname) const
{

// Returns const ptr to first selected CandHandle in daughter list.
// If classname is a null ptr or blank string, no class selection is
// done.  If classname is provided, the qualifying object must
// InheritFrom() or be a classname.  The objname is optional, but,
// if filled, will further qualify the selection by requiring
// agreement with the GetName() method of the selected object.

   TString classtr("");
   if (classname)
     classtr.Append(TString(classname).Strip(TString::kBoth));
   TString objstr("");
   if (objname)
     objstr.Append(TString(objname).Strip(TString::kBoth));

   TIter chli(&GetCandBase()->fDaughters);
   CandHandle *ch;

   while ((ch = dynamic_cast<CandHandle *>(chli()))) {

// First check whether ch InheritsFrom "classname"
     if (classtr.IsNull() || ch->InheritsFrom(classtr)) {

// Then check whether ch name equals "objname"
       if (objstr.IsNull() || (objstr == ch->GetName()))
         return ch;
     }
   }
   return 0;
}

//______________________________________________________________________
const AlgConfig *CandHandle::GetAlgConfig() const
{
   return GetCandBase()->GetAlgConfig();
}

//______________________________________________________________________
UInt_t CandHandle::GetArchUidInt() const
{
   return GetCandBase()->GetArchUidInt();
}

//______________________________________________________________________
CandBase *CandHandle::GetCandBase()
{
   return fCandRefer();
}

//______________________________________________________________________
const CandBase *CandHandle::GetCandBase() const
{
   return fCandRefer();
}

//______________________________________________________________________
CandRecord *CandHandle::GetCandRecord() const
{
   return GetCandBase()->fCandRecord;
}

//______________________________________________________________________
const CandUid &CandHandle::GetCandUid() const
{
   return GetCandBase()->GetCandUid();
}

//______________________________________________________________________
const CandHandle *CandHandle::GetDaughter(Int_t ndau) const
{
   return GetCandBase()->GetDaughter(ndau);
}

//______________________________________________________________________
TIter CandHandle::GetDaughterIterator() const
{
   return GetCandBase()->GetDaughterIterator();
}

//______________________________________________________________________
const CandHandle *CandHandle::GetLocalHandle() const
{
   return GetCandBase()->GetLocalHandle();
}

//______________________________________________________________________
const CandHandle *CandHandle::GetMother() const
{
   if ((fMother == 0) && (GetLocalHandle() != 0) && !IsLocal()) {
     return GetLocalHandle()->GetMother();
   }
   return fMother;
}

//______________________________________________________________________
const char *CandHandle::GetName() const
{
   return GetCandBase()->GetName();
}

//______________________________________________________________________
Int_t CandHandle::GetNDaughters() const
{
   return GetCandBase()->GetNDaughters();
}

//______________________________________________________________________
Int_t CandHandle::GetNRefers() const
{
   return GetCandRefer().GetNRefers();
}

//______________________________________________________________________
CandBase *CandHandle::GetOwnedCandBase()
{
   if (IsLocked()) {
     MSG("Cand", Msg::kSynopsis) << endl
         << "Attempt to modify a Candidate through a locked CandHandle:"
        << endl << "Class = " << ClassName() << "  Name = " << GetName()
                << endl << "This CandHandle is owned by the I/O system."
             << endl << "Please create your own CandHandle first using:"
                                 << endl << "(" << ClassName() << " *) "
                      << "mycandhandle = lockedcandhandle->DupHandle();"
      << endl << "Then modify the Candidate object through the new one."
                       << endl << "Your modification will be made to a "
           << "clone of the original Candidate object." << endl << endl;
   }

// Experimental:  fsSlushyEnabled = kTRUE clones only Candidates read-in
   else if (CandHandle::IsSlushyEnabled()) return fCandRefer();

   assert(this != GetLocalHandle());           // Cannot own LocalHandle
   CandBase *cb = GetCandRefer().OwnRef();  // Exclusive ref to CandBase

// Set CandRecord to that of mother, if any, and if not already set.
   if (GetMother() &&    // This CandHandle is a daughter (has a mother)
      (GetMother()->GetCandRecord() != cb->fCandRecord))
     cb->SetCandRecord(GetMother()->GetCandRecord());

   return cb;                               // Exclusive ref to CandBase
}

//______________________________________________________________________
const char *CandHandle::GetTitle() const
{
   return GetCandBase()->GetTitle();
}

//______________________________________________________________________
UInt_t CandHandle::GetUidInt() const
{
   return GetCandBase()->GetUidInt();
}

//______________________________________________________________________
const VldContext *CandHandle::GetVldContext() const
{
   CandRecord *cr = GetCandBase()->fCandRecord;
   if (!cr) return 0;
   const CandHeader *ch = cr->GetCandHeader();
   return ch ? &ch->GetVldContext() : 0;
}

//______________________________________________________________________
Bool_t CandHandle::HasOverlapWith(const CandHandle &ch) const
{
   return GetCandBase()->HasOverlapWith(*(ch.GetCandBase()));
}

//______________________________________________________________________
Bool_t CandHandle::IsCloneOf(const CandHandle &ch) const
{
   return GetCandUid().IsCloneOf(ch.GetCandUid());
}

//______________________________________________________________________
Bool_t CandHandle::IsComposite() const
{
   return GetCandBase()->IsComposite();
}

//______________________________________________________________________
Bool_t CandHandle::IsEqual(const TObject *rhs) const
{

// Overloads TObject::IsEqual method.
   if (TObject::IsEqual(rhs)) return true;     // Tests for same address

// CandHandles refer to same CandBase address.
   const CandHandle *rch = dynamic_cast<const CandHandle*>(rhs);
   if (rch == 0) return false;
   if (this->GetCandBase() == rch->GetCandBase()) return true;

   return false;
}

//______________________________________________________________________
Bool_t CandHandle::IsEquivalent(const TObject *rhs) const
{

// IsEquivalent does deep comparison for templated Test... methods

// Check for equivalent CandBases.
   const CandHandle *rch = dynamic_cast<const CandHandle*>(rhs);
   if (rch == NULL) {
    MSG("VCand", Msg::kDebug)
      << "CandHandle::IsEquivalent(): Comparison object not CandHandle."
                 << "  rhs->ClassName() = " << rhs->ClassName() << endl;
    return false;
  }
   if (this->IsEqual(rhs)) return true;      // Same address or CandBase
   if (this->GetCandBase()->IsEquivalent(rch->GetCandBase())) {
     return true;
   }

   return false;
}

//______________________________________________________________________
Bool_t CandHandle::IsLocal() const
{
   return (this == GetLocalHandle());
}

//______________________________________________________________________
Bool_t CandHandle::RemoveDaughter(CandHandle *ch)
{

// Removes the first daughter encountered with the same Candidate
// object referenced, not the same CandHandle object.
   CandHandle *chexact = FindDaughter(ch);
   if (chexact) return GetOwnedCandBase()->RemoveDaughter(chexact);
   else return kFALSE;
}

//______________________________________________________________________
void CandHandle::SetCandRecord(CandRecord *cr)
{
  if (cr) {                          // Don't reset a CandRecord to zero
    if (cr != GetCandRecord()) {                      // Change of value
      if (GetCandRecord()) GetOwnedCandBase()->SetCandRecord(cr);//Reset
      else GetCandBase()->SetCandRecord(cr);   // Init non-zero for free
    }
  }
}

//______________________________________________________________________
void CandHandle::SetSlushyEnabled(Bool_t tf)
{
   if (tf == CandHandle::fsSlushyEnabled) {
     MSG("Cand", Msg::kSynopsis)
       << "CandHandle::fsSlushyEnabled unchanged from "
       << CandHandle::fsSlushyEnabled << endl
       << "by call to CandHandle::SetSlushyEnabled(" << tf << ")."
       << endl;
   }
   else {
     MSG("Cand", Msg::kSynopsis)
       << "CandHandle::fsSlushyEnabled changed from "
       << CandHandle::fsSlushyEnabled << " to " << tf
       << endl << "by call to CandHandle::SetSlushyEnabled(Bool_t)."
       << endl;
     CandHandle::fsSlushyEnabled = tf;
   }
}

//______________________________________________________________________
void CandHandle::SetLock()
{
   fLocked = kTRUE;
}

//______________________________________________________________________
void CandHandle::SetMotherLink(CandHandle *ch)
{
   assert(ch != 0);                        // Can't set null mother link
   assert(fMother == 0);                  // Can't already have a mother
   assert(!IsLocal());               // Can't set mother for LocalHandle
   fMother = ch;                                  // Set the mother link
   fMother->GetCandBase()->fDaughters.Add(this);  // Add to ma's dghters
}

//______________________________________________________________________
void CandHandle::SetName(const char *name)
{
   GetOwnedCandBase()->SetName(name);
}

//______________________________________________________________________
void CandHandle::SetTitle(const char *title)
{
   GetOwnedCandBase()->SetTitle(title);
}

//______________________________________________________________________
void CandHandle::Trace(const char *c) const
{
  MSG("Cand", Msg::kDebug)
         << "**********Begin CandHandle::Trace(\"" << c << "\")" << endl
                  << "Information from CandHandle's CandBase: " << endl;
  GetCandBase()->Trace(c);

  if (GetMother()) {
    MSG("Cand", Msg::kDebug) <<
      "This CandHandle is in Daughter list of CandBase whose Trace is: "
                                                                << endl;
    GetMother()->GetCandBase()->Trace("Mother's CandBase");
  }

  else {
    MSG("Cand", Msg::kDebug)
            << "Traces of CandHandle's CandBase Daughterlist: " << endl;
  }

  if (GetNDaughters()) {
    MSG("Cand", Msg::kDebug)
            << "Traces of CandHandle's CandBase Daughterlist: " << endl;
    TIter iterdau = GetDaughterIterator();
    CandHandle *dau;
    while ((dau = (CandHandle *) iterdau())) {
      dau->Trace("Daughter CandHandles");
    }
  }
  MSG("Cand", Msg::kDebug)
          << "**********End CandHandle::Trace(\"" << c << "\")" << endl;
}

//______________________________________________________________________
void CandHandle::Print(Option_t *option) const
{
  FormatToOStream(cout,option);
}
//______________________________________________________________________
std::ostream& CandHandle::FormatToOStream(std::ostream& os,
                                          Option_t *option) const
{
  // Format the Candidate (and Daughters) to a ostream
  // if option string contains:
  //    n    = include name
  //    t    = include title
  //    i    = include Uid's
  //    v[n] = verbosity level, v0 to suppresss data values 
  //    d[n] = include daughters to depth of [n] (infinite if no n)
  //           d0 means don't print daughters

  GetCandBase()->FormatToOStream(os,option);
  return GetCandBase()->FormatDaughtersToOStream(os,option);
}
//______________________________________________________________________
