#include "FillFarRunQuality.h"

#include "RunQuality/DbuFarRunQuality.h"

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

#include "DatabaseInterface/DbiResultPtr.h"
#include "DatabaseInterface/DbiSqlContext.h"
#include "DatabaseInterface/DbiValidityRec.h"
#include "DatabaseInterface/DbiCascader.h"
#include "DatabaseInterface/DbiTableProxyRegistry.h"
#include "DatabaseInterface/DbiWriter.h"

#include "RawData/RawRecord.h"
#include "RawData/RawRecord.h"
#include "RawData/RawHeader.h"
#include "RawData/RawDaqHeader.h"
#include "RawData/RawDaqSnarlHeader.h"
#include "RawData/RawDaqHeaderBlock.h"
#include "RawData/RawSnarlHeaderBlock.h"
#include "RawData/RawRunStartBlock.h"
#include "RawData/RawRunEndBlock.h"
#include "RawData/RawSubRunEndBlock.h"
#include "RawData/RawRunConfigBlock.h"
#include "RawData/RawConfigFilesBlock.h"
#include "RawData/RawTpSinglesSummaryBlock.h"

#include "OnlineUtil/mdTriggerCodes.h"
#include "OnlineUtil/mdRunTypeCodes.h"

#include <cmath>

//
// $Log $
//

ClassImp(FillFarRunQuality)

CVSID("$Id: FillFarRunQuality.cxx,v 1.4 2008/01/11 17:31:59 bspeak Exp $");

JOBMODULE(FillFarRunQuality,"FillFarRunQuality","FillFarRunQuality");

FillFarRunQuality::FillFarRunQuality() :
  fTime(-1),
  fRecord(0),
  fCounter(-1),
  fGoodCounter(0),
  fHvStatus(-1),
  fHvStatusSM1(-1),
  fHvStatusSM2(-1),
  fReadoutStatus(-1),
  fTimeFrame(-1),
  fSource(-1),
  fRun(-1),
  fSubRun(-1),
  fRunType(-1),
  fRopMask(-1),
  fCrateMask(-1),
  fTriggerMask(-1),
  fStartTime(-1),
  fEndTime(-1),
  fTimeFrames(0),
  fSnarls(0),
  fGoodSnarls(0),
  fSnarlRateMin(-1),
  fSnarlRateMax(-1),
  fSnarlRateMean(-999.9),
  fSnarlRateMedian(-999.9),
  fTriggersPlane(0),
  fTriggersEnergy(0),
  fTriggersSpill(0),
  fTriggersRealSpill(0),
  fTriggersFakeSpill(0),
  fPhysicsRun(-1),
  fModifiedRun(-1),
  fTestRun(-1),
  fGoodRun(-1),
  fRawReadout(0)
{
  MSG("FarRunQuality",Msg::kDebug) << " *** FillFarRunQuality::FillFarRunQuality() *** " << endl;

  fRunQualityFile = 0;
  fRunQualityTree = 0;
  fRunQualityFileName = "dburunquality.root";

  fWriteOutResults = 0;
  fWriteToDatabase = 0;
  fReadFromDatabase = 0;

  fSnarlRates = new Int_t[1001];
  fCrateRates = new Int_t[17];

  fRawReadout = new RawReadout();

  this->Reset();
}

FillFarRunQuality::~FillFarRunQuality()
{
  MSG("FarRunQuality",Msg::kDebug) << " *** FillFarRunQuality::~FillFarRunQuality() *** " << endl;

  delete [] fSnarlRates;
  delete [] fCrateRates;
  if( fRawReadout ) delete fRawReadout;
}

void FillFarRunQuality::BeginJob()
{
  MSG("FarRunQuality",Msg::kInfo) << " *** FillFarRunQuality::BeginJob(...) *** " << endl;

}

JobCResult FillFarRunQuality::Get(MomNavigator *mom) 
{
  MSG("FarRunQuality",Msg::kDebug) << " *** FillFarRunQuality::Get(...) *** " << endl;

  JobCResult result(JobCResult::kPassed);

  VldContext vldc;
  TObject* momobject = 0;
  Bool_t foundit = 0;

  // PROCESS RAW RECORD
  // ==================
  TIter momitr(mom->FragmentIter());
  while((momobject = momitr())){
    if(momobject->InheritsFrom("RawRecord")){
      foundit = 1;
      fRecord++;
      MSG("FarRunQuality",Msg::kDebug) << "  *** RAW RECORD [" << fRecord << "] ***  " << endl;
      RawRecord* rawrec = dynamic_cast<RawRecord*>(momobject);
  
      // Get the raw header and extract validity context
      // Process the raw header (ProcessHeader)
      vldc = rawrec->GetRawHeader()->GetVldContext();
      this->ProcessHeader((TObject*)(rawrec->GetRawHeader()));
       
      // Iterate over the raw blocks in raw record
      // Process the raw block (ProcessBlock)
      TIter rawrecitr = rawrec->GetRawBlockIter();
      TObject* tob = 0;
      while((tob = rawrecitr())){
        MSG("FarRunQuality",Msg::kDebug) << " " << tob->GetName() << endl;
        this->ProcessBlock(tob);
      }                                  
    }
  }
             
  if( !foundit ){
    MSG("FarRunQuality",Msg::kWarning) << "  *** FAILED TO FIND RAW RECORD ***  " << endl;
    return result.SetFailed();
  }

  return result;
}

