
#include "AlgDataQuality.h"

#include "Algorithm/AlgConfig.h"
#include "Algorithm/AlgFactory.h"
#include "Algorithm/AlgHandle.h"
#include "CandData/CandRecord.h"
#include "Candidate/CandContext.h"
#include "MessageService/MsgService.h"
#include "MinosObjectMap/MomNavigator.h" 

#include "CandDataQualityHandle.h"
#include "CandDeadChipHandle.h"

#include "DQHeader.h"
#include "DQRawDigits.h"
#include "DQHotColdElectronics.h"
#include "DQLightInjection.h"
#include "DQSpillServer.h"
#include "RawChip.h"

#include "TObjArray.h"

//
// $Log $
//

ClassImp(AlgDataQuality)

CVSID("$Id: AlgDataQuality.cxx,v 1.3 2006/07/05 16:44:47 blake Exp $");

AlgDataQuality::AlgDataQuality()
{

}
  
AlgDataQuality::~AlgDataQuality()
{

}

void AlgDataQuality::RunAlg(AlgConfig& /*ac*/, CandHandle& ch, CandContext& cx)
{
  MSG("DataQuality", Msg::kDebug) << " AlgDataQuality::RunAlg(...) " << endl;

  Int_t i,j;
  Int_t addme;
  Int_t time,subtime;
  Int_t fSnarlPassFail;

  CandDataQualityHandle& cdh = dynamic_cast<CandDataQualityHandle&>(ch);
  const TObjArray* arr = dynamic_cast<const TObjArray*>(cx.GetDataIn());

  CandRecord* candrec = (CandRecord*)(cx.GetCandRecord());
  Detector::Detector_t detector = candrec->GetVldContext()->GetDetector();
  

  // STORE DATA QUALITY INFORMATION
  // ==============================
  // Sort through monitoring objects and store header information

  MSG("DataQuality", Msg::kDebug) << " Store the Header Information " << endl;

  // Store information from the data quality header
  //  [ run, snarl, timeframe, trigger source etc ... ]
  DQHeader* myDQHeader = (DQHeader*)(arr->At(0));
  MSG("DataQuality", Msg::kDebug) << " Header (+" << myDQHeader->GetTime()-myDQHeader->GetTime() << ")" << endl; 
  MSG("DataQuality", Msg::kDebug) << "     [run=" << myDQHeader->GetRun() << ", snarl=" << myDQHeader->GetSnarl() << "]" << endl;
  MSG("DataQuality", Msg::kDebug) << "     [trigger=" << myDQHeader->GetTriggerNanosec() << ", base=" << myDQHeader->GetBaseNanosec() << ", subtracted=" << myDQHeader->GetTriggerNanosec()-myDQHeader->GetBaseNanosec() << "]" << endl;
  cdh.SetTime(myDQHeader->GetTime());
  cdh.SetRunType(myDQHeader->GetRunType());
  cdh.SetRun(myDQHeader->GetRun());
  cdh.SetSubRun(myDQHeader->GetSubRun());
  cdh.SetTimeFrame(myDQHeader->GetTimeFrame());
  cdh.SetSnarl(myDQHeader->GetSnarl());
  cdh.SetTriggerSource(myDQHeader->GetTriggerSource());
  cdh.SetTriggerTime(myDQHeader->GetTriggerNanosec());
  cdh.SetErrorCode(myDQHeader->GetErrorCode());
  fSnarlPassFail = 1;

  // Store information on the raw digit data block
  //  [ readout errors, busy electronics etc ... ]
  DQRawDigits* myDQRawDigits = (DQRawDigits*)(arr->At(1));
  MSG("DataQuality", Msg::kDebug) << " Raw Digits (+" << myDQRawDigits->GetTime()-myDQHeader->GetTime() << ")" << endl;
  MSG("DataQuality", Msg::kDebug) << "     [pre hits=" << myDQRawDigits->GetPreTriggerDigits() << ", post hits=" << myDQRawDigits->GetPostTriggerDigits() << ", snarl hits=" << myDQRawDigits->GetSnarlMultiplicity() << ", busy/error chips=" << 1+myDQRawDigits->GetLast()<< ", pass/fail=" << myDQRawDigits->GetSnarlPassFail() << "]" << endl;
  cdh.SetPreTriggerDigits(myDQRawDigits->GetPreTriggerDigits());
  cdh.SetPostTriggerDigits(myDQRawDigits->GetPostTriggerDigits());
  cdh.SetSnarlMultiplicity(myDQRawDigits->GetSnarlMultiplicity());
  fSnarlPassFail = myDQRawDigits->GetSnarlPassFail();
  
  // Store information on the hot and cold electronics (check time)
  // [ crate mask etc... ]
  DQHotColdElectronics* myDQHotColdElectronics = (DQHotColdElectronics*)(arr->At(2));
  if( myDQHotColdElectronics->GetTime()>0 ){
    MSG("DataQuality", Msg::kDebug) << " Hot/Cold Electronics (+" << myDQHotColdElectronics->GetTime()-myDQHeader->GetTime() << ")" << endl;
    MSG("DataQuality", Msg::kDebug) << "     [crates=" << myDQHotColdElectronics->GetNumberOfCrates() << ", hot/cold chips=" << 1+myDQHotColdElectronics->GetLast() << "]" << endl;
  }
  if( myDQHotColdElectronics->GetTime()>0
   && myDQHotColdElectronics->GetTime()-myDQHeader->GetTime()==0 ){
    cdh.SetCrateMask(myDQHotColdElectronics->GetNumberOfCratesInReadout());
  }
  
  // Store information on light injection (check time)
  //  [ LI type, LI pulser box/led, LI pulse parameters, LI time etc... ]
  DQLightInjection* myDQLightInjection = (DQLightInjection*)(arr->At(3));
  if( myDQLightInjection->GetTime()>0 ){
    MSG("DataQuality", Msg::kDebug) << " Light Injection (+" << myDQLightInjection->GetTime()-myDQHeader->GetTime() << ")" << endl;
    MSG("DataQuality", Msg::kDebug) << "     [box=" << myDQLightInjection->GetPulserBox() << ", led=" << myDQLightInjection->GetPulserLed() << ", tpmt hits=" << 1+myDQLightInjection->GetLast()<< "]" << endl;
  }
  if( myDQLightInjection->GetTime()>0 
   && myDQLightInjection->GetTime()-myDQHeader->GetTime()==0 ){
    cdh.SetLiCalibPoint(myDQLightInjection->GetCalibPoint());
    cdh.SetLiCalibType(myDQLightInjection->GetCalibType());
    cdh.SetLiPulserBox(myDQLightInjection->GetPulserBox());
    cdh.SetLiPulserLed(myDQLightInjection->GetPulserLed());
    cdh.SetLiPulseHeight(myDQLightInjection->GetPulseHeight());
    cdh.SetLiPulseWidth(myDQLightInjection->GetPulseWidth());
  }

  // Store information on spills (check time)
  //  [ spill status, type, time error etc... ]
  DQSpillServer* myDQSpillServer = (DQSpillServer*)(arr->At(4));
  if( myDQSpillServer->GetTime()>0 ){
    MSG("DataQuality", Msg::kDebug) << " Spill Server (+" << myDQSpillServer->GetTime()-myDQHeader->GetTime() << ")" << endl;
    MSG("DataQuality", Msg::kDebug) << "     [status=" << myDQSpillServer->GetSpillStatus() << ", type=" << myDQSpillServer->GetSpillType() << ", error=" << myDQSpillServer->GetSpillTimeError() << "]" << endl;
  }
  if( myDQSpillServer->GetTime()>0 
   && myDQSpillServer->GetTime()-myDQHeader->GetTime()==0 ){
    cdh.SetSpillStatus(myDQSpillServer->GetSpillStatus());
    cdh.SetSpillType(myDQSpillServer->GetSpillType());
    cdh.SetSpillTimeError(myDQSpillServer->GetSpillTimeError());
  }
  
  
  // CALCULATE THE CLOSEST TPMT HIT
  // ==============================
  // look in window [-100,+100] microseconds around trigger time
    
  if( myDQLightInjection->GetTime()>0 
   && myDQLightInjection->GetTime()-myDQHeader->GetTime()==0 ){

    Int_t fMinSubTime=-100000;
    Int_t fMaxSubTime=+100000;
    
    Int_t litrigger=0;
    Int_t litime=-99999;
    Int_t lisubtime=-99999;
    Int_t lireltime=-99999;

    for(i=0;i<1+myDQLightInjection->GetLast();i++){
      RawChip* tpmt = (RawChip*)(myDQLightInjection->At(i));

      time=tpmt->GetNanosec();
      subtime=tpmt->GetNanosec()-(myDQHeader->GetTriggerNanosec()-myDQHeader->GetBaseNanosec());

      if( (subtime>fMinSubTime && subtime<fMaxSubTime) 
       && (lireltime<0 || abs(subtime)<lireltime) ){
        litrigger=1;
        litime=time;
        lisubtime=subtime;
        lireltime=abs(subtime);
      }
    }
    
    MSG("DataQuality", Msg::kDebug) << " Tpmt Hits (+" << myDQLightInjection->GetTime()-myDQHeader->GetTime() << ")" << endl;
    MSG("DataQuality", Msg::kDebug) << "     [trigger=" << litrigger << ", litime=" << litime << ", lisubtime=" << lisubtime << ", lireltime=" << lireltime << "]" << endl;

    cdh.SetLiTrigger(litrigger);
    cdh.SetLiTime(litime);
    cdh.SetLiSubtractedTime(lisubtime);
    cdh.SetLiRelativeTime(lireltime);
}


  // STORE THE BAD CHANNELS
  // ======================
  //
 
  MSG("DataQuality", Msg::kDebug) << " Sort the Bad Chips " << endl;

  TObjArray* badelectronics = new TObjArray();

  // Adding Hot/Cold Chips
  for(i=0;i<1+myDQHotColdElectronics->GetLast();i++){
    RawChip* badchip = (RawChip*)(myDQHotColdElectronics->At(i));
    addme=1;
    for(j=0;j<1+badelectronics->GetLast();j++){
      TObjArray* tmparray = (TObjArray*)(badelectronics->At(j));
      RawChip* tmpchip = (RawChip*)(tmparray->First());
      if( badchip->IsSameChip(tmpchip) ){
        tmparray->Add(badchip);
        addme=0;
      }
    }
    if( addme ){
      TObjArray* newarray = new TObjArray();
      newarray->Add(badchip);
      badelectronics->Add(newarray);
    }
  }

  // Adding Raw Digits
  for(i=0;i<1+myDQRawDigits->GetLast();i++){
    RawChip* badchip = (RawChip*)(myDQRawDigits->At(i));
    addme=1;
    for(j=0;j<1+badelectronics->GetLast();j++){
      TObjArray* tmparray = (TObjArray*)(badelectronics->At(j));
      RawChip* tmpchip = (RawChip*)(tmparray->First());
      if( badchip->IsSameChip(tmpchip) ){
        tmparray->Add(badchip);
        addme=0;
      }
    }
    if( addme ){
      TObjArray* newarray = new TObjArray();
      newarray->Add(badchip);
      badelectronics->Add(newarray);
    }
  }

  MSG("DataQuality", Msg::kDebug) << "   ... Found " << 1+badelectronics->GetLast() << " Bad Chips " << endl;

  // Create CandDeadChips
  AlgFactory &af = AlgFactory::GetInstance();
  AlgHandle ah = af.GetAlgHandle("AlgDeadChip", "default");
  CandContext mycx(this, cx.GetMom());
  mycx.SetCandRecord(candrec);

  for(i=0;i<1+badelectronics->GetLast();i++){
    TObjArray* tmparray = (TObjArray*)(badelectronics->At(i)); 
    mycx.SetDataIn(tmparray);
    CandDeadChipHandle deadchip = CandDeadChip::MakeCandidate(ah,mycx);
    deadchip.SetName(TString("CandDeadChipHandle"));
    deadchip.SetTitle(TString("Created by CandDataQualityModule"));
    cdh.AddDaughterLink(deadchip);
  }

  badelectronics->Delete();
  delete badelectronics;


  // SET THE DATA QUALITY SUMMARY
  // ============================

  CandDataQuality::DataQuality_t quality(CandDataQuality::kOkay);
  
  // Far Detector
  if( detector==Detector::kFar ){

    Int_t fMinMultiplicity=10;
    Int_t fMaxMultiplicity=1000;
    Int_t fMaxColdChips=20;
    Int_t fMaxBusyChips=20;
    Int_t fMaxHotChips=9999;
    Int_t fMaxReadoutErrors=9999;

    // low multiplicity (less than 10 hits)
    if( cdh.GetSnarlMultiplicity()<fMinMultiplicity ){
      quality=(CandDataQuality::DataQuality_t)(quality|CandDataQuality::kLowMultiplicity);
    }

    // high multiplicity (more than 1000 hits)
    if( cdh.GetSnarlMultiplicity()>fMaxMultiplicity ){
      quality=(CandDataQuality::DataQuality_t)(quality|CandDataQuality::kHighMultiplicity);
    }

    // proximity to Light Injection
    if( cdh.GetLiTrigger()==1 ){
      quality=(CandDataQuality::DataQuality_t)(quality|CandDataQuality::kLightInjection);
    };

    // proximity to Timeframe Boundary
    if( cdh.GetTriggerTime()-1000000000>-100000 ){
      quality=(CandDataQuality::DataQuality_t)(quality|CandDataQuality::kTimeFrameBoundary);
    }

    // too many cold chips
    if( cdh.GetColdChips()>fMaxColdChips ){
      quality=(CandDataQuality::DataQuality_t)(quality|CandDataQuality::kBad);
      quality=(CandDataQuality::DataQuality_t)(quality|CandDataQuality::kManyColdChips);
    }

    // too many hot chips
    if( cdh.GetBusyChips()>fMaxHotChips ){
      quality=(CandDataQuality::DataQuality_t)(quality|CandDataQuality::kBad);
      quality=(CandDataQuality::DataQuality_t)(quality|CandDataQuality::kManyHotChips);
    }

    // too many busy chips
    if( cdh.GetBusyChips()>fMaxBusyChips ){
      quality=(CandDataQuality::DataQuality_t)(quality|CandDataQuality::kBad);
      quality=(CandDataQuality::DataQuality_t)(quality|CandDataQuality::kManyBusyChips);
    }

    // too many readout errors
    if( cdh.GetReadoutErrors()>fMaxReadoutErrors ){
      quality=(CandDataQuality::DataQuality_t)(quality|CandDataQuality::kBad);
      quality=(CandDataQuality::DataQuality_t)(quality|CandDataQuality::kManyReadoutErrors);
    }

  }

  // Near Detector
  if( detector==Detector::kNear ){

    //
    // SET NEAR DETECTOR STUFF
    //

  }

  // bailed out of snarl
  if( fSnarlPassFail==0 ){
    quality=(CandDataQuality::DataQuality_t)(quality|CandDataQuality::kBad);
    quality=(CandDataQuality::DataQuality_t)(quality|CandDataQuality::kFailed);
  }

  // set data quality summary
  cdh.SetDataQuality(quality);


  MSG("DataQuality", Msg::kDebug) << endl  
    << "   NEW CANDDATAQUALITYHANDLE " << endl
    << "   ========================= " << endl
    << "    Time=" << cdh.GetTime() << endl
    << "    RunType=" << cdh.GetRunType() << endl
    << "    Run=" << cdh.GetRun() << endl
    << "    SubRun=" << cdh.GetSubRun() << endl
    << "    TimeFrame=" << cdh.GetTimeFrame() << endl
    << "    Snarl=" << cdh.GetSnarl() << endl
    << "    TriggerSource=" << cdh.GetTriggerSource() << endl
    << "    TriggerTime=" << cdh.GetTriggerTime() << endl
    << "    ErrorCode=" << cdh.GetErrorCode() << endl
    << "    CrateMask=" << cdh.GetCrateMask() << endl
    << "    PreTriggerDigits=" << cdh.GetPreTriggerDigits() << endl
    << "    PostTriggerDigits=" << cdh.GetPostTriggerDigits() << endl
    << "    SnarlMultiplicity=" << cdh.GetSnarlMultiplicity() << endl
    << "    SpillStatus=" << cdh.GetSpillStatus() << endl
    << "    SpillType=" << cdh.GetSpillType() << endl
    << "    SpillTimeError=" << cdh.GetSpillTimeError() << endl
    << "    LiTrigger=" << cdh.GetLiTrigger() << endl
    << "    LiTime=" << cdh.GetLiTime() << endl
    << "    LiSubtractedTime=" << cdh.GetLiSubtractedTime() << endl
    << "    LiRelativeTime=" << cdh.GetLiRelativeTime() << endl
    << "    LiCalibPoint=" << cdh.GetLiCalibPoint() << endl
    << "    LiCalibType=" << cdh.GetLiCalibType() << endl
    << "    LiPulserBox=" << cdh.GetLiPulserBox() << endl
    << "    LiPulserLed=" << cdh.GetLiPulserLed() << endl
    << "    LiPulseHeight=" << cdh.GetLiPulseHeight() << endl
    << "    LiPulseWidth=" << cdh.GetLiPulseWidth() << endl
    << "    ColdChips=" << cdh.GetColdChips() << endl
    << "    HotChips=" << cdh.GetHotChips() << endl
    << "    BusyChips=" << cdh.GetBusyChips() << endl
    << "    ReadoutErrors=" << cdh.GetReadoutErrors() << endl
    << "    DataQuality=" << cdh.GetDataQuality() << endl;

  return;
}

void AlgDataQuality::Trace(const char * /* c */) const
{

}


