#include "DataQualityReader.h"

#include "MessageService/MsgService.h"
#include "MinosObjectMap/MomNavigator.h"
#include "JobControl/JobCommand.h"
#include "JobControl/JobCModuleRegistry.h"

#include "RawData/RawRecord.h"
#include "RawData/RawHeader.h"
#include "RawData/RawDaqHeader.h"
#include "RawData/RawDaqSnarlHeader.h"
#include "RawData/RawDigit.h"
#include "RawData/RawChannelId.h"
 
#include "RawData/RawDaqHeaderBlock.h"
#include "RawData/RawSnarlHeaderBlock.h"
#include "RawData/RawRunStartBlock.h"
#include "RawData/RawRunEndBlock.h"
#include "RawData/RawSubRunEndBlock.h"
#include "RawData/RawTpSinglesSummaryBlock.h"
#include "RawData/RawVarcErrorInTfBlock.h"
#include "RawData/RawSpillServerMonitorBlock.h"
#include "RawData/RawLiTpmtDigitsBlock.h"
#include "RawData/RawLIAdcSummaryBlock.h"
#include "RawData/RawDigitDataBlock.h"
 
#include "CandData/CandRecord.h"
#include "CandData/CandHeader.h"
#include "Candidate/CandContext.h"

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

#include "Validity/VldContext.h"
#include "Validity/VldTimeStamp.h"

//
// $Log $
//

ClassImp(DataQualityReader)

CVSID("$Id: DataQualityReader.cxx,v 1.3 2007/03/02 19:44:48 blake Exp $");

JOBMODULE(DataQualityReader,"DataQualityReader","DataQualityReader");

DataQualityReader::DataQualityReader() : 
  fRecord(0),
  fSnarl(0),
  fFilter(0),
  fCounter(0),
  fDQHeader(0),
  fDQRawDigits(0),
  fDQHotColdElectronics(0),
  fDQLightInjection(0),
  fDQSpillServer(0),
  fWriteDataQuality(0),
  fWriteDeadChips(0),
  fFilterOnOff(0),
  fFilterBadDataQuality(0),
  fFilterLowMultiplicity(0),
  fFilterHighMultiplicity(0),
  fFilterWord(0),
  fDataQualityFile(0),
  fDataQualityTree(0),
  fDeadChipFile(0),
  fDeadChipTree(0)
{
  MSG("DataQuality",Msg::kDebug) << " *** DataQualityReader::DataQualityReader() *** " << endl;

  // Create the monitoring objects which extract the 
  // monitoring information from the raw data blocks

  fDQHeader = new DQHeader();
  fDQRawDigits = new DQRawDigits();
  fDQHotColdElectronics = new DQHotColdElectronics();
  fDQLightInjection = new DQLightInjection();
  fDQSpillServer = new DQSpillServer();
}

DataQualityReader::~DataQualityReader()
{
  MSG("DataQuality",Msg::kInfo) << " *** DataQualityReader::~DataQualityReader() *** " << endl;

  delete fDQHeader;
  delete fDQRawDigits;
  delete fDQHotColdElectronics;
  delete fDQLightInjection;
  delete fDQSpillServer;
}

void DataQualityReader::BeginJob()
{
  MSG("DataQuality",Msg::kInfo) << " *** DataQualityReader::BeginJob() *** " << endl;

}
                     