// JobCResult FillFarRunQuality::Ana(const MomNavigator *mom) 
// {
//   MSG("FarRunQuality",Msg::kInfo) << " *** FillFarRunQuality::Ana(...) *** " << endl;
//
//   return JobCResult::kPassed;
// }

const Registry& FillFarRunQuality::DefaultConfig() const
{
  MSG("FarRunQuality",Msg::kDebug) << " *** FillFarRunQuality::DefaultConfig() *** " << endl;
 
  static Registry r;
  r.SetName("FillFarRunQuality.config.default");
  r.UnLockValues();
  r.Set("RunQualityFileName",fRunQualityFileName.Data());
  r.Set("WriteToDatabase",fWriteToDatabase);
  r.Set("ReadFromDatabase",fReadFromDatabase);
  r.Set("WriteOutResults",fWriteOutResults);
  r.LockValues();
                    
  return r;
}
  
void FillFarRunQuality::Config(const Registry& r)
{
  MSG("FarRunQuality",Msg::kDebug) << " *** FillFarRunQuality::Config() *** " << endl;

  Int_t tmpint; const char* tmpchar = 0;

  if(r.Get("RunQualityFileName",tmpchar)) fRunQualityFileName = tmpchar;
  if(r.Get("WriteToDatabase",tmpint)) fWriteToDatabase = tmpint;
  if(r.Get("ReadFromDatabase",tmpint)) fReadFromDatabase = tmpint;
  if(r.Get("WriteOutResults",tmpint)) fWriteOutResults = tmpint;

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

  }
}

void FillFarRunQuality::EndJob() 
{
  MSG("FarRunQuality",Msg::kInfo) << " *** FillFarRunQuality::EndJob() *** " << endl;

  // process last subrun
  // ===================
  this->EndSubRun();

  // writing out file
  // ================
  if( fWriteOutResults ){
    this->WriteOutFile();
  }
}

void FillFarRunQuality::WriteToDatabase()
{
  MSG("FarRunQuality",Msg::kDebug) << " *** FillFarRunQuality::WriteToDatabase() *** " << endl;

  DbiCascader& cascader = DbiTableProxyRegistry::Instance().GetCascader();

  if( !cascader.TableExists("DBUFARRUNQUALITY") ){
    MSG("FarRunQuality",Msg::kDebug) << "    Creating temporary DBUFARRUNQUALITY table " << endl;

    string tableDescr = "(SEQNO int, RUN int, SUBRUN int, RUNTYPE int, ROPMASK int, CRATEMASK int, TRIGGERMASK int, STARTTIME timestamp, ENDTIME timestamp, TIMEFRAMES int, SNARLS int, GOODSNARLS int, MINSNARLRATE int, MAXSNARLRATE int, MEANSNARLRATE float, MEDIANSNARLRATE float, PLANETRIGGERS int, ENERGYTRIGGERS int, SPILLTRIGGERS int, SPILLTRIGGERSREAL int, SPILLTRIGGERSFAKE int, PHYSICSRUN int, MODIFIEDRUN int, TESTRUN int)";
      
    Int_t dbNoTempDb = cascader.CreateTemporaryTable("DBUFARRUNQUALITY",tableDescr);

    MSG("FarRunQuality",Msg::kDebug) << "    DbNoTempDb=" << dbNoTempDb << endl;
  }

  if( cascader.TableExists("DBUFARRUNQUALITY") ){
    MSG("FarRunQuality",Msg::kVerbose) << "    Writing to DBUFARRUNQUALITY table... " << endl;

    VldTimeStamp start(fStartTime,0);
    VldTimeStamp end(fEndTime,0);
    TString logentry("Run Quality (Run=");
    logentry+=fRun;
    logentry.Append("/");
    logentry+=fSubRun;
    logentry.Append(")");

    MSG("FarRunQuality",Msg::kVerbose) << "     run: " << fRun << "/" << fSubRun << endl; 
    MSG("FarRunQuality",Msg::kVerbose) << "     start time: " << start << endl;
    MSG("FarRunQuality",Msg::kVerbose) << "     end time: " << end << endl;
    MSG("FarRunQuality",Msg::kVerbose) << "     log entry: " << logentry.Data() << endl;

    VldRange range(Detector::kFar,SimFlag::kData,start,end,logentry.Data());

    DbuFarRunQuality row( fRun,fSubRun,fRunType,
                      fRopMask,fCrateMask,fTriggerMask, 
                      start,end,fTimeFrames,
                      fSnarls,fGoodSnarls,
                      fSnarlRateMin,fSnarlRateMax, 
		      fSnarlRateMean,fSnarlRateMedian,
		      fTriggersPlane,fTriggersEnergy, 
		      fTriggersSpill,fTriggersRealSpill,fTriggersFakeSpill,
		      fPhysicsRun,fModifiedRun,fTestRun );
    fGoodRun = row.GetGoodRun();

    Int_t aggNo = row.GetAggregateNo();
    Dbi::Task task = 0;
    VldTimeStamp creationDate;

    MSG("FarRunQuality",Msg::kVerbose) << "     aggNo: " << aggNo << " taskNo: " << task << endl;

    DbiWriter<DbuFarRunQuality> writer(range,aggNo,task,creationDate,0,logentry.Data());
    writer << row;
    writer.Close();

    MSG("FarRunQuality",Msg::kDebug) << endl
				  << " WRITING RESULTS: " << endl
                                  << " =============== " << endl
                                  << " Run=" << fRun << endl
                                  << " SubRun=" << fSubRun << endl
                                  << " RunType=" << fRunType << endl
                                  << " RopMask=" << fRopMask << endl
				  << " CrateMask=" << fCrateMask << endl
                                  << " TriggerMask=" << fTriggerMask << endl
                                  << " StartTime=" << fStartTime << endl
                                  << " EndTime=" << fEndTime << endl
                                  << " TimeFrames=" << fTimeFrames << endl
                                  << " Snarls=" << fSnarls << endl
                                  << " GoodSnarls=" << fGoodSnarls << endl
                                  << " MinSnarlRate=" << fSnarlRateMin << endl
                                  << " MaxSnarlRate=" << fSnarlRateMax << endl
                                  << " MeanSnarlRate=" << fSnarlRateMean << endl
                                  << " MedianSnarlRate=" << fSnarlRateMedian << endl
				  << " PlaneTriggers=" << fTriggersPlane << endl
				  << " EnergyTriggers=" << fTriggersEnergy << endl
				  << " SpillTriggers=" << fTriggersSpill << endl
				  << " SpillTriggersReal=" << fTriggersRealSpill << endl
				  << " SpillTriggersFake=" << fTriggersFakeSpill << endl
				  << " PhysicsRun=" << fPhysicsRun << endl
				  << " ModifiedRun=" << fModifiedRun << endl
					<< " TestRun=" << fTestRun << endl
				  << " GoodRun=" << fGoodRun << endl;

  }
}

