////////////////////////////////////////////////////////////////////////
// $Id: CandBase.cxx,v 1.30 2005/08/26 23:30:13 gmieg Exp $
//
// CandBase.cxx
//
// CandBase is the base class for Reconstruction Candidates.
//
// Each concrete CandBase must define a Dup function.
// CandBase must grant friendship to class CandRefer.
//
// Adapted from Babar's BtaCandBase (written by Gautier Hamel de
// Monchenault and Bob Jacobsen).
//
// Author:  G. Irwin 1/2000
////////////////////////////////////////////////////////////////////////

#include <cassert>

#include "Algorithm/AlgHandle.h"                  // Needed for Root I/O
#include "Candidate/CandBase.h"
#include "Candidate/CandHandle.h"
#include "Candidate/CH.h"
#include "Candidate/CandUid.h"
#include "MessageService/MsgService.h"
#include "TRegexp.h"

ClassImp(CandBase)

template class CH<CandBase>;

// Define static members
Int_t CandBase::fsNAlloc = 0;            // Initializes fsNAlloc to zero

Int_t   CandBase::fgIndentLevel = 0;          // Initialize indent level
TString CandBase::fgIndentString = "";       // Initialize indent string
TString CandBase::fgDataIndentString = "| "; 

//______________________________________________________________________
CVSID("$Id: CandBase.cxx,v 1.30 2005/08/26 23:30:13 gmieg Exp $");

#include "Candidate/CandBase.tpl"

//______________________________________________________________________
CandBase::CandBase() :
  fAlgConfigRef(0)
, fCandRecord(0)
, fLocalHandle(0)
, fCandUid(0)
{
  fsNAlloc++;
  fCandUid = new CandUid(*this);
}

//______________________________________________________________________
CandBase::CandBase(const AlgHandle &ah) :
  fAlgConfigRef(ah.GetAlgConfigRef())
, fCandRecord(0)
, fLocalHandle(0)
, fCandUid(0)
{
  fsNAlloc++;
  fCandUid = new CandUid(*this);
}

//______________________________________________________________________
CandBase::CandBase(const CandBase &cb) :

// NO, DON'T DO THIS!!! (gmieg 021025) TNamed(cb), CandRefCounted(cb)
  TNamed()
, CandRefCounted()
, fAlgConfigRef(cb.fAlgConfigRef)
, fCandRecord(cb.fCandRecord)
, fLocalHandle(0)
, fCandUid(0)
{

// Base copy ctor dups owned pointers, but defers copying Daughter List.
// Daughter List copy is made in the derived class Dup() function.
// This is because base class copy constructor hasn't yet created
// fLocalHandle with a CandHandle* of the full derived type.
  fsNAlloc++;
  assert (&cb != 0);
  fCandUid = new CandUid(*this, cb);
}

//______________________________________________________________________
CandBase &CandBase::operator=(const CandBase &)    // Should not be used
{
  abort();                             // This method should not be used
  return *this;
}

//______________________________________________________________________
CandBase::~CandBase()
{
  fDaughters.Delete();           // Delete all CandHandles in fDaughters
  delete fLocalHandle;  fLocalHandle = 0;
  delete fCandUid;      fCandUid = 0;
  fsNAlloc--;
}

//______________________________________________________________________
const CandHandle *CandBase::AddDaughterLink(const CandHandle &ch)
{

// Assume pre-existing daughter check is already done.
  assert(&ch != 0);
  CandHandle *chnew = ch.DupHandle();
  chnew->SetMotherLink(fLocalHandle);  // Set the daughter's mother link
  chnew->SetCandRecord(fCandRecord);
  return chnew;
}

//______________________________________________________________________
AlgConfig *CandBase::GetAlgConfig() const
{
   return (AlgConfig *) (fAlgConfigRef.GetObject());
}

//______________________________________________________________________
UInt_t CandBase::GetArchUidInt() const
{
   return GetCandUid().GetArchUidInt();
}

//______________________________________________________________________
const CandHandle *CandBase::GetDaughter(Int_t ndau) const
{
  if ((ndau > -1) && (ndau < (fDaughters.GetLast()+1)))
    return (CandHandle *) fDaughters.At(ndau);
  else
    return 0;
}

//______________________________________________________________________
TIter CandBase::GetDaughterIterator() const
{
  return TIter(&fDaughters);                   // Daughter list Iterator
}

//______________________________________________________________________
Int_t CandBase::GetNDaughters() const
{
  return fDaughters.GetEntries();    // Counts occupied cells, not holes
}