JobCResult DataQualityReader::Reco(MomNavigator* mom)
{
  MSG("DataQuality",Msg::kInfo) << " *** DataQualityReader::Reco(...) *** " << endl;
 
  JobCResult result(JobCResult::kPassed);

  VldContext vldc;
  TObject* momobject = 0;
  Int_t run,snarl;

  fRecord=0;
  fSnarl=0;
  fFilter=0;

  // ===================
  // PROCESS RAW RECORDS
  // ===================
  // Iterate over raw records in mom and extract 
  // monitoring information from raw data blocks

  TIter momitr(mom->FragmentIter());
  while((momobject = momitr())){
    if(momobject->InheritsFrom("RawRecord")){
      fRecord=1;
      fCounter++;
      MSG("DataQuality",Msg::kInfo) << "  *** RAW RECORD [" << fCounter << "] ***  " << endl;
      RawRecord* rawrec = dynamic_cast<RawRecord*>(momobject);
 
      // Get the raw header and extract validity context
      vldc = rawrec->GetRawHeader()->GetVldContext();
      this->ProcessHeader((TObject*)(rawrec->GetRawHeader()));
      
      // Iterate over the raw blocks in raw record
      TIter rawrecitr = rawrec->GetRawBlockIter();
      TObject* tob = 0;
      while((tob = rawrecitr())){
        MSG("DataQuality",Msg::kDebug) << " " << tob->GetName() << endl;
        this->ProcessBlock(tob);
      }  

    }
  }

  if( !fRecord ){
    MSG("DataQuality",Msg::kWarning) << "  *** FAILED TO FIND RAW RECORD ***  " << endl;
    return result.SetFailed();
  }

  // ===========================
  // MAKE DATA QUALITY CANDIDATE
  // ===========================
  // If the raw record contains a snarl then create a new
  // data quality candidate and add it to the CandRecord  

  if( fSnarl ){
    MSG("DataQuality",Msg::kInfo) << "  *** MAKING DATA QUALITY CANDIDATE *** " << endl;
    run=fDQHeader->GetRun();
    snarl=fDQHeader->GetSnarl();

    // Get the CandRecord 
    CandRecord* candrec = dynamic_cast<CandRecord*>(mom->GetFragment("CandRecord", "PrimaryCandidateRecord"));
    if(candrec==0) {
      MSG("DataQuality",Msg::kInfo) << "  *** MAKING NEW CANDRECORD (" << run << "," << snarl << ") ***" << endl;
      CandHeader *head = new CandHeader(vldc,run,snarl);
      candrec = new CandRecord(head);
      candrec->SetName("PrimaryCandidateRecord");
      candrec->SetTitle("Created from RawRecord.");
      mom->AdoptFragment(candrec);
    }

    AlgFactory &af = AlgFactory::GetInstance();
    AlgHandle ah = af.GetAlgHandle("AlgDataQuality", "default"); 

    // Package up the monitoring objects and 
    // pass them to the data quality algorithm
    TObjArray* mycx = new TObjArray();
    mycx->Add(fDQHeader);
    mycx->Add(fDQRawDigits);
    mycx->Add(fDQHotColdElectronics);
    mycx->Add(fDQLightInjection);
    mycx->Add(fDQSpillServer);

    CandContext cx(this, mom);
    cx.SetCandRecord(candrec);
    cx.SetDataIn(mycx);
    CandDataQualityHandle dataquality = CandDataQuality::MakeCandidate(ah,cx);
    dataquality.SetName("CandDataQualityHandle");
    dataquality.SetTitle(TString("Created by CandDataQualityReader"));

    candrec->SecureCandHandle(dataquality);

    delete mycx;
  }

  // APPLY DATA QUALITY FILTER
  // =========================
  // apply filter based on data quality status bits

  if( fFilterOnOff ){
    MSG("DataQuality",Msg::kDebug) << " *** DATA QUALITY FILTER *** " << endl;

    if( fSnarl ){
      CandRecord* candrec = dynamic_cast<CandRecord*>(mom->GetFragment("CandRecord", "PrimaryCandidateRecord"));
      if( candrec ){
        CandDataQualityHandle* cdh = dynamic_cast<CandDataQualityHandle*>(candrec->FindCandHandle("CandDataQualityHandle"));
        if( cdh ){
          fFilter = this->ApplyFilter(cdh);
        }
      }
    }
          
    if( fFilter ){
      MSG("DataQuality",Msg::kDebug) << "   *** PASSED FILTER *** " << endl;
      result.SetPassed();
    }
    else{
      MSG("DataQuality",Msg::kDebug) << "   *** FAILED FILTER *** " << endl;
      result.SetFailed();
    }
  }

  MSG("DataQuality",Msg::kInfo) << " *** DataQualityReader::Reco(...) FINISHED *** " << endl;

  return result;
}