void FillFarRunQuality::ReadFromDatabase()
{
  MSG("FarRunQuality",Msg::kDebug) << " *** FillFarRunQuality::ReadFromDatabase() *** " << endl;

  DbiCascader& cascader = DbiTableProxyRegistry::Instance().GetCascader();

  if( cascader.TableExists("DBUFARRUNQUALITY") ){
    MSG("FarRunQuality",Msg::kVerbose) << "    Reading from DBUFARRUNQUALITY table... " << endl;
    cout << "    Reading from DBUFARRUNQUALITY table... " << endl;
    
    Int_t time = (Int_t)(0.5*((double)fStartTime+(double)fEndTime));
    VldTimeStamp timestamp(time,0);
    VldContext vldc(Detector::kFar,SimFlag::kData,timestamp);
    Dbi::Task task = 0;

    DbiResultPtr<DbuFarRunQuality> resptr("DBUFARRUNQUALITY",vldc,task);

    MSG("FarRunQuality",Msg::kVerbose) << "    Number Of Rows = " << resptr.GetNumRows() << endl;

    if( resptr.GetNumRows()>0 ){
      for( unsigned int i=0; i<resptr.GetNumRows(); i++){
        const DbuFarRunQuality* rowptr = resptr.GetRow(i);
        fRun = rowptr->GetRun();
	fSubRun = rowptr->GetSubRun();
	fRunType = rowptr->GetRunType();
	fRopMask = rowptr->GetRopMask();
        fCrateMask = rowptr->GetCrateMask();
	fTriggerMask = rowptr->GetTriggerMask();

	fStartTime = rowptr->GetStartTime().GetSec();
	fEndTime = rowptr->GetEndTime().GetSec();
	fTimeFrames = rowptr->GetTimeFrames();
	fSnarls = rowptr->GetSnarls();
	fGoodSnarls = rowptr->GetGoodSnarls();
        fSnarlRateMin = rowptr->GetMinSnarlRate();
	fSnarlRateMax = rowptr->GetMaxSnarlRate();
	fSnarlRateMean = rowptr->GetMeanSnarlRate();
	fSnarlRateMedian = rowptr->GetMedianSnarlRate();
	fTriggersPlane = rowptr->GetPlaneTriggers();
	fTriggersEnergy = rowptr->GetEnergyTriggers();
	fTriggersSpill = rowptr->GetSpillTriggers();
	fTriggersRealSpill = rowptr->GetSpillTriggersReal();
	fTriggersFakeSpill = rowptr->GetSpillTriggersFake(); 
        fPhysicsRun = rowptr->GetPhysicsRun();
        fModifiedRun = rowptr->GetModifiedRun();
        fTestRun = rowptr->GetTestRun();
        fGoodRun = rowptr->GetGoodRun();

        MSG("FarRunQuality",Msg::kDebug) << endl
				  << " READING RESULTS: " << endl
                                  << " =============== " << endl
                                  << " Run=" << fRun << endl
                                  << " SubRun=" << fSubRun << endl
                                  << " RunType=" << fRunType << endl
                                  << " RopMask=" << fRopMask << endl
				  << " CrateMask=" << fCrateMask << endl
                                  << " TriggerMask=" << fTriggerMask << endl
                                  << " StartTime=" << fStartTime << endl
                                  << " EndTime=" << fEndTime << endl
                                  << " TimeFrames=" << fTimeFrames << endl
                                  << " Snarls=" << fSnarls << endl
                                  << " GoodSnarls=" << fGoodSnarls << endl
                                  << " MinSnarlRate=" << fSnarlRateMin << endl
                                  << " MaxSnarlRate=" << fSnarlRateMax << endl
                                  << " MeanSnarlRate=" << fSnarlRateMean << endl
                                  << " MedianSnarlRate=" << fSnarlRateMedian << endl
				  << " PlaneTriggers=" << fTriggersPlane << endl
				  << " EnergyTriggers=" << fTriggersEnergy << endl
				  << " SpillTriggers=" << fTriggersSpill << endl
				  << " SpillTriggersReal=" << fTriggersRealSpill << endl
				  << " SpillTriggersFake=" << fTriggersFakeSpill << endl
				  << " PhysicsRun=" << fPhysicsRun << endl
				  << " ModifiedRun=" << fModifiedRun << endl
				  << " TestRun=" << fTestRun << endl
				  << " GoodRun=" << fGoodRun << endl;
      }
    }
  }
}

