////////////////////////////////////////////////////////////////////////
// $Id: AlgDigiPairList.cxx,v 1.28 2007/11/11 05:02:32 rhatcher Exp $
//
// AlgDigiPairList
//
// Begin_Html<img src="../../pedestrians.gif" align=center>
// <a href="../source_warning.html">Warning for beginners</a>.<br> 
//
// This is an Algorithm class to fill a CandDigiPairList with
// CandDigiPairHandles. The CandDigiPairHandles point to CandDigiPairs
// that are constructed by matching CandDigits that correspond to
// opposite ends of the same strip.
//
// Author:  P.S. Miyagawa 8/2000
//
// Also see <a href="../../root_crib/index.html">The ROOT Crib</a> and 
// <a href="../ALG_Classes.html"> Algorithm Classes</a> (part of
// <a href="../index.html">The MINOS Class User Guide</a>)End_Html
////////////////////////////////////////////////////////////////////////

#include <cassert>

#include "Algorithm/AlgConfig.h"
#include "Algorithm/AlgFactory.h"
#include "Algorithm/AlgHandle.h"
#include "Candidate/CandContext.h"
#include "CandDigit/CandDigitHandle.h"
#include "CandDigit/CandDigitListHandle.h"
#include "Conventions/Detector.h"
#include "MessageService/MsgService.h"
#include "Navigation/NavKey.h"
#include "Navigation/NavSet.h"
#include "Validity/VldContext.h"
#include "BubbleSpeak/AlgDigiPairList.h"
#include "BubbleSpeak/CandDigiPair.h"
#include "BubbleSpeak/CandDigiPairHandle.h"

ClassImp(AlgDigiPairList)

// Declare sorting function.
static NavKey BubKeyFromPSEId(const CandDigitHandle *cdh);
static NavKey BubKeyFromTime(const CandDigitHandle *cdh);

//......................................................................

CVSID("$Id: AlgDigiPairList.cxx,v 1.28 2007/11/11 05:02:32 rhatcher Exp $");

//......................................................................

AlgDigiPairList::AlgDigiPairList()
{
//
//  Purpose:    Default constructor.
//
//  Arguments:  n/a
//
//  Return:     n/a
//
}

//......................................................................

AlgDigiPairList::~AlgDigiPairList()
{
//
//  Purpose:    Default destructor.
//
//  Arguments:  n/a
//
//  Return:     n/a
//
}

//......................................................................

Bool_t AlgDigiPairList::IsCosmic(PlexPlaneId pid) const
{
//
//  Purpose:    Tests whether a plane is a cosmic counter in the Caldet.
//
//  Arguments:
//    pid       in     PlexPlaneId to test
//
//  Return:     kTRUE  if pid is a cosmic counter
//

// Check whether cosmic counter.
   if ((pid.GetPlaneView() == PlaneView::kA)
       || (pid.GetPlaneView() == PlaneView::kB))
      return kTRUE;
   else return kFALSE;
}

//......................................................................

Bool_t AlgDigiPairList::MatchedEnds(TObjArray &cdhAr) const
{
//
//  Purpose:    Tests whether a list of CandDigits has both ends of a
//              strip.
//
//  Arguments:
//    cdhAr     in    Array of CandDigits to be tested for match.
//
//  Return:     kTRUE  if both ends have digits.
//              kFALSE otherwise.
//

// Iterate over CandDigits.
   Bool_t pos = kFALSE, neg = kFALSE;
   TIter cdhItr(&cdhAr);
   while (CandDigitHandle *cdh =
             dynamic_cast<CandDigitHandle *>(cdhItr())) {

// Check which strip end.
      StripEnd::StripEnd_t end = cdh->GetPlexSEIdAltL().GetEnd();
      if (end == StripEnd::kNegative)      neg = kTRUE;
      else if (end == StripEnd::kPositive) pos = kTRUE;

// Check whether both ends found.
      if (neg && pos) return kTRUE;
   }
   return kFALSE;
}