JobCResult DataQualityReader::Ana(const MomNavigator* mom)
{
  MSG("DataQuality",Msg::kDebug) << " *** DataQualityReader::Ana(...) *** " << endl;

  JobCResult result(JobCResult::kPassed);

  // Write out data quality and/or dead chip info

  CandRecord* candrec = dynamic_cast<CandRecord*>(mom->GetFragment("CandRecord", "PrimaryCandidateRecord"));
  if( candrec ){
    CandDataQualityHandle* cdh = dynamic_cast<CandDataQualityHandle*>(candrec->FindCandHandle("CandDataQualityHandle"));
    if( cdh ){

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

      if(fWriteDataQuality && !fDataQualityFile){
        TString mystring("dataquality.root");
        TDirectory* tmpd = gDirectory;
        fDataQualityFile = new TFile(mystring.Data(),"RECREATE");
        fDataQualityTree = new TTree("DataQuality","DataQuality");
        fDataQualityTree->SetAutoSave(300);
        fDataQualityTree->Branch("Time",&Time,"Time/I");
        fDataQualityTree->Branch("RunType",&RunType,"RunType/I");
        fDataQualityTree->Branch("Run",&Run,"Run/I");
        fDataQualityTree->Branch("SubRun",&SubRun,"SubRun/I");
        fDataQualityTree->Branch("TimeFrame",&TimeFrame,"TimeFrame/I");
        fDataQualityTree->Branch("Snarl",&Snarl,"Snarl/I");
        fDataQualityTree->Branch("TriggerSource",&TriggerSource,"TriggerSource/I");
        fDataQualityTree->Branch("TriggerTime",&TriggerTime,"TriggerTime/I");
        fDataQualityTree->Branch("ErrorCode",&ErrorCode,"ErrorCode/I");
        fDataQualityTree->Branch("CrateMask",&CrateMask,"CrateMask/I");
        fDataQualityTree->Branch("PreTriggerDigits",&PreTriggerDigits,"PreTriggerDigits/I");
        fDataQualityTree->Branch("PostTriggerDigits",&PostTriggerDigits,"PostTriggerDigits/I");
        fDataQualityTree->Branch("SnarlMultiplicity",&SnarlMultiplicity,"SnarlMultiplicity/I");
        fDataQualityTree->Branch("SpillStatus",&SpillStatus,"SpillStatus/I");
        fDataQualityTree->Branch("SpillType",&SpillType,"SpillType/I");
        fDataQualityTree->Branch("SpillTimeError",&SpillTimeError,"SpillTimeError/I");
        fDataQualityTree->Branch("LiTrigger",&LiTrigger,"LiTrigger/I");
        fDataQualityTree->Branch("LiTime",&LiTime,"LiTime/I");
        fDataQualityTree->Branch("LiSubtractedTime",&LiSubtractedTime,"LiSubtractedTime/I");
        fDataQualityTree->Branch("LiRelativeTime",&LiRelativeTime,"LiRelativeTime/I");
        fDataQualityTree->Branch("LiCalibPoint",&LiCalibPoint,"LiCalibPoint/I");
        fDataQualityTree->Branch("LiCalibType",&LiCalibType,"LiCalibType/I");
        fDataQualityTree->Branch("LiPulserBox",&LiPulserBox,"LiPulserBox/I");
        fDataQualityTree->Branch("LiPulserLed",&LiPulserLed,"LiPulserLed/I");
        fDataQualityTree->Branch("LiPulseHeight",&LiPulseHeight,"LiPulseHeight/I");
        fDataQualityTree->Branch("LiPulseWidth",&LiPulseWidth,"LiPulseWidth/I");
        fDataQualityTree->Branch("ColdChips",&ColdChips,"ColdChips/I");
        fDataQualityTree->Branch("HotChips",&HotChips,"HotChips/I");
        fDataQualityTree->Branch("BusyChips",&BusyChips,"BusyChips/I");
        fDataQualityTree->Branch("ReadoutErrors",&ReadoutErrors,"ReadoutErrors/I");
        fDataQualityTree->Branch("DataQuality",&DataQuality,"DataQuality/I");
        gDirectory = tmpd;
      }

      if(fWriteDataQuality && fDataQualityFile){
        TDirectory* tmpd = gDirectory;
        fDataQualityFile->cd();
        fDataQualityTree->Fill();
        gDirectory = tmpd;
      }
      
      TIter deadchipitr(cdh->GetDaughterIterator());
      while(CandDeadChipHandle* deadchip = dynamic_cast<CandDeadChipHandle*>(deadchipitr())){
        if( deadchip ){
        
          Entries=deadchip->GetEntries();
          ChannelId=deadchip->GetChannelId().GetChAdd();
          Adc=deadchip->GetAdc();
          Tdc=deadchip->GetTdc();
          ErrorCode=deadchip->GetErrorCode();
          TriggerRate=deadchip->GetTriggerRate();
          ChipStatus=deadchip->GetChipStatus();

          if(fWriteDeadChips && !fDeadChipFile){
            TString mystring("deadchips.root");
            TDirectory* tmpd = gDirectory;
            fDeadChipFile = new TFile(mystring.Data(),"RECREATE");
            fDeadChipTree = new TTree("DeadChips","DeadChips");
            fDeadChipTree->SetAutoSave(300);
            fDeadChipTree->Branch("Time",&Time,"Time/I");
            fDeadChipTree->Branch("Run",&Run,"Run/I");
            fDeadChipTree->Branch("SubRun",&SubRun,"SubRun/I");
            fDeadChipTree->Branch("TimeFrame",&TimeFrame,"TimeFrame/I");
            fDeadChipTree->Branch("Snarl",&Snarl,"Snarl/I");
            fDeadChipTree->Branch("Entries",&Entries,"Entries/I");
            fDeadChipTree->Branch("ChannelId",&ChannelId,"ChannelId/I");
            fDeadChipTree->Branch("Adc",&Adc,"Adc/I");
            fDeadChipTree->Branch("Tdc",&Tdc,"Tdc/I");
            fDeadChipTree->Branch("ErrorCode",&ErrorCode,"ErrorCode/I");
            fDeadChipTree->Branch("TriggerRate",&TriggerRate,"TriggerRate/I");
            fDeadChipTree->Branch("ChipStatus",&ChipStatus,"ChipStatus/I");
	    gDirectory = tmpd;
	  }

          if(fWriteDeadChips && fDeadChipFile){
            TDirectory* tmpd = gDirectory;
            fDeadChipFile->cd();
            fDeadChipTree->Fill();
            gDirectory = tmpd;
	  }
	}

      }
    }
  }

  return result;
}