void FillFarRunQuality::WriteOutResults()
{
  MSG("FarRunQuality",Msg::kDebug) << " *** FillFarRunQuality::WriteOutResults() *** " << endl;

  if( !fRunQualityFile ){
    MSG("FarRunQuality",Msg::kDebug) << "   opening file: " << fRunQualityFileName.Data() << endl;
    TDirectory* tmpd = gDirectory;
    fRunQualityFile = new TFile(fRunQualityFileName.Data(),"RECREATE");
    fRunQualityTree = new TTree("FarRunQuality","FarRunQuality");
    fRunQualityTree->SetAutoSave(100);
    fRunQualityTree->Branch("Source",&fSource,"Source/I");
    fRunQualityTree->Branch("Run",&fRun,"Run/I");
    fRunQualityTree->Branch("SubRun",&fSubRun,"SubRun/I");
    fRunQualityTree->Branch("RunType",&fRunType,"RunType/I");  
    fRunQualityTree->Branch("RopMask",&fRopMask,"RopMask/I");
    fRunQualityTree->Branch("CrateMask",&fCrateMask,"CrateMask/I");
    fRunQualityTree->Branch("TriggerMask",&fTriggerMask,"TriggerMask/I");
    fRunQualityTree->Branch("StartTime",&fStartTime,"StartTime/I");
    fRunQualityTree->Branch("EndTime",&fEndTime,"EndTime/I");
    fRunQualityTree->Branch("TimeFrames",&fTimeFrames,"TimeFrames/I");
    fRunQualityTree->Branch("Snarls",&fSnarls,"Snarls/I");
    fRunQualityTree->Branch("GoodSnarls",&fGoodSnarls,"GoodSnarls/I");
    fRunQualityTree->Branch("SnarlRateMin",&fSnarlRateMin,"MinSnarlRate/I");
    fRunQualityTree->Branch("SnarlRateMax",&fSnarlRateMax,"MaxSnarlRate/I");
    fRunQualityTree->Branch("SnarlRateMean",&fSnarlRateMean,"MeanSnarlRate/F");
    fRunQualityTree->Branch("SnarlRateMedian",&fSnarlRateMedian,"MedianSnarlRate/F");
    fRunQualityTree->Branch("PlaneTriggers",&fTriggersPlane,"PlaneTriggers/I");
    fRunQualityTree->Branch("EnergyTriggers",&fTriggersEnergy,"EnergyTriggers/I");
    fRunQualityTree->Branch("SpillTriggers",&fTriggersSpill,"SpillTriggers/I");
    fRunQualityTree->Branch("SpillTriggersReal",&fTriggersRealSpill,"SpillTriggersReal/I");
    fRunQualityTree->Branch("SpillTriggersFake",&fTriggersFakeSpill,"SpillTriggersFake/I");
    fRunQualityTree->Branch("PhysicsRun",&fPhysicsRun,"PhysicsRun/I");
    fRunQualityTree->Branch("ModifiedRun",&fModifiedRun,"ModifiedRun/I");
    fRunQualityTree->Branch("TestRun",&fTestRun,"TestRun/I");
    fRunQualityTree->Branch("GoodRun",&fGoodRun,"GoodRun/I");
    gDirectory = tmpd;
  }

  if( fRunQualityFile ){
    TDirectory* tmpd = gDirectory;
    fRunQualityFile->cd();
    fRunQualityTree->Fill();
    gDirectory = tmpd;
  }
}

void FillFarRunQuality::WriteOutFile()
{
  MSG("FarRunQuality",Msg::kDebug) << " *** FillFarRunQuality::WriteOutFile() *** " << endl;

  if( fRunQualityFile ){
    MSG("FarRunQuality",Msg::kDebug) << "   closing file: " << fRunQualityFileName.Data() << endl;
    TDirectory* tmpd = gDirectory;
    fRunQualityFile->cd();
    fRunQualityTree->Write();
    fRunQualityFile->Close();
    gDirectory = tmpd;
  }
}

