#include "DQRawDigits.h"
#include "RawChip.h"

#include "RawData/RawDigit.h"
#include "MessageService/MsgService.h"
 
//
// $Log $
//

ClassImp(DQRawDigits)
 
CVSID("$Id: DQRawDigits.cxx,v 1.4 2006/09/19 13:11:27 blake Exp $");

DQRawDigits::DQRawDigits() :
  fTime(-1), 
  fBaseTime(0),
  fTriggerTime(-99999),
  fTriggerBaseTime(0),
  fSnarl(-1),
  fPreTriggerDigits(0),
  fPostTriggerDigits(0), 
  fSnarlMultiplicity(0),
  fSnarlPassFail(0),
  fRawReadout(0), 
  fRawDigitsList(0),
  fReadoutErrorList(0),
  fReadoutBusyList(0)
{
  fRawReadout = new RawReadout();

  fRawDigitsList = new TObjArray();
  fReadoutErrorList = new TObjArray();
  fReadoutBusyList = new TObjArray();
}
 
DQRawDigits::DQRawDigits(const DQRawDigits& rhs) :
  TObject(rhs),
  fTime(rhs.fTime), 
  fBaseTime(rhs.fBaseTime),
  fTriggerTime(rhs.fTriggerTime), 
  fTriggerBaseTime(rhs.fTriggerBaseTime), 
  fSnarl(rhs.fSnarl),
  fPreTriggerDigits(rhs.fPreTriggerDigits),
  fPostTriggerDigits(rhs.fPostTriggerDigits),
  fSnarlMultiplicity(rhs.fSnarlMultiplicity),
  fSnarlPassFail(rhs.fSnarlPassFail),
  fRawReadout(rhs.fRawReadout),
  fRawDigitsList(rhs.fRawDigitsList),
  fReadoutErrorList(rhs.fReadoutErrorList),
  fReadoutBusyList(rhs.fReadoutBusyList)
{
 
}
   
DQRawDigits::~DQRawDigits()
{
  delete fRawReadout;

  fReadoutErrorList->Delete();
  delete fReadoutErrorList;

  fReadoutBusyList->Delete();
  delete fReadoutBusyList;

  fRawDigitsList->Clear();
  delete fRawDigitsList;
}

void DQRawDigits::Process(RawDaqHeader*)
{
  MSG("DataQuality",Msg::kVerbose) << " DQRawDigits::Process(RawDaqHeader) " << endl;  

}

void DQRawDigits::Process(RawDaqSnarlHeader* hdr)
{
  MSG("DataQuality",Msg::kVerbose) << " DQRawDigits::Process(RawDaqSnarlHeader) " << endl;

  fTime = hdr->GetVldContext().GetTimeStamp().GetSec();
  fTriggerTime = hdr->GetVldContext().GetTimeStamp().GetNanoSec();
  fSnarl = hdr->GetSnarl();
  fSnarlPassFail = 0;

  MSG("DataQuality",Msg::kVerbose) << " DQRawDigits [" << fSnarl << "] [" << fTime << "|" << fTriggerTime << "] [" << fBaseTime << "|" << fTriggerBaseTime << "]" << endl;
}

void DQRawDigits::Process(RawDaqHeaderBlock* hdr)
{
  MSG("DataQuality",Msg::kVerbose) << " DQRawDigits::Process(RawDaqHeaderBlock) " << endl;  

  fTime = -1;
  fTriggerTime = -99999;
  fSnarl = -1;
  fSnarlPassFail = 0;

  fBaseTime = hdr->GetVldContext().GetTimeStamp().GetSec();
  fTriggerBaseTime = hdr->GetVldContext().GetTimeStamp().GetNanoSec();

  MSG("DataQuality",Msg::kVerbose) << " DQRawDigits [" << fSnarl << "] [" << fTime << "|" << fTriggerTime << "] [" << fBaseTime << "|" << fTriggerBaseTime << "]" << endl;
}