const Registry& DataQualityReader::DefaultConfig() const
{
  MSG("DataQuality",Msg::kDebug) << " *** DataQualityReader::DefaultConfig() *** " << endl;

  static Registry r;
  r.SetName("DataQualityReader.config.default");
  r.UnLockValues();
  r.Set("WriteDataQuality",fWriteDataQuality);
  r.Set("WriteDeadChips",fWriteDeadChips);
  r.Set("FilterOnOff",fFilterOnOff);
  r.Set("FilterBadDataQuality",fFilterBadDataQuality);
  r.Set("FilterLowMultiplicity",fFilterLowMultiplicity);
  r.Set("FilterHighMultiplicity",fFilterHighMultiplicity);
  r.Set("FilterWord",fFilterWord);
  r.LockValues();
                   
  return r;
}

void DataQualityReader::Config(const Registry& r)
{
  MSG("DataQuality",Msg::kDebug) << " *** DataQualityReader::Config() *** " << endl;

  Int_t tmpint;
  Int_t tmpFilterWord;
    
  if(r.Get("WriteDataQuality",tmpint)) fWriteDataQuality = tmpint;
  if(r.Get("WriteDeadChips",tmpint)) fWriteDeadChips = tmpint;
  if(r.Get("FilterOnOff",tmpint)) fFilterOnOff = tmpint;

  if(r.Get("FilterBadDataQuality",tmpint)){ 
    if(tmpint){
      tmpFilterWord = fFilterWord;
      fFilterWord |= CandDataQuality::kBad; 
      MSG("DataQuality",Msg::kDebug) << "  Filter bad data quality : " << tmpFilterWord << "|CandDataQuality::kBad = " << fFilterWord << endl; 
    }
  }

  if(r.Get("FilterLowMultiplicity",tmpint)){ 
    if(tmpint){
      tmpFilterWord = fFilterWord;
      fFilterWord |= CandDataQuality::kLowMultiplicity; 
      MSG("DataQuality",Msg::kDebug) << "  Filter low multiplicity : " << tmpFilterWord << "|CandDataQuality::kLowMultiplicity = " << fFilterWord << endl; 
    }
  }

  if(r.Get("FilterHighMultiplicity",tmpint)){ 
    if(tmpint){
      tmpFilterWord = fFilterWord;
      fFilterWord |= CandDataQuality::kHighMultiplicity; 
      MSG("DataQuality",Msg::kDebug) << "  Filter high multiplicity : " << tmpFilterWord << "|CandDataQuality::kHighMultiplicity = " << fFilterWord << endl; 
    }
  }

  if(r.Get("FilterWord",tmpint)){
    if(tmpint){
      tmpFilterWord = fFilterWord;
      fFilterWord |= tmpint; 
      MSG("DataQuality",Msg::kDebug) << "  Filter word : " << tmpFilterWord << "|" << tmpint << " = " << fFilterWord << endl; 
    }
  }

  MSG("DataQuality",Msg::kDebug)
    << "  configuration: " << endl
    << "     WriteDataQuality=" << fWriteDataQuality << endl
    << "     WriteDeadChips=" << fWriteDeadChips << endl
    << "     FilterOnOff=" << fFilterOnOff << endl
    << "     FilterWord=" << fFilterWord << endl;

  return;
}