void FillFarRunQuality::ProcessHeader(TObject* obj)
{
 
  // DAQ Header
  // ==========
  if(obj->InheritsFrom("RawDaqHeader")){
    MSG("FarRunQuality",Msg::kDebug) << " ... found RawDaqHeader " << endl;
    RawDaqHeader* hdr = (RawDaqHeader*)(obj); 

    Int_t time = hdr->GetVldContext().GetTimeStamp().GetSec();
    Int_t timeframe = hdr->GetTimeFrameNum();
    Int_t run = hdr->GetRun();
    Int_t subrun = hdr->GetSubRun();
    Int_t runtype = hdr->GetRunType();

    MSG("FarRunQuality",Msg::kDebug) << "   timeframe=" << timeframe << " time=" << time << endl;

    // New Run/SubRun
    if( (fRun>=0 && fSubRun>=0)
     && !(run==fRun && subrun==fSubRun) 
     && !(run==fRun && fEndTime-fStartTime<30) ){
      MSG("FarRunQuality",Msg::kDebug) << "   closing:" << fRun << "/"<< fSubRun << endl;
      this->EndSubRun();
      this->Reset();
    }

    // Record Run/SubRun
    if( fRun<0 || fSubRun<0 ){
      MSG("FarRunQuality",Msg::kDebug) << "   opening:" << run << "/"<< subrun << endl;
      fRun = run;
      fSubRun = subrun;
      fRunType = runtype;
    }

    // Record RunType
    if( fPhysicsRun<0 ){
      if( fRun>=0 ){      
        fPhysicsRun = 0;
        fModifiedRun = 0;
        fTestRun = 0;
        if( fRunType>=0 ){
          if( (fRunType==2) ) fPhysicsRun = 1;
          if( (fRunType&RUN_TYPE_PHYSICS)==(RUN_TYPE_PHYSICS) ) fPhysicsRun = 1;
          if( (fRunType&RUN_TYPE_MASK_MODIFIED)==(RUN_TYPE_MASK_MODIFIED) ) fModifiedRun = 1;
          if( (fRunType&RUN_TYPE_MASK_TEST)==(RUN_TYPE_MASK_TEST) ) fTestRun = 1;
	}
      }
    }
    
    // Record Time
    if( timeframe>=0 && time!=fTime ){

      if( time>fTime ){
        if( fStartTime<0 ) fStartTime = time;
        if( time>fEndTime ) fEndTime = time;
        if( fCounter>=0 ){
          if( fSnarlRateMin<0 || fCounter<fSnarlRateMin ) fSnarlRateMin = fCounter;
          if( fCounter>fSnarlRateMax ) fSnarlRateMax = fCounter;
          if( fCounter>1000 ) fCounter = 1000;
          fSnarls += fCounter;
          fGoodSnarls += fGoodCounter;
          fSnarlRates[fCounter]++;
          fTimeFrames++;
        }
      }

      fCounter = 0;
      fGoodCounter = 0;
      fTime = time;
      fTimeFrame = timeframe;
    }   

    MSG("FarRunQuality",Msg::kVerbose) << "   run=" << fRun << " subrun=" << fSubRun << " runtype=" << fRunType << " physics=" << fPhysicsRun << endl; 
  }
 
  // Snarl Header
  // ============
  if(obj->InheritsFrom("RawDaqSnarlHeader")){
    MSG("FarRunQuality",Msg::kDebug) << " ... found RawDaqSnarlHeader " << endl;
    RawDaqSnarlHeader* hdr = (RawDaqSnarlHeader*)(obj);
    
    Int_t time = hdr->GetVldContext().GetTimeStamp().GetSec();
    Int_t timeframe = hdr->GetTimeFrameNum();
    Int_t trigger = hdr->GetTrigSrc();
    Int_t digits = hdr->GetNumRawDigits();
    Int_t spilltype = hdr->GetRemoteSpillType();

    // Record Trigger Type
    Bool_t plane_trigger = ((trigger&TRIGGER_BIT_PLANE)==(TRIGGER_BIT_PLANE));
    Bool_t energy_trigger = ((trigger&TRIGGER_BIT_E4)==(TRIGGER_BIT_E4));
    Bool_t spill_trigger = ((trigger&TRIGGER_BIT_SPILL_IP)==(TRIGGER_BIT_SPILL_IP));
    Bool_t real_spill_trigger = 0; if( spill_trigger && spilltype==1 ) real_spill_trigger = 1;
    Bool_t fake_spill_trigger = 0; if( spill_trigger && spilltype==3 ) fake_spill_trigger = 1;

    MSG("FarRunQuality",Msg::kVerbose) << "   time=" << time << " timeframe=" << timeframe << endl;
    MSG("FarRunQuality",Msg::kVerbose) << "   trigger=" << trigger << " (PL,EN,SP)=(" << plane_trigger << "," << energy_trigger << "," << spill_trigger << ")" << endl;
   
    Bool_t good_trigger = 0;
    if( plane_trigger ) { good_trigger |= 1; fTriggersPlane++; }
    if( energy_trigger ){ good_trigger |= 1; fTriggersEnergy++; }
    if( spill_trigger ) { good_trigger |= 1; fTriggersSpill++; }
    if( real_spill_trigger ) { good_trigger |= 1; fTriggersRealSpill++; }
    if( fake_spill_trigger ) { good_trigger |= 1; fTriggersFakeSpill++; }

    Bool_t good_detector = 0;
    if( fHvStatus>0 ) good_detector = fHvStatus;

    Bool_t good_event = 0;
    if( digits>=0 && digits<1000 ) good_event = 1;
 
    // Count Good Snarls
    if( fCounter>=0 ){
      fCounter++;
      if( good_detector && good_trigger && good_event ) fGoodCounter++;
    }
  }

}