//......................................................................

void AlgDigiPairList::RunAlg(AlgConfig &ac, CandHandle &ch,
                                            CandContext &cx)
{
//
//  Purpose:  Fills the CandDigiPairList with CandDigiPairs constructed
//            using the supplied CandDigitList.
//
//  Argument:
//    ac        in    AlgConfig (not used).
//    ch        in    Handle to the CandDigiPairList to fill.
//    cx        in    CandContext containing a demuxed CandDigitList.
//
//  Return:   n/a
//

   MSG("BubAlg", Msg::kVerbose)
      << "Starting AlgDigiPairList::RunAlg()" << endl;

   Bool_t needmatch = ac.GetInt("MatchEnds");
   Double_t maxtimesep = ac.GetDouble("TimeSepMax");

// Check for CandDigitListHandle input.
   assert(cx.GetDataIn());
   if (cx.GetDataIn()->InheritsFrom("CandDigitListHandle")) {

// Recover CandDigitListHandle.
      MSG("BubAlg", Msg::kVerbose)
         << "Recover CandDigitListHandle." << endl;
      const CandDigitListHandle *cdlh =
               dynamic_cast<const CandDigitListHandle*>(cx.GetDataIn());

// Get detector type.
      const VldContext *vld = cdlh->GetVldContext();
      assert(vld);
      Detector::Detector_t dettype = vld->GetDetector();

// Sort by PlaneStripEndId.
      MSG("BubAlg", Msg::kVerbose) << "Create iterator." << endl;
      CandDigitHandleItr cdhItr(cdlh->GetDaughterIterator());

      MSG("BubAlg", Msg::kVerbose)
         << "Sort CandDigitList by StripEndId." << endl;
      CandDigitHandleKeyFunc *cdhKf = cdhItr.CreateKeyFunc();
      cdhKf->SetFun(BubKeyFromPSEId);
      cdhItr.GetSet()->AdoptSortKeyFunc(cdhKf);
      cdhKf = 0;

// General setup.
      // Get singleton instance of AlgFactory.
      MSG("BubAlg", Msg::kVerbose)
         << "AlgFactory &af = AlgFactory::GetInstance();" << endl;
      AlgFactory &af = AlgFactory::GetInstance();

      // Get an AlgHandle to AlgDigiPair with "default" AlgConfig.
      MSG("BubAlg", Msg:: kVerbose) <<
        "AlgHandle ah = af.GetAlgHandle(\"AlgDigiPair\", \"default\");"
                                                               << endl;
      AlgHandle ah = af.GetAlgHandle("AlgDigiPair", "default");

      MSG("BubAlg", Msg::kVerbose)
         << "Create CandContext instance." << endl;
      CandContext cxx(this, cx.GetMom());
      cxx.SetCandRecord(cx.GetCandRecord());

// Iterate over CandDigitList daughters and create CandDigiPairs.
      CandDigitHandle *prev = 0;
      TObjArray cdhAr;
      while(CandDigitHandle *curr = cdhItr()) {

         // Check whether a special CalDet digit (empty demux list).
         if (curr->GetPlexSEIdAltL().GetSize() == 0) continue;

         // Check for demux veto flag.
         if (curr->GetPlexSEIdAltL().GetDemuxVetoFlag()) continue;

         // Check for best weight of zero in Far Detector.
         if ((curr->GetPlexSEIdAltL().GetBestWeight() <= 0)
             && (dettype == Detector::kFar))
            continue;

         if (prev) {
            // Check whether same strip.
            PlexStripEndId pSEId =prev->GetPlexSEIdAltL().GetBestSEId();
            PlexStripEndId cSEId =curr->GetPlexSEIdAltL().GetBestSEId();
            if (pSEId.IsSameStrip(cSEId)) cdhAr.Add(curr);
            else {   // New strip.
               // Check whether need to match strip ends or have match
               // or is cosmic counter in Caldet
               if (!needmatch || MatchedEnds(cdhAr) || IsCosmic(pSEId)){
                  // Check number of digits.
                  if (cdhAr.GetEntriesFast() == 1) {
                     // Create CandDigiPair from previous list.
                     cxx.SetDataIn(&cdhAr);
                     CandDigiPairHandle chh =
                                  CandDigiPair::MakeCandidate(ah, cxx);
                     ch.AddDaughterLink(chh);
                  }
                  else {
                     // Sort digits by time.
                     CandDigitHandleItr tmpItr(cdhAr.MakeIterator());
                     cdhKf = tmpItr.CreateKeyFunc();
                     cdhKf->SetFun(BubKeyFromTime);
                     tmpItr.GetSet()->AdoptSortKeyFunc(cdhKf);
                     cdhKf = 0;

                     // Check for time separation.
                     TObjArray tmpAr;
                     CandDigitHandle *cdh = tmpItr();
                     tmpAr.Add(cdh);
                     Double_t lasttime = cdh->GetTime();
                     while ((cdh = tmpItr())) {
                        if ((cdh->GetTime() - lasttime) > maxtimesep) {
                           // Create strip, then start new one.
                           cxx.SetDataIn(&tmpAr);
                           CandDigiPairHandle chh =
                                  CandDigiPair::MakeCandidate(ah, cxx);
                           ch.AddDaughterLink(chh);
                           tmpAr.Clear();
                        }
                        lasttime = cdh->GetTime();
                        tmpAr.Add(cdh);
                     }

                     // Create strip from last digit added.
                     cxx.SetDataIn(&tmpAr);
                     CandDigiPairHandle chh =
                                  CandDigiPair::MakeCandidate(ah, cxx);
                     ch.AddDaughterLink(chh);

                     // Clean up
                     tmpAr.Clear();
                  }
               }

               // Prepare for next strip.
               cdhAr.Clear();
               cdhAr.Add(curr);
            }
         }
         else cdhAr.Add(curr);
         prev = curr;
      }
      if (prev) {
         // Create CandDigiPair from last digit added.
         PlexStripEndId pSEId =prev->GetPlexSEIdAltL().GetBestSEId();
         if (!needmatch || MatchedEnds(cdhAr) || IsCosmic(pSEId)) {
            cxx.SetDataIn(&cdhAr);
            CandDigiPairHandle chh =
                                   CandDigiPair::MakeCandidate(ah, cxx);
            ch.AddDaughterLink(chh);
         }
         // Clean up.
         cdhAr.Clear();
         prev = 0;
      }
   }
}

//......................................................................

void AlgDigiPairList::Trace(const char *c) const
{
//
//  Purpose:  Trace the AlgDigiPairList.
//
//  Arguments:
//    c          in    String tag for the trace.
//
//  Return:   n/a
//

  MSG("BubCand", Msg::kDebug)
    << "**********Begin AlgDigiPairList::Trace(\"" << c << "\")" << endl
    << "**********End AlgDigiPairList::Trace(\"" << c << "\")" << endl;
}

//......................................................................

NavKey BubKeyFromPSEId(const CandDigitHandle *cdh)
{
//
//  Purpose:  Generate a sort key based on the encoded strip end ID of
//            a CandDigit.
//
//  Arguments:
//    cdh        in    Handle to CandDigit to be sorted.
//
//  Return:   Encoded strip end ID.
//

   return cdh->GetPlexSEIdAltL().GetBestSEId().GetEncoded();
}

//......................................................................

NavKey BubKeyFromTime(const CandDigitHandle *cdh)
{
//
//  Purpose:  Generate a sort key based on the time of a CandDigit.
//
//  Arguments:
//    cdh        in    Handle to CandDigit to be sorted.
//
//  Return:   Time of CandDigit.
//

   return cdh->GetTime();
}