//______________________________________________________________________
UInt_t CandBase::GetUidInt() const
{
   return GetCandUid().GetUidInt();
}

//______________________________________________________________________
Bool_t CandBase::HasOverlapWith(const CandBase & /* candbase */) const
{
  return kFALSE;                                // Needs to be filled in
}

//______________________________________________________________________
Bool_t CandBase::IsComposite() const
{
  return (GetNDaughters() > 1);
}

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

// IsEquivalent does deep comparison for templated Test... methods
  TestDisplayCandBanner("CandBase");
  if (this->IsEqual(rhs)) return true;    // CandBases have same address
  Bool_t result = true;
  const CandBase* rCnd = dynamic_cast<const CandBase*>(rhs);
  if (rCnd == NULL) {
    MSG("VCand", Msg::kDebug)
        << "CandBase::IsEquivalent(): Comparison object not a CandBase."
                 << "  rhs->ClassName() = " << rhs->ClassName() << endl;
    return false;
  }
  
  result = TestEquality("fAlgConfigRef", this->fAlgConfigRef,
                                         rCnd->fAlgConfigRef) && result;

// Compare fDaughters
  Bool_t all_daughters_ok = true;
  TIter thisIter = this->GetDaughterIterator();
  TIter rhsIter  = rCnd->GetDaughterIterator();

  TObject *thisObj;
  TObject *rhsObj;

  while ((thisObj = thisIter()) && (rhsObj  = rhsIter())) {
    CandHandle *thisCdh = dynamic_cast<CandHandle *>(thisObj);
    CandHandle *rhsCdh  = dynamic_cast<CandHandle *>(rhsObj);
    if (thisCdh == 0 || rhsCdh == 0) {
      MSG("VCand", Msg::kDebug)
           << "CandBase::IsEquivalent(): Bad daughter CandHandle found."
                  << "  thisObj->ClassName() = " << thisObj->ClassName()
                  << ".  rhsObj->ClassName() = " <<  rhsObj->ClassName()
                                                                << endl;
      all_daughters_ok = false;
      continue;
    }

    if (thisCdh->GetMother() != this->fLocalHandle) {
      MSG("VCand", Msg::kError) << endl
                 << "Bad CandHandle found in LHS Daughter List." << endl
       << "CandHandle Mother pointer is not to LocalHandle of CandBase."
                                      << endl << "Mother ClassName() = "
                            << thisCdh->GetMother()->ClassName() << endl
             << "Mother GetName() = " << thisCdh->GetMother()->GetName()
                                 << endl << "LocalHandle ClassName() = "
                                      << this->fLocalHandle->ClassName()
                                   << endl << "LocalHandle GetName() = "
                                        << this->fLocalHandle->GetName()
                           << endl << "Needs to be understood!" << endl;
      all_daughters_ok = false;
    }

    if (*thisCdh != *rhsCdh) {    // Compares Candidate Object addresses
      MSG("VCand", Msg::kDebug) << "\tDaughter     [not ok]" << endl;
      all_daughters_ok = false;
    }
  }

  if (!all_daughters_ok) {
    MSG("VCand", Msg::kSynopsis) << "Daughters \t[not all ok]" << endl;
    return false;
  }

  MSG("VCand", Msg::kDebug) << "All daughters \t[ok]" << endl;

  return result;
}

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

// Removes the first daughter encountered with the same CandHandle ref'd
// object.  CandHandle caller is expected to have found the exact ch.
  if ((ch == 0)) {
    MSG("Cand", Msg::kError) << endl
       << "Failed attempt to remove null CandHandle from Daughter List."
    << endl << "LocalHandle ClassName() = " << fLocalHandle->ClassName()
        << endl << "LocalHandle GetName() = " << fLocalHandle->GetName()
                          << endl << "CandHandle not deleted."  << endl;
    return kFALSE;                                     // Failure return
  }
  if (CandHandle* newh =      // Guarantee Mother link pointer if cloned
                 dynamic_cast<CandHandle*>(fDaughters.FindObject(ch))) {
    if (newh->GetMother() != fLocalHandle) {
      MSG("Cand", Msg::kError) << endl
       << "Request to Remove bad CandHandle from Daughter List." << endl
       << "CandHandle mother pointer is not to LocalHandle of CandBase."
                                                                 << endl
            << "Mother ClassName() = " << newh->GetMother()->ClassName()
        << endl << "Mother GetName() = " << newh->GetMother()->GetName()
                                                                 << endl
            << "LocalHandle ClassName() = " << fLocalHandle->ClassName()
        << endl << "LocalHandle GetName() = " << fLocalHandle->GetName()
        << endl << "CandHandle Removed anyway.  Should be investigated!"
                                                                << endl;
    }
    if (fDaughters.Remove(newh)) {  // Remove CandHandle from fDaughters
      delete newh;                        // Delete CandHandle ch object
      fDaughters.Compress();       // Squeeze out holes in Daughter list
      return kTRUE;                                    // Success return
    }
  }
  else {
    MSG("Cand", Msg::kError) << endl
           << "Failed to find and remove CandHandle from Daughter List."
                         << endl << "  CandHandle not deleted." << endl;
    return kFALSE;                                     // Failure return 
  }
  return kFALSE;                                          // Did nothing
}