void FillFarRunQuality::ProcessBlock(TObject* obj)
{
 
  // Header Block
  // ============
  if(obj->InheritsFrom("RawDaqHeaderBlock")){
    MSG("FarRunQuality",Msg::kDebug) << " ... found RawDaqHeaderBlock " << endl;

    //////////////
    // RawDaqHeaderBlock* rdb = (RawDaqHeaderBlock*)(obj); 
  }

  // Snarl Header Block
  // ==================
  if(obj->InheritsFrom("RawSnarlHeaderBlock")){
    MSG("FarRunQuality",Msg::kDebug) << " ... found RawSnarlHeaderBlock " << endl;

    //////////////
    // RawSnarlHeaderBlock* rdb = (RawSnarlHeaderBlock*)(obj);
  }

  // Run Configuration
  // =================
  if(obj->InheritsFrom("RawRunConfigBlock")){
    MSG("FarRunQuality",Msg::kDebug) << " ... found RawRunConfigBlock " << endl;
    RawRunConfigBlock* rdb = (RawRunConfigBlock*)(obj);

    char* config;
    config = (char*)(rdb->GetRunConfig());  

    MSG("FarRunQuality",Msg::kVerbose) << " RUN_CONFIG_BLOCK: " << config << endl;

    char* word;
    int ctr = 0;
    bool cont = 1;
    word = strtok(config,";");

    while( cont ){
      MSG("FarRunQuality",Msg::kVerbose) << " (" << ctr << ") " << word << endl;

      // extract RopMask from run configuration
      if( strstr(word,"ropIdMask#") ){
        if( (word = strchr(word,'=')) ){
          if( strlen(word)>1 ){
            word=&(word[1]); 
            if( fRopMask<0 ) fRopMask = strtol(word,&word,0);
	  }
	}
      }

      // extract TriggerMask from  run configuration
      if( strstr(word,"triggerMask#") ){
        if( (word = strchr(word,'=')) ){
          if( strlen(word)>1 ){
            word=&(word[1]); 
            if( fTriggerMask<0 ) fTriggerMask = strtol(word,&word,0);
	  }
	}
      }

      cont = ( word = strtok(0,";") );
      ctr++;
    }

    // convert RopMask into CrateMask
    if( fRopMask>=0 ){
      Int_t cratemask = 0;
      for( Int_t n=0; n<16; n++){
        Int_t nbit = ( 1 << n );
	if( (fRopMask&nbit)==(nbit) ) cratemask++;
      }
      // (could record number of crates here)
      // fCrateMask = cratemask;
    }

    MSG("FarRunQuality",Msg::kDebug) << " RopMask=" << fRopMask << " TriggerMask=" << fTriggerMask << endl;
  }

  // Run Configuration Files
  // =======================
  if(obj->InheritsFrom("RawConfigFilesBlock")){
    MSG("FarRunQuality",Msg::kDebug) << " ... found RawConfigFilesBlock " << endl;
    RawConfigFilesBlock* rdb = (RawConfigFilesBlock*)(obj);

    char* config;
    config = (char*)(rdb->GetConfigFile()); 

    //////////////
    // MSG("FarRunQuality",Msg::kVerbose) << " RUN_CONFIG_FILE: " << config << endl;
  }

  // Run Start Block
  // ===============
  if(obj->InheritsFrom("RawRunStartBlock")){
    MSG("FarRunQuality",Msg::kDebug) << " ... found RawRunStartBlock " << endl;
    RawRunStartBlock* rdb = (RawRunStartBlock*)(obj);

    fStartTime = rdb->GetStartTime().GetSec();

    MSG("FarRunQuality",Msg::kDebug) << " StartTime=" << fStartTime << endl;
  }

  // Singles Summary Block
  // =====================
  if(obj->InheritsFrom("RawTpSinglesSummaryBlock")){
    MSG("FarRunQuality",Msg::kDebug) << " ... found RawTpSinglesSummaryBlock " << endl;
    RawTpSinglesSummaryBlock* rdb = (RawTpSinglesSummaryBlock*)(obj);

    Int_t time = rdb->GetStartTimeStamp().GetSec();
    Double_t frametime = (rdb->GetEndTimeStamp().GetSec()-rdb->GetStartTimeStamp().GetSec())+1.0e-9*(rdb->GetEndTimeStamp().GetNanoSec()-rdb->GetStartTimeStamp().GetNanoSec());

    Int_t source = rdb->GetSource();
    Int_t sourcecheck = 0;
    Int_t minrate = -99999;
    Int_t maxchips = +99999;

    Int_t allchips1 = 0;
    Int_t allchips2 = 0;
    Int_t coldchips1 = 0;
    Int_t coldchips2 = 0;

    Detector::Detector_t Detector = Detector::kUnknown;

    switch( source ){
      case 0: // Crate only
        Detector = Detector::kUnknown;
        break;
      case 1: // Crate and VARC (VA)
        Detector = Detector::kFar;
        break;
      case 2: // Crate and VFB/ADCSEL (VA)
        Detector = Detector::kFar;
        break;
      case 3: // Crate and VACHIP (VA)
        Detector = Detector::kFar;
        break;
      case 4: // Crate and Plane
        Detector = Detector::kUnknown;
        break;
      case 5: // Crate and MASTER (QIE)
        Detector = Detector::kNear;
        break;
      case 6: // Crate and MINDER (QIE)
        Detector = Detector::kNear;
        break;
      case 7: // Crate and MENU (QIE)
        Detector = Detector::kNear;
        break;
      default:
        break;
    }

    // check source type
    if( source==3 && Detector==Detector::kFar ){
      minrate = 50;
      maxchips = 20;
      sourcecheck = 1;
    }      

    if( source==7 && Detector==Detector::kNear ){
      minrate = -99999;
      maxchips = +99999;
      sourcecheck = 1;
    }

    // Iterate over raw channels and record any hot/cold chips
    typedef std::map<RawChannelId,UInt_t> RCIdToRateMap;
    const RCIdToRateMap& rates = rdb->GetRates();
    RCIdToRateMap::const_iterator rateItr = rates.begin();
    RCIdToRateMap::const_iterator rateEnd = rates.end();
    while ( rateItr != rateEnd ) {
      RawChannelId rawch = rateItr->first;
      Int_t rawrate = rateItr->second;
      Int_t correctedrate = (Int_t)(rawrate/frametime);
      Int_t rate = correctedrate;
      Int_t crate = rawch.GetCrate();

      RawReadout::ReadoutType_t readout = fRawReadout->GetReadoutType(rawch);
            
      if( ( sourcecheck )
       && ( readout==RawReadout::kDetector 
	 || readout==RawReadout::kVetoShield ) ){     

        // supermodule 1   
        if( crate>=0 && crate<8 ){
          allchips1++; 
          if( rate<minrate ){
            coldchips1++;
            MSG("FarRunQuality",Msg::kVerbose) << "   cold chip (SM1): crate=" << crate << " corrected rate=" << correctedrate << endl; 
	  }
	}

        // supermodule 2
        if( crate>=8 && crate<16 ){
          allchips2++; 
          if( rate<minrate ){
            coldchips2++;
            MSG("FarRunQuality",Msg::kVerbose) << "   cold chip (SM2): crate=" << crate << " corrected rate=" << correctedrate << endl; 
	  }
	}

      }

      rateItr++;
    }

    // record number of crates in readout
    Int_t ncrates = rdb->GetNumberOfCrates();
    Int_t ncrates_readout = 0;
 
    for( Int_t k=0; k<ncrates; k++ ){
      if( rdb->GetHitsPerSecByCrate(k)>0 ) ncrates_readout++;
    }  

    if( ncrates_readout>=0 && ncrates_readout<=16 ){
      fCrateRates[ncrates_readout]++;
    }
  
    MSG("FarRunQuality",Msg::kVerbose) << "   time=" << time << " frametime=" << frametime << endl;
    MSG("FarRunQuality",Msg::kVerbose) << "   crates=" << ncrates_readout << "/" << ncrates << endl;
    MSG("FarRunQuality",Msg::kVerbose) << "   source=" << source << " check=" << sourcecheck << endl;
    MSG("FarRunQuality",Msg::kVerbose) << "   minrate=" << minrate << " maxchips=" << maxchips << endl;
    MSG("FarRunQuality",Msg::kVerbose) << "   SM1: allchips=" << allchips1 << " coldchips=" << coldchips1 << endl;
    MSG("FarRunQuality",Msg::kVerbose) << "   SM2: allchips=" << allchips2 << " coldchips=" << coldchips2 << endl;

    fHvStatus = -1;
    fHvStatusSM1 = -1;
    fHvStatusSM2 = -1;
    fReadoutStatus = -1;

    // analyse singles data
    if( sourcecheck ){

      // supermodule 1
      if( allchips1>0 ){
        if( coldchips1<maxchips ) fHvStatusSM1 = 1;
        else fHvStatusSM1 = 0;
      }

      // supermodule 2
      if( allchips2>0 ){
        if( coldchips2<maxchips ) fHvStatusSM2 = 1;
        else fHvStatusSM2 = 0;
      }

      // determine HV status
      if( fHvStatusSM1==1 && fHvStatusSM2!=0 ) fHvStatus = 1;
      if( fHvStatusSM1!=0 && fHvStatusSM2==1 ) fHvStatus = 1;
      if( fHvStatusSM1==0 || fHvStatusSM2==0 ) fHvStatus = 0;

      // determine readout status
      if( ncrates_readout==16 ) fReadoutStatus = 1; 
      else fReadoutStatus = 0;
    }

    MSG("FarRunQuality",Msg::kDebug) << "   HV STATUS: SM1=" << fHvStatusSM1 << " SM2=" << fHvStatusSM2 << " overall=" << fHvStatus << endl;
    MSG("FarRunQuality",Msg::kDebug) << "   READOUT STATUS: " << fReadoutStatus << endl;
  }
}

