////////////////////////////////////////////////////////////////////////
// $Id: AlgThruMuonList.cxx,v 1.19 2002/08/24 23:45:53 miyagawa Exp $
//
// AlgThruMuonList
//
// Begin_Html<img src="../../pedestrians.gif" align=center>
// <a href="../source_warning.html">Warning for beginners</a>.<br> 
//
// This is an Algorithm class to create a list of CandThruMuons from a
// CandMSTClusterList.  It matches clusters whose ends are close to each
// other and whose strips have opposite orientation.
//
// Author:  P.S. Miyagawa 9/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 "MessageService/MsgService.h"
#include "BubbleSpeak/AlgThruMuonList.h"
#include "BubbleSpeak/CandDigiPairHandle.h"
#include "BubbleSpeak/CandMSTClusterHandle.h"
#include "BubbleSpeak/CandMSTClusterListHandle.h"
#include "BubbleSpeak/CandThruMuon.h"
#include "BubbleSpeak/CandThruMuonHandle.h"

ClassImp(AlgThruMuonList)

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

CVSID("$Id: AlgThruMuonList.cxx,v 1.19 2002/08/24 23:45:53 miyagawa Exp $");

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

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

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

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

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

Bool_t AlgThruMuonList::IsMatch(const CandMSTClusterHandle &clh1,
          const CandMSTClusterHandle &clh2, Float_t diff) const
{
//
//  Purpose:  Check whether clusters match up.
//
//  Arguments:
//    clh1    in      Handle to CandMSTCluster to check for match.
//    clh2    in      Handle to CandMSTCluster to check for match.
//    diff    in      Max separation between cluster ends.
//
//  Return:
//    kTRUE   if ends of a cluster are within specified distance of ends
//            of other cluster
//    kFALSE  otherwise
//

   return ((TMath::Abs(clh1.GetZmin() - clh2.GetZmin()) < diff)
           && (TMath::Abs(clh1.GetZmax() - clh2.GetZmax()) < diff));
}

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

void AlgThruMuonList::RunAlg(AlgConfig &ac, CandHandle &ch,
                                            CandContext &cx)
{
//
//  Purpose: Identify CandThruMuons by matching together CandMSTClusters
//           with opposite strip orientations.
//
//  Argument:
//    ac        in    AlgConfig containing cluster matching parameters.
//    ch        in    Handle to the new CandThruMuonList to fill.
//    cx        in    CandContext containing the CandMSTClusterList from
//                    which to identify muons.
//
//  Return:  n/a
//

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

// Check for CandMSTClusterListHandle input.
   assert(cx.GetDataIn());
   assert(cx.GetDataIn()->InheritsFrom("CandMSTClusterListHandle"));
   const CandMSTClusterListHandle *cllh =
         dynamic_cast<const CandMSTClusterListHandle *>(cx.GetDataIn());

// Separate clusters into U- and V-orientations.
   TObjArray *clhayU = new TObjArray();
   TObjArray *clhayV = new TObjArray();
   TObjArray *cosmiccounts = 0;
   TIter clhItr(cllh->GetDaughterIterator());
   while (CandMSTClusterHandle *clh =
             dynamic_cast<CandMSTClusterHandle *>(clhItr())) {

      // First check for cluster with veto shield digits.
      TIter chhItr(clh->GetDaughterIterator());
      CandDigiPairHandle *chh =
                          dynamic_cast<CandDigiPairHandle *>(chhItr());
      if (chh->GetStripEndId().IsVetoShield()) {
         if (!cosmiccounts) cosmiccounts = new TObjArray();
         while ( (chh = dynamic_cast<CandDigiPairHandle*>(chhItr())) ) {
            cosmiccounts->Add(chh);
         }
         continue;
      }

      // Switch on plane orientation.
      switch (clh->GetPlaneView()) {
      case PlaneView::kU:
         if (clh->GetNDaughters() <= 1) continue;
         clhayU->Add(clh);
         break;
      case PlaneView::kV:
         if (clh->GetNDaughters() <= 1) continue;
         clhayV->Add(clh);
         break;
      case PlaneView::kA:
      case PlaneView::kB:
         if (!cosmiccounts) cosmiccounts = new TObjArray();
         cosmiccounts->Add(chh);
         while ( (chh = dynamic_cast<CandDigiPairHandle*>(chhItr())) ) {
            cosmiccounts->Add(chh);
         }
/*
         {
            TIter chhItr(clh->GetDaughterIterator());
            while (CandDigiPairHandle *chh =
                   dynamic_cast<CandDigiPairHandle *>(chhItr())) {
               cosmiccounts->Add(chh);
            }
         }
*/
         break;
      default:
         MSG("BubAlg", Msg::kWarning)
            << "Unrecognized plane orientation." << endl;
      }
   }

// Match clusters.
   TObjArray *clhayUV = new TObjArray(2);
   clhayUV->AddAt(clhayU, 0);
   clhayUV->AddAt(clhayV, 1);
   RunMatchMSTClusterAlg(ac.GetDouble("DiffMax"), clhayUV);

// General setup for creating new CandThruMuons.
   MSG("BubAlg", Msg::kVerbose)
      << "Get AlgThruMuon instance from AlgThruMuonFactory." << endl;
   AlgFactory &af = AlgFactory::GetInstance();
   AlgHandle ah = af.GetAlgHandle("AlgThruMuon", "default");

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

   MSG("BubAlg", Msg::kVerbose)
      << "cxx.SetCandRecord(cx.GetCandRecord());" << endl;
   cxx.SetCandRecord(cx.GetCandRecord());

// Iterate over matched pairs to create CandThruMuons.
   TIter mpairItr(clhayUV);
   while (TObjArray *mpair = dynamic_cast<TObjArray *>(mpairItr())) {
      if (cosmiccounts) mpair->AddAt(cosmiccounts, 2);
      cxx.SetDataIn(mpair);
      CandThruMuonHandle cmh = CandThruMuon::MakeCandidate(ah, cxx);
      ch.AddDaughterLink(cmh);
   }
   clhayUV->Delete();
   delete clhayUV;
   delete cosmiccounts;
}

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

