////////////////////////////////////////////////////////////////////////
// Package: CandFitTrackCam
//
// AlgFitTrackCamList.cxx
//
// marshall@hep.phy.cam.ac.uk
////////////////////////////////////////////////////////////////////////
#include <cassert>
extern "C" {
#include <unistd.h>    // sysconf
#include <sys/times.h> // times()
}

#include "Conventions/Munits.h"
#include "MessageService/MsgService.h"
#include "MinosObjectMap/MomNavigator.h"
#include "Navigation/NavKey.h"
#include "Navigation/NavSet.h"
#include "Validity/VldContext.h"

#include "Algorithm/AlgFactory.h"
#include "Algorithm/AlgHandle.h"
#include "Algorithm/AlgConfig.h"
#include "Candidate/CandContext.h"

#include "RecoBase/CandTrackHandle.h"
#include "RecoBase/CandTrackListHandle.h"
#include "RecoBase/CandStripHandle.h"
#include "RecoBase/CandStripListHandle.h"
#include "RecoBase/CandSliceHandle.h"
#include "RecoBase/CandSliceListHandle.h"
#include "CandDigit/CandDigitHandle.h"

#include "CandFitTrackCam/AlgFitTrackCamList.h"
#include "CandFitTrackCam/CandFitTrackCamListHandle.h"
#include "CandFitTrackCam/CandFitTrackCamHandle.h"

#include "TObjArray.h"

ClassImp(AlgFitTrackCamList)

CVSID("$Id: AlgFitTrackCamList.cxx,v 1.11 2008/07/03 21:55:22 gmieg Exp $");