void FillFarRunQuality::EndSubRun() 
{
  MSG("FarRunQuality",Msg::kDebug) << " *** FillFarRunQuality::EndSubRun() *** " << endl;

  // Calculate run quality parameters
  // ================================

  // calculate mean and median snarl rates
  if( fTimeFrames>0 ){
    Int_t oldtotal = 0;
    Int_t newtotal = 0;
    Int_t middletotal = (Int_t)(0.5*fTimeFrames);
  
    for(Int_t i=0; i<1001; i++){
      oldtotal = newtotal;
      newtotal += fSnarlRates[i];
      if( oldtotal<=middletotal && middletotal<newtotal ){
        fSnarlRateMedian = (float)i - 0.5;
        if( newtotal-oldtotal>0 ){
          fSnarlRateMedian += (float)(middletotal-oldtotal)/(float)(newtotal-oldtotal);
	}
      }
    }

    if( fSnarlRateMedian<fSnarlRateMin) fSnarlRateMedian=fSnarlRateMin;
    if( fSnarlRateMedian>fSnarlRateMax) fSnarlRateMedian=fSnarlRateMax;

    fSnarlRateMean = ((float)(fSnarls))/((float)(fTimeFrames));
  }

  // determine cratemask (number of crates reading out)
  // for cratemask=16 (full readout), require:
  //   >60 secs "good readout" OR <60 secs "bad readout".
  if( fTimeFrames>0 ){
    Int_t goodreadout = 0;
    Int_t badreadout = 0;
    for(Int_t i=0; i<=16; i++){
      if( i==16 ) goodreadout += fCrateRates[i]; 
      else badreadout += fCrateRates[i];
    }

    Int_t cratemask = -1;
    Int_t maxcratemask = 16;
    if( ( goodreadout>0 )
     && ( goodreadout>60 || badreadout<60 ) ) maxcratemask=16;
    else maxcratemask = 15;
    
    for(Int_t i=0; i<=maxcratemask; i++){
      if( fCrateRates[i]>0 ) cratemask = i;
    }

    fCrateMask = cratemask;
  }

  // determine whether this is a good run
  // physics runtype + no test bit set + full readout
  // run >= 300 seconds, subrun >= 120 seconds
  // good snarls >= 100
  // good snarl rate < 30 Hz
  // median snarl rate < 100 Hz
  // maximum snarl rate < 300 Hz
  fGoodRun = 0;
  if( ( fRun>=0 )
   && ( fPhysicsRun==1 && fTestRun==0 )
   && ( fCrateMask==16 )
   && ( fTimeFrames>=300 || (fSubRun>0 && fTimeFrames>120 ) )
   && ( fGoodSnarls>=100 && (double)fGoodSnarls/(double)fTimeFrames<30 )
   && ( fSnarlRateMedian<100 && fSnarlRateMax<300 ) ){
    fGoodRun = 1;
  }

  // print results
  // =============
  MSG("FarRunQuality",Msg::kVerbose) << " *** FillFarRunQuality::PrintResults() *** " << endl;

  MSG("FarRunQuality",Msg::kInfo) << endl
				  << " RESULTS: " << endl
                                  << " ======= " << endl
                                  << " Run=" << fRun << endl
                                  << " SubRun=" << fSubRun << endl
                                  << " RunType=" << fRunType << endl
                                  << " RopMask=" << fRopMask << endl
				  << " CrateMask=" << fCrateMask << endl
                                  << " TriggerMask=" << fTriggerMask << endl
                                  << " StartTime=" << fStartTime << endl
                                  << " EndTime=" << fEndTime << endl
                                  << " TimeFrames=" << fTimeFrames << endl
                                  << " Snarls=" << fSnarls << endl
                                  << " GoodSnarls=" << fGoodSnarls << endl
                                  << " MinSnarlRate=" << fSnarlRateMin << endl
                                  << " MaxSnarlRate=" << fSnarlRateMax << endl
                                  << " MeanSnarlRate=" << fSnarlRateMean << endl
                                  << " MedianSnarlRate=" << fSnarlRateMedian << endl
				  << " PlaneTriggers=" << fTriggersPlane << endl
				  << " EnergyTriggers=" << fTriggersEnergy << endl
				  << " SpillTriggers=" << fTriggersSpill << endl
				  << " SpillTriggersReal=" << fTriggersRealSpill << endl
				  << " SpillTriggersFake=" << fTriggersFakeSpill << endl
				  << " PhysicsRun=" << fPhysicsRun << endl
				  << " ModifiedRun=" << fModifiedRun << endl
				  << " TestRun=" << fTestRun << endl
				  << " GoodRun=" << fGoodRun << endl;  

  // write out results
  // =================
  this->WriteSubRun();

}