void DataQualityReader::HandleCommand(JobCommand* command)
{
  TString cmd = command->PopCmd();
  if(cmd=="Set"){
    TString opt = command->PopOpt();

  }
}

void DataQualityReader::EndJob()
{
  MSG("DataQuality",Msg::kInfo) << " *** DataQualityReader::EndJob() *** " << endl;

  if(fDataQualityFile){
    MSG("DataQuality",Msg::kInfo) << " *** saving data quality info to file ... " << endl;
    TDirectory* tmpd = gDirectory;
    fDataQualityFile->cd();
    fDataQualityTree->Write();
    fDataQualityFile->Close();
    gDirectory = tmpd;
    MSG("DataQuality",Msg::kInfo) << "      ... data quality info saved to file *** " << endl;
  }

  if(fDeadChipFile){
    MSG("DataQuality",Msg::kInfo) << " *** saving dead chip info to file ... " << endl;
    TDirectory* tmpd = gDirectory;
    fDeadChipFile->cd();
    fDeadChipTree->Write();
    fDeadChipFile->Close();
    gDirectory = tmpd;
    MSG("DataQuality",Msg::kInfo) << "      ... dead chip info saved to file *** " << endl;
  }
}

void DataQualityReader::ProcessHeader(TObject* obj)
{

  // DAQ HEADER
  // ==========
  if(obj->InheritsFrom("RawDaqHeader")){
    MSG("DataQuality",Msg::kDebug) << " ... found RawDaqHeader " << endl;
    RawDaqHeader* hdr = (RawDaqHeader*)(obj);
    fDQHeader->Process(hdr);
    fDQRawDigits->Process(hdr);
  }

  // SNARL HEADER
  // ============
  if(obj->InheritsFrom("RawDaqSnarlHeader")){
    MSG("DataQuality",Msg::kDebug) << " ... found RawDaqSnarlHeader " << endl;
    RawDaqSnarlHeader* hdr = (RawDaqSnarlHeader*)(obj);
    fDQHeader->Process(hdr);
    fDQRawDigits->Process(hdr);
  }
}