//______________________________________________________________________
void CandBase::SetCandRecord(CandRecord *cr)
{
  if (cr == fCandRecord) return;   // New fCandRecord is same as old one

  fCandRecord = cr;
  TIter iterdau = GetDaughterIterator();
  CandHandle *dau;
  while ((dau = dynamic_cast<CandHandle *>(iterdau()))) {
    dau->SetCandRecord(cr);
  }
}

//______________________________________________________________________
void CandBase::SetLocalHandle(CandHandle *ch)
{
  assert (fLocalHandle == 0);
  fLocalHandle = ch;             // Handle supplied by derived Candidate
  assert (fLocalHandle != 0);
}

//______________________________________________________________________
void CandBase::Trace(const char *c) const
{
  MSG("Cand", Msg::kDebug)
           << "**********Begin CandBase::Trace(\"" << c << "\")" << endl
      << CandBase::GetNAlloc() << " CandBase objects allocated." << endl
                      << "UidInt = " << GetUidInt() << ", ArchUidInt = "
                                              << GetArchUidInt() << endl
                   << "No. of ref-counted CandHandles = " << GetNLinks()
                                 << " (LocalHandle not counted)" << endl
                   << "NDaughters = " << GetNDaughters() << endl << endl
            << "**********End CandBase::Trace(\"" << c << "\")" << endl;
}

//______________________________________________________________________
std::ostream& CandBase::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

  TString opt(option);
  bool doName  = opt.Contains("n");
  bool doTitle = opt.Contains("t");
  bool doUid   = opt.Contains("i");

  const TString& indent = GetIndentString();

  os << indent
     << this->ClassName() << " ";
  if (doName) {
    const TString name(this->GetName());
    if (name  != "") os << " Name: '" << this->GetName() << "'";
  }
  os << " Ndaughters " << this->GetNDaughters()
     << " NLinks " << this->GetNLinks(); // nlinks = nrefers + 1
  if (doTitle ) {
    const TString title(this->GetTitle());
    if (title != "") os << endl << indent 
                        << "   Title: '" << title << "'";
  }
  os << endl;
  if (doUid) {
    os << indent 
       << "   Uid " << this->GetUidInt() 
       << " ArchUid (ancestor) " << this->GetArchUidInt()
       << endl;
  }
  
  return os;
}

//______________________________________________________________________
std::ostream& CandBase::FormatDaughtersToOStream(std::ostream& os,
                                                 Option_t *option) const
{
  TString opt(option);
  bool nodaughters = opt.Contains("d0") || (GetNDaughters() == 0);
  if (!nodaughters) {
    TString optsub(opt);

// here's where d[n] should be modified to d[n-1]
// using wildcard would assume ^$
    static const TRegexp regexp("d*[0-9]",false);
    TSubString dn = opt(regexp);    // just finds the start of d[0-9]!!!
    int nlevels = -1;
    sscanf(dn.Data(),"d%d",&nlevels);
    if (nlevels>0) {
      TString oldn, newn;
      oldn += nlevels; 
      newn += (nlevels-1);
      optsub.ReplaceAll(oldn,newn);
    }

    const TString& indent = GetIndentString();
    os << indent << "Daughter List:" << endl;
    IncIndent();
    TIter itr = GetDaughterIterator();
    const TObject *obj;
    while ( ( obj = itr() ) ) {
      const CandHandle *cand = dynamic_cast<const CandHandle*>(obj);
      if (cand) cand->FormatToOStream(os,optsub);
    }
    DecIndent();
  }
  return os;
}

//______________________________________________________________________

Int_t CandBase::ResizeIndent()
{ 
  fgIndentString.Resize(3*fgIndentLevel);
  return fgIndentLevel;
}

//______________________________________________________________________