void FillFarRunQuality::WriteSubRun() 
{
  MSG("FarRunQuality",Msg::kDebug) << " *** FillFarRunQuality::WriteSubRun() *** " << endl;

  // write results to file
  // =====================
  if( fWriteOutResults ){
    fSource = 0;
    this->WriteOutResults();
  }

  // write results to database
  // =========================
  if( fWriteToDatabase ){
    this->WriteToDatabase();
    if( fWriteOutResults ){
      fSource = 1;
      this->WriteOutResults();
    }
  }
    
  // read results from database
  // ==========================
  if( fReadFromDatabase ){
    this->ReadFromDatabase();
    if( fWriteOutResults ){
      fSource = 2;
      this->WriteOutResults();
    }
  }

}

void FillFarRunQuality::Reset() 
{
  MSG("FarRunQuality",Msg::kDebug) << " *** FillFarRunQuality::Reset() *** " << endl;

  fTime = -1;
  fCounter = -1;
  fGoodCounter = 0;
  fHvStatus = -1;
  fHvStatusSM1 = -1;
  fHvStatusSM2 = -1;
  fReadoutStatus = -1;
  fTimeFrame = -1;
  fSource = -1;
  fRun = -1;
  fSubRun = -1;
  fRunType = -1;
  fRopMask = -1;
  fCrateMask = -1;
  fTriggerMask = -1;
  fStartTime = -1;
  fEndTime = -1;
  fTimeFrames = 0;
  fSnarls = 0;
  fGoodSnarls = 0;
  fSnarlRateMin = -1;
  fSnarlRateMax = -1;
  fSnarlRateMean = -999.9;
  fSnarlRateMedian = -999.9;
  fTriggersPlane = 0;
  fTriggersEnergy = 0;
  fTriggersSpill = 0;
  fTriggersRealSpill = 0;
  fTriggersFakeSpill = 0;
  fPhysicsRun = -1;
  fModifiedRun = -1;
  fTestRun = -1;
  fGoodRun = -1;

  for(Int_t i=0; i<17; i++){ fCrateRates[i]=0; }
  for(Int_t i=0; i<1001; i++){ fSnarlRates[i]=0; }
}