void DataQualityReader::ProcessBlock(TObject* obj)
{

  // HEADER BLOCKS
  // =============
  if(obj->InheritsFrom("RawDaqHeaderBlock")){
    MSG("DataQuality",Msg::kDebug) << " ... found RawDaqHeaderBlock " << endl;
    RawDaqHeaderBlock* rdb = (RawDaqHeaderBlock*)(obj);
    fDQHeader->Process(rdb);
    fDQRawDigits->Process(rdb);
  }

  if(obj->InheritsFrom("RawSnarlHeaderBlock")){
    MSG("DataQuality",Msg::kDebug) << " ... found RawSnarlHeaderBlock " << endl;
    RawSnarlHeaderBlock* rdb = (RawSnarlHeaderBlock*)(obj);
    fDQHeader->Process(rdb);
    fDQRawDigits->Process(rdb);
  }

  // MONITORING BLOCKS
  // =================
  if(obj->InheritsFrom("RawTpSinglesSummaryBlock")){
    MSG("DataQuality",Msg::kDebug) << " ... found RawTpSinglesSummaryBlock " << endl;
    RawTpSinglesSummaryBlock* rdb = (RawTpSinglesSummaryBlock*)(obj);
    fDQHotColdElectronics->Process(rdb);
  }

  if(obj->InheritsFrom("RawSpillServerMonitorBlock")){
    MSG("DataQuality",Msg::kDebug) << " ... found RawSpillServerMonitorBlock " << endl;
    RawSpillServerMonitorBlock* rdb = (RawSpillServerMonitorBlock*)(obj);
    fDQSpillServer->Process(rdb);
  }

  if(obj->InheritsFrom("RawLiTpmtDigitsBlock")){
    MSG("DataQuality",Msg::kDebug) << " ... found RawLiTpmtDigitsBlock " << endl;
    RawLiTpmtDigitsBlock* rdb = (RawLiTpmtDigitsBlock*)(obj);
    fDQLightInjection->Process(rdb);
  }

  // LIGHT INJECTION BLOCKS
  // ======================
  if(obj->InheritsFrom("RawLIAdcSummaryBlock")){
    MSG("DataQuality",Msg::kDebug) << " ... found RawLIAdcSummaryBlock " << endl;
    RawLIAdcSummaryBlock* rdb = (RawLIAdcSummaryBlock*)(obj);
    fDQLightInjection->Process(rdb);
  }

  // RAW DATA BLOCKS
  // ===============
  if(obj->InheritsFrom("RawDigitDataBlock")){
    MSG("DataQuality",Msg::kDebug) << " ... found RawDigitDataBlock " << endl;
    RawDigitDataBlock* rdb = (RawDigitDataBlock*)(obj);
    fDQRawDigits->Process(rdb);
    fSnarl=1;
  }

}

Bool_t DataQualityReader::ApplyFilter(CandDataQualityHandle* cdh)
{
  // APPLY DATA QUALITY FILTER
  // =========================
  // pass events if:
  //  (a) no filter word
  //  (b) no problem with data quality
  //  (c) filter mask doesn't match quality bits

  Bool_t filter=0;

  if( ( fFilterWord==0 || cdh->GetDataQuality()==0 )
   || ( (cdh->GetDataQuality()&fFilterWord)==0 ) ) filter=1;

  MSG("DataQuality",Msg::kVerbose) << "  APPLY FILTER [filterword=" << fFilterWord << ", quality=" << cdh->GetDataQuality() << ", filter=" << filter << "]" << endl;

  return filter;
}