////////////////////////////////////////////////////////////////////////
AlgFitTrackCamList::AlgFitTrackCamList()
{
}
////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////
AlgFitTrackCamList::~AlgFitTrackCamList()
{
}
////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////
void AlgFitTrackCamList::RunAlg(AlgConfig &ac,CandHandle &ch,CandContext &cx)
{
  assert(cx.GetDataIn());

  // Check for CandTrackListHandle input
  if (cx.GetDataIn()->InheritsFrom("CandTrackListHandle")) 
    {
      const CandTrackListHandle *ctlh = dynamic_cast<const CandTrackListHandle*>(cx.GetDataIn());
      const MomNavigator *mom = cx.GetMom();
      
      CandRecord* candrec = (CandRecord*)(cx.GetCandRecord());
      assert(candrec);
      
      VldContext* vldc = (VldContext*)(candrec->GetVldContext());
      Detector::Detector_t detector = vldc->GetDetector();


      // Create the new tracklist
      ////////////////////////////////////////////
      CandFitTrackCamListHandle& tracklist = dynamic_cast<CandFitTrackCamListHandle&>(ch);
      if( !ctlh || ctlh->GetNDaughters()<1 ) {
	// Require number of CandTracks to be non-zero to do anything more
	MSG("AlgFitTrackCamList", Msg::kWarning) << " !ctlh || ctlh->GetNDaughters()<1 " << endl;
	return;
      }
      ////////////////////////////////////////////



      // Make sure we pass the finder track with most strips to the fitter first of all
      // (important for ND Spectrometer DeMuxing)
      ////////////////////////////////////////////
      TIter trackItr(ctlh->GetDaughterIterator());

      int* NFinderStrips = new int[ctlh->GetNDaughters()]; 
      int index; int MaxFinderStrips; int id;
      
      for(index=0; index<ctlh->GetNDaughters(); ++index) {NFinderStrips[index]=-1;} 
      index=0;
      
      while (CandTrackHandle *track = dynamic_cast<CandTrackHandle*>(trackItr())) 
	{NFinderStrips[index]=track->GetNDaughters(); ++index;}
      ////////////////////////////////////////////



      // Set-up for calculating CPUTime
      ////////////////////////////////////////////
      clock_t dummyt;
      struct tms t1;
      struct tms t2;
      static double ticksPerSecond = sysconf(_SC_CLK_TCK);
      ////////////////////////////////////////////



      // Now loop over the ordered list of tracks, carrying out the fit
      ////////////////////////////////////////////
      for(int k=0; k<ctlh->GetNDaughters(); ++k) 
	{
	  MaxFinderStrips=0; id=-1;
	  for(index=0; index<ctlh->GetNDaughters(); ++index) {
	    if(MaxFinderStrips<NFinderStrips[index]) {MaxFinderStrips=NFinderStrips[index]; id=index;} 
	  }
	  
	  if(id>=0) {
	    NFinderStrips[id]=-1; index=0; trackItr.Reset();
	    
	    CandTrackHandle *track = 0; 
	    
	    while (CandTrackHandle *track1 = dynamic_cast<CandTrackHandle*>(trackItr())) {
	      if(index==id) {track=track1; break;} 
	      ++index;
	    }
	    
	    if(track!=0) {
	      AlgFactory &af = AlgFactory::GetInstance();

              const char *configname;
              AlgHandle ah_trk;
	      if (ac.Get("AlgFitTrackCamConfig", configname))
                ah_trk = af.GetAlgHandle("AlgFitTrackCam", configname);
              else
                ah_trk = af.GetAlgHandle("AlgFitTrackCam", "default");
	      
	      // Create complete track
	      CandContext cx0(this, mom);
	      cx0.SetDataIn(track);
	      cx0.SetCandRecord(candrec);
	      
	      dummyt = times(&t1);
	      CandFitTrackCamHandle cth = CandFitTrackCam::MakeCandidate(ah_trk, cx0);
	      dummyt = times(&t2);
	      cth.SetCPUTime((Double_t)(t2.tms_utime+t2.tms_stime-t1.tms_utime-t1.tms_stime)/ticksPerSecond);
	      
	      cth.SetName(TString("CandFitTrackCamHandle"));
	      cth.SetTitle(TString("Created by AlgFitTrackCamList"));
	      // Add candtrack to candtracklist
	      tracklist.AddDaughterLink(cth);
	      
	      if(detector==Detector::kNear) {CleanNDSlices(candrec);}
	    }
	  }
	}
      delete[] NFinderStrips;

      if(detector==Detector::kNear) {CheckWeights(candrec);}

      ////////////////////////////////////////////

    }

}
////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////
void AlgFitTrackCamList::CleanNDSlices(CandRecord* candrec)
{
  // Check that there is nothing in slicelist that isn't in striplist
  bool SlushyOnEntry = CandHandle::IsSlushyEnabled();
  vector<CandStripHandle*> StripsToRemove;

  CandHandle::SetSlushyEnabled(kTRUE);

  CandStripListHandle* StripList = dynamic_cast<CandStripListHandle *> 
    (candrec->FindCandHandle("CandStripListHandle"));
	
  CandSliceListHandle* SliceList = dynamic_cast<CandSliceListHandle *> 
    (candrec->FindCandHandle("CandSliceListHandle"));


  if(SliceList && StripList) {
    CandSliceHandleItr sliceItr(SliceList->GetDaughterIterator());
	  
    for (CandSliceHandle* Slice=sliceItr(); Slice ; Slice=sliceItr()) {
      CandStripHandleItr SliceStripItr(Slice->GetDaughterIterator());

      for (CandStripHandle* SliceStrip=SliceStripItr(); SliceStrip; SliceStrip=SliceStripItr()) {
	bool found = false;

	if(SliceStrip->GetPlane()>120){
	  CandStripHandleItr stripItr(StripList->GetDaughterIterator());

	  for (CandStripHandle* strip=stripItr(); strip ; strip=stripItr()) {
	    if(strip->IsEqual(SliceStrip)) {found=true; break;}
	  }
	  if(!found) {StripsToRemove.push_back(SliceStrip);}
	}
      }
      for(unsigned int i=0; i<StripsToRemove.size(); ++i) {Slice->RemoveDaughter(StripsToRemove[i]);}
      StripsToRemove.clear();
    }
  }
        
  if(!SlushyOnEntry) {CandHandle::SetSlushyEnabled(kFALSE);}
}
////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////
void AlgFitTrackCamList::CheckWeights(CandRecord* candrec)
{
  // Make sure that the weights in the list of alternative PlexSEIds are
  // good after the ND Spectrometer DeMuxing is complete
  CandStripListHandle* StripList = dynamic_cast<CandStripListHandle *> 
    (candrec->FindCandHandle("CandStripListHandle"));

  if(StripList) {
    CandStripHandleItr stripItr(StripList->GetDaughterIterator());

    for (CandStripHandle* strip=stripItr(); strip ; strip=stripItr()) {
      if(strip->GetPlane()<121) {continue;}
      
      CandDigitHandleItr digitItr(strip->GetDaughterIterator());

      for (CandDigitHandle* digit=digitItr(); digit ; digit=digitItr()) {
	CandDigitHandle* newdig=digit->DupHandle();

	PlexSEIdAltL& newaltl=newdig->GetPlexSEIdAltLWritable();

	for(unsigned int i=0; i<newaltl.size(); ++i) {
	  if(newaltl[i].GetWeight()>0) {newaltl[i].SetWeight((float)1.);}
	}
      }
    }
  }
}
////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////
void AlgFitTrackCamList::Trace(const char * /* c */) const
{
}
////////////////////////////////////////////////////////////////////////