void DQRawDigits::Process(RawSnarlHeaderBlock* rdb)
{
  MSG("DataQuality",Msg::kVerbose) << " DQRawDigits::Process(RawSnarlHeaderBlock) " << endl;

  fTime = rdb->GetTriggerTime().GetSec();
  fTriggerTime = rdb->GetTriggerTime().GetNanoSec();
  fSnarl = rdb->GetSnarl();
  fSnarlPassFail = 0;
  
  MSG("DataQuality",Msg::kVerbose) << " DQRawDigits [" << fSnarl << "] [" << fTime << "|" << fTriggerTime << "] [" << fBaseTime << "|" << fTriggerBaseTime << "]" << endl;
}

void DQRawDigits::Process(RawDigitDataBlock* rdb)
{
  MSG("DataQuality",Msg::kVerbose) << " DQRawDigits::Process(RawDigitDataBlock) " << endl;

  MSG("DataQuality",Msg::kVerbose) << " DQRawDigits [" << fSnarl << "] [" << fTime << "|" << fTriggerTime << "] [" << fBaseTime << "|" << fTriggerBaseTime << "]" << endl;
  MSG("DataQuality",Msg::kVerbose) << " Subtracted time (+" << fTime-fBaseTime << ",+" << fTriggerTime-fTriggerBaseTime << ")" << endl;
  MSG("DataQuality",Msg::kVerbose) << " Number of Raw Digits [" << rdb->GetNumberOfDigits() << "]" << endl;

  Int_t i,j,k;
  Int_t chid;
  
  VldContext vldc = (VldContext)(rdb->GetVldContext());
  
  fPreTriggerDigits = 0;
  fPostTriggerDigits = 0;
  fSnarlMultiplicity = 0;
  fSnarlPassFail = 0;

  // Reset the lists of busy/error chips
  fReadoutErrorList->Delete();
  fReadoutBusyList->Delete();
  fRawDigitsList->Clear();

  // Bail out of snarl if number of digits is too high
  if( vldc.GetDetector()==Detector::kFar
   && rdb->GetNumberOfDigits()>10000 ){
    fPreTriggerDigits = 0;
    fPostTriggerDigits = rdb->GetNumberOfDigits();
    fSnarlMultiplicity = rdb->GetNumberOfDigits();
    fSnarlPassFail = 0;
    MSG("DataQuality",Msg::kWarning) << " RAW DIGITS > 10,000, BAILING OUT !!! " << endl;
    return;
  }

  // Iterate over raw digits and record any pre-trigger 
  // hits along with any hits containing readout errors
  // ==================================================
  TIter digitr = rdb->GetDatumIter();
  while(RawDigit *rd = (RawDigit*)(digitr())){
    if(rd){
      RawChannelId rawch = (RawChannelId)(rd->GetChannel());
      
      Int_t adc = (Int_t)(rd->GetADC());
      Int_t tdc = (Int_t)(rd->GetTDC());
      Int_t errorcode = (Int_t)(rd->GetErrorCode());
      RawReadout::ReadoutType_t readout = fRawReadout->GetReadoutType(rawch);

      // Near Detector, QIE Electronics
      // ==============================
      if( vldc.GetDetector()==Detector::kNear
       && rawch.GetElecType()==ElecType::kQIE ){

        // Readout Errors
        if( errorcode>0 ){
          // (don't check for duplicates in ND)
          // chid=-1;
          // for(i=0;chid<0&&i<1+fReadoutErrorList->GetLast();i++){
          //   RawChip* chip = (RawChip*)(fReadoutErrorList->At(i));
          //   if( chip->IsSameMenu(rawch) && chip->GetTdc()==tdc ) chid=i;
	  // }
          RawChip* chip = new RawChip(rawch,adc,tdc,readout,RawChip::kError,errorcode);
          fReadoutErrorList->Add(chip);
	}

        // Overflowing TDC values (TDC>TF+1ms)
        if( tdc>53100000+53100 ){
          // (don't check for duplicates in ND)
          // chid=-1;
          // for(i=0;chid<0&&i<1+fReadoutErrorList->GetLast();i++){
          //   RawChip* chip = (RawChip*)(fReadoutErrorList->At(i));
          //   if( chip->IsSameMenu(rawch) && chip->GetTdc()==tdc ) chid=i;
	  // }
          RawChip* chip = new RawChip(rawch,adc,tdc,readout,RawChip::kError,errorcode);
          fReadoutErrorList->Add(chip);
	}

        // Pre-Trigger Hits
        if( fTime==fBaseTime
	 && (Double_t)((1000.0/53.1)*(tdc+1.0))-(Double_t)(fTriggerTime-fTriggerBaseTime)<0.0 ){
          // (don't check for duplicates in ND)
          // chid=-1;
          // for(i=0;chid<0&&i<1+fReadoutBusyList->GetLast();i++){
          //   RawChip* chip = (RawChip*)(fReadoutBusyList->At(i));
          //   if( chip->IsSameMenu(rawch) && chip->GetTdc()==tdc ) chid=i;
	  // }
          RawChip* chip = new RawChip(rawch,adc,tdc,readout,RawChip::kBusy,0);
          fReadoutBusyList->Add(chip);
          fPreTriggerDigits++;
	}
        else{
          fPostTriggerDigits++;
          if( readout==RawReadout::kDetector ){
            fSnarlMultiplicity++;
	  }
	}
      }

      // Far Detector, VA Electronics
      // ============================
      if( vldc.GetDetector()==Detector::kFar
       && rawch.GetElecType()==ElecType::kVA ){

        // Readout Errors
        if( errorcode>0 ){
          chid=-1;
          for(i=0;chid<0&&i<1+fReadoutErrorList->GetLast();i++){
            RawChip* chip = (RawChip*)(fReadoutErrorList->At(i));
            if( chip->IsSameVaChip(rawch) && chip->GetTdc()==tdc ) chid=i;
	  }
          if( chid<0 ){
            RawChip* chip = new RawChip(rawch,adc,tdc,readout,RawChip::kError,errorcode);
            fReadoutErrorList->Add(chip);
	  }
          else{
            RawChip* chip = (RawChip*)(fReadoutErrorList->At(chid));
            chip->AddChannel(rawch,adc,tdc,errorcode);
	  }
	}

        // Overflowing TDC values (TDC>TF+1ms)
        if( tdc>640000000+640000 ){
          chid=-1;
          for(i=0;chid<0&&i<1+fReadoutErrorList->GetLast();i++){
            RawChip* chip = (RawChip*)(fReadoutErrorList->At(i));
            if( chip->IsSameVaChip(rawch) && chip->GetTdc()==tdc ) chid=i;
	  }
          if( chid<0 ){
            RawChip* chip = new RawChip(rawch,adc,tdc,readout,RawChip::kError,errorcode);
            fReadoutErrorList->Add(chip);
	  }
          else{
            RawChip* chip = (RawChip*)(fReadoutErrorList->At(chid));
            chip->AddChannel(rawch,adc,tdc,errorcode);
	  }
	}

        // Pre-Trigger Hits
        if( fTime==fBaseTime
	 && (Double_t)((1000.0/640.0)*(tdc+1.0))-(Double_t)(fTriggerTime-fTriggerBaseTime)<0.0 ){
          chid=-1;
          for(i=0;chid<0&&i<1+fReadoutBusyList->GetLast();i++){
            RawChip* chip = (RawChip*)(fReadoutBusyList->At(i));
            if( chip->IsSameVaChip(rawch) && chip->GetTdc()==tdc ) chid=i;
	  }
          if( chid<0 ){
            RawChip* chip = new RawChip(rawch,adc,tdc,readout,RawChip::kBusy,0);
            fReadoutBusyList->Add(chip);
	  }
          else{
            RawChip* chip = (RawChip*)(fReadoutBusyList->At(chid));
            chip->AddChannel(rawch,adc,tdc,errorcode);
	  }
          fPreTriggerDigits++;
	}
        else{
          fPostTriggerDigits++;
          if( readout==RawReadout::kDetector ){
            fSnarlMultiplicity++;
	  }
	}
      }

    }
  }

  // Now sort throught the list of pre-trigger hits 
  // to determine which chips were actually busy.

  // Far Detector, VA Electronics
  if( vldc.GetDetector()==Detector::kFar ){

    // Busy VA Electronics:
    // This assumes 2/36 VARC trigger in detector but not in shield.
    // The VARC-triggered hits are read out in the following order:
    //   (vaadc,vachip)=(0,0)->(0,1)->(0,2)->(1,0)->(1,1)->(1,2)
    // It's assumed that no triggered hits get sparsified away.
    // The parameters used are:
    //   VaTriggerTime = VA Trigger Window 
    //                  (1ns in shield, 400ns in detector)
    //   VaReadoutTime = VA Readout Time
    //                  (5000ns)

    Int_t n,ntmp;
    Int_t prehits[6];
    Int_t VaTriggerTime=400;
    Int_t VaReadoutTime=5000;
    
    for(i=0;i<1+fReadoutBusyList->GetLast();i++){
      RawChip* chip = (RawChip*)(fReadoutBusyList->At(i));
      
      n=0; 
      for(k=0;k<6;k++){ prehits[k]=0; }
      if( chip->GetReadoutType()==RawReadout::kDetector ) VaTriggerTime=400; 
      else VaTriggerTime=1;
      
      for(j=0;j<1+fReadoutBusyList->GetLast();j++){
        RawChip* prechip = (RawChip*)(fReadoutBusyList->At(j));
        if(chip->IsSameVmm(prechip)){
          if( prechip->GetNanosec()-chip->GetNanosec()<+VaTriggerTime
           && prechip->GetNanosec()-chip->GetNanosec()>=-VaTriggerTime
           && prechip->GetVaPriority()<=chip->GetVaPriority() ) n++;
          for(k=0;k<6;k++){
            if( prechip->GetNanosec()-chip->GetNanosec()<-VaTriggerTime
	     && prechip->GetNanosec()-chip->GetNanosec()<-k*VaReadoutTime
	     && prechip->GetNanosec()-chip->GetNanosec()>=-(k+1)*VaReadoutTime ) prehits[k]++;
	  }
        }
      }

      for(j=0;j<6;j++){
        ntmp=0;
        for(k=0;k<j+1;k++){
          ntmp+=prehits[k];
	}
        if(ntmp>j) n+=ntmp;
      }

      if( chip->GetNanosec()-fTriggerTime>-n*VaReadoutTime ){
        fRawDigitsList->Add(chip);
      }
    }

    // VA Readout Errors
    for(i=0;i<1+fReadoutErrorList->GetLast();i++){
      RawChip* chip = (RawChip*)(fReadoutErrorList->At(i));
      fRawDigitsList->Add(chip);
    }

  }

  // Near Detector, QIE Electronics
  if( vldc.GetDetector()==Detector::kNear ){

    // Busy QIE Electronics:
    // assume it's deadtime-less for now

    // QIE Readout Errors
    for(i=0;i<1+fReadoutErrorList->GetLast();i++){
      RawChip* chip = (RawChip*)(fReadoutErrorList->At(i));
      fRawDigitsList->Add(chip);
    }
  }

  // Reached the end of the snarl okay
  fSnarlPassFail=1;

  MSG("DataQuality",Msg::kVerbose) << " Found " << 1+this->GetLast() << " bad chips " << endl;

}
  
Int_t DQRawDigits::GetLast() const
{
  return fRawDigitsList->GetLast();
}
   
TObject* DQRawDigits::At(Int_t i) const
{
  return (TObject*)(fRawDigitsList->At(i));
}