void AlgThruMuonList::RunMatchMSTClusterAlg(Float_t diff,
                                            TObjArray *&toayUV)
{
//
//  Purpose:  Match clusters together whose ends are within a specified
//            distance in the axial direction. Each cluster may be
//            matched to only one other cluster to be a muon.
//
//  Arguments:
//    diff    in      Maximum separation between cluster ends.
//    toayUV  in/out  Array with:
//                      [0]  array of U-clusters
//                      [1]  array of V-clusters
//                    On return toayUV points to a new array of matched
//                    cluster pairs saved as arrays, i.e., toayUV is an
//                    array of arrays.  The pairs will have a third slot
//                    for possible cosmic counts.
//
//  Return:   n/a
//

   MSG("BubAlg", Msg::kVerbose)
      << "Starting AlgThruMuonList::RunMatchMSTClusterAlg()" << endl;

// Separate U- and V-cluster arrays.
   assert(toayUV->At(0)->InheritsFrom("TObjArray"));
   assert(toayUV->At(1)->InheritsFrom("TObjArray"));
   TObjArray *toayU = dynamic_cast<TObjArray *>(toayUV->At(0));
   TObjArray *toayV = dynamic_cast<TObjArray *>(toayUV->At(1));

// Declare array to store matches.
   TObjArray *matchUV = new TObjArray();

// Iterate over all cluster combinations.
   TIter clhUItr(toayU);
   while (CandMSTClusterHandle *clhU =
             dynamic_cast<CandMSTClusterHandle *>(clhUItr())) {
      TIter clhVItr(toayV);
      while (CandMSTClusterHandle *clhV =
                dynamic_cast<CandMSTClusterHandle *>(clhVItr())) {

// Check for match.
         if (IsMatch(*clhU, *clhV, diff)) {

// Check whether either cluster has already been matched.
            Bool_t isMatched = kFALSE;
            TObjArrayIter tobjItr(matchUV);
            while (TObjArray *mpair = (TObjArray*) tobjItr()) {
               CandMSTClusterHandle *mlhU =
                     dynamic_cast<CandMSTClusterHandle *>(mpair->At(0));
               CandMSTClusterHandle *mlhV =
                     dynamic_cast<CandMSTClusterHandle *>(mpair->At(1));
               if ((*clhU == *mlhU) || (*clhV == *mlhV)) {

// Remove previous pair from match array.
                  delete matchUV->Remove(mpair);
                  isMatched = kTRUE;

// Remove both pairs from original arrays to stop future searches.
                  toayU->Remove(mlhU);
                  toayV->Remove(mlhV);
                  toayU->Remove(clhU);
                  toayV->Remove(clhV);
                  break;
               }
            }

// If no previous match, add new pair to match array.
// Extra spot for cosmic counts.
            if (isMatched) break;
            else {
               TObjArray *mpair = new TObjArray(3);
               mpair->AddAt(clhU, 0);
               mpair->AddAt(clhV, 1);
               matchUV->Add(mpair);
            }
         }     // End if IsMatch
      }        // End while clhV
   }           // End while clhU

// Clean-up.
   delete toayU;
   delete toayV;
   delete toayUV;
   toayUV = matchUV;
}

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

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

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