#ifndef HstTimers_HH
#define HstTimers_HH

#include <cassert>

#include "TH1F.h"
#include "TH2F.h"
#include "TProfile.h"
#include "TRandom.h"

#include "RcdUserRO.hh"
#include "SubAccessor.hh"


class HstTimers : public RcdUserRO {

public:
  enum {
    maxMultiTimers=21
  };

  HstTimers(unsigned r=256+32+16, unsigned c=4) : _records(r), _configurations(c) {
    std::cout << "HstTimers::ctor() Records = "
	      << printHex(_records) << std::endl;
    std::cout << "           Configurations = "
	      << printHex(_configurations) << std::endl;
    _nConfiguration=0;

    if(_records.bit(RcdHeader::spillStart)) {
      _splHist[0]=new TH1F("HstTimersPollSpillRange0","SpillStart, Spill poll time",
			   100,0.0,0.01);
      _splHist[1]=new TH1F("HstTimersPollSpillRange1","SpillStart, Spill poll time",
			   100,0.0,0.1);
      _splHist[2]=new TH1F("HstTimersPollSpillRange2","SpillStart, Spill poll time",
			   100,0.0,1.0);
      _splHist[3]=new TH1F("HstTimersPollSpillRange3","SpillStart, Spill poll time",
			   100,0.0,20.0);
    }
    
    if(_records.bit(RcdHeader::trigger)) {
      _trgHist[0]=new TH1F("HstTimersPollTriggerRange0","Trigger, Trigger poll time",
			   100,0.0,0.01);
      _trgHist[1]=new TH1F("HstTimersPollTriggerRange1","Trigger, Trigger poll time",
			   100,0.0,0.1);
      _trgHist[2]=new TH1F("HstTimersPollTriggerRange2","Trigger, Trigger poll time",
			   100,0.0,1.0);
      _trgHist[3]=new TH1F("HstTimersPollTriggerRange3","Trigger, Trigger poll time",
			   100,0.0,20.0);
    }

    for(unsigned i(0);i<RcdHeader::endOfRecordTypeEnum;i++) {
      if(_records.bit(i)) {
	_rcdHist[i][0]=new TH1F((std::string("HstTimersRecord")+RcdHeader::recordTypeName((RcdHeader::RecordType)i)+"Range0").c_str(),
				(RcdHeader::recordTypeName((RcdHeader::RecordType)i)+", Record time").c_str(),
				100,0.0,0.01);
	_rcdHist[i][1]=new TH1F((std::string("HstTimersRecord")+RcdHeader::recordTypeName((RcdHeader::RecordType)i)+"Range1").c_str(),
				(RcdHeader::recordTypeName((RcdHeader::RecordType)i)+", Record time").c_str(),
				100,0.0,0.1);
	_rcdHist[i][2]=new TH1F((std::string("HstTimersRecord")+RcdHeader::recordTypeName((RcdHeader::RecordType)i)+"Range2").c_str(),
				(RcdHeader::recordTypeName((RcdHeader::RecordType)i)+", Record time").c_str(),
				100,0.0,1.0);
	_rcdHist[i][3]=new TH1F((std::string("HstTimersRecord")+RcdHeader::recordTypeName((RcdHeader::RecordType)i)+"Range3").c_str(),
				(RcdHeader::recordTypeName((RcdHeader::RecordType)i)+", Record time").c_str(),
				100,0.0,20.0);
      }
    }
  }

  virtual ~HstTimers() {
    std::cout << "In HstTimers::dtor()" << std::endl;
    /*
    for(unsigned i(0);i<RcdHeader::endOfRecordTypeEnum;i++) {
      for(unsigned j(0);j<3;j++) {
	for(unsigned k(0);k<_vHist[i][j].size();k++) {
	  delete _vHist[i][j][k];
	}
      }
    }
    */
  }

  bool record(const RcdRecord &r) {
    if(doPrint(r.recordType())) {
      std::cout << "HstTimers::record()" << std::endl;
      r.RcdHeader::print(std::cout," ") <<std::endl;
    }

    if(r.recordType()==RcdHeader::configurationEnd) _nConfiguration++;
    if(_configurations.bit(_nConfiguration%3)) {

      // Find time of previous record
      if(_records.bit(_lastHeader.recordType())) {
	double dt((r.recordTime()-_lastHeader.recordTime()).deltaTime());
	_rcdHist[_lastHeader.recordType()][0]->Fill(dt);
	_rcdHist[_lastHeader.recordType()][1]->Fill(dt);
	_rcdHist[_lastHeader.recordType()][2]->Fill(dt);
	_rcdHist[_lastHeader.recordType()][3]->Fill(dt);
      }
      
      // Now find timer information
      if(_records.bit(r.recordType())) {
	
	SubAccessor accessor(r);
	
	std::vector<const DaqTwoTimer* >
	  v(accessor.access<DaqTwoTimer>());
	
	for(unsigned i(0);i<v.size();i++) {
	  
	  // See if id is already known
	  unsigned k(_vId[r.recordType()].size());
	  for(unsigned j(0);j<_vId[r.recordType()].size();j++) {
	    if(_vId[r.recordType()][j]==v[i]->timerId()) k=j;
	  }
	  
	  // If not, add this to the list
	  if(k==_vId[r.recordType()].size()) {
	    _vId[r.recordType()].push_back(v[i]->timerId());
	    
	    for(unsigned l(0);l<4;l++) {
	      std::ostringstream sLabel;
	      sLabel << "HstTimers"
		     << "0x" << std::hex << std::setfill('0')
		     << std::setw(8) << v[i]->timerId() << std::dec
		     << r.recordTypeName() << "TwoRange" << l;

	      std::ostringstream sTitle;
	      sTitle << r.recordTypeName() << ", DaqTwoTimer Difference, Id=0x" << std::hex << std::setfill('0') << std::setw(8) << v[i]->timerId();
	      
	      /*
	      if(l==0) {
		r.RcdHeader::print(std::cout);
		std::cout << " Adding new histograms: " 
			  << sTitle.str() << std::endl;
	      }
	      */
	      
	      double upper(0.01);
	      if(l==1) upper=0.1;
	      if(l==2) upper=1.0;
	      if(l==3) upper=20.0;
	      
	      _vHist[r.recordType()][l].push_back(new TH1F(sLabel.str().c_str(),sTitle.str().c_str(),100,0.0,upper));
	    }
	  }
	
	  _vHist[r.recordType()][0][k]->Fill(v[i]->timeDifference().deltaTime());
	  _vHist[r.recordType()][1][k]->Fill(v[i]->timeDifference().deltaTime());
	  _vHist[r.recordType()][2][k]->Fill(v[i]->timeDifference().deltaTime());
	  _vHist[r.recordType()][3][k]->Fill(v[i]->timeDifference().deltaTime());
	}
	
	
	std::vector<const DaqMultiTimer* >
	  w(accessor.access<DaqMultiTimer>());
	
	for(unsigned i(0);i<w.size();i++) {
	  if(doPrint(r.recordType(),1))  w[i]->print(std::cout) << std::endl;

	  if(w[i]->numberOfTimers()>maxMultiTimers) {
	    std::cout << "Number of timers " << w[i]->numberOfTimers()
		      << " > " << maxMultiTimers << std::endl;
	  }

	  
	  // See if id is already known
	  unsigned k(_vMId[r.recordType()].size());
	  for(unsigned j(0);j<_vMId[r.recordType()].size();j++) {
	    if(_vMId[r.recordType()][j]==w[i]->timerId()) k=j;
	  }
	  
	  // If not, add this to the list
	  if(k==_vMId[r.recordType()].size()) {
	    _vMId[r.recordType()].push_back(w[i]->timerId());
	    
	    for(unsigned m(0);m<maxMultiTimers;m++) {
	      for(unsigned l(0);l<4;l++) {
		std::ostringstream sLabel;
		sLabel << "HstTimers"
		       << "0x" << std::hex << std::setfill('0')
		       << std::setw(8) << w[i]->timerId() << std::dec
		       << r.recordTypeName() 
		       << "Multi" << std::setw(2) << m << " " << "Range" << l;

		std::ostringstream sTitle;
		sTitle << r.recordTypeName() << ", DaqMultiTimer" << m << " Difference, Id=0x" << std::hex << std::setfill('0') << std::setw(8) << w[i]->timerId();
		
		/*
		if(l==0) {
		  r.RcdHeader::print(std::cout);
		  std::cout << " Adding new histograms: " 
			  << sTitle.str() << std::endl;
		}
		*/
		
		double upper(0.01);
		if(l==1) upper=0.1;
		if(l==2) upper=1.0;
		if(l==3) upper=20.0;
		
		_vMHist[r.recordType()][m][l].push_back(new TH1F(sLabel.str().c_str(),sTitle.str().c_str(),100,0.0,upper));
	      }
	    }
	  }
	  
	  for(unsigned m(0);m<w[i]->numberOfTimers() && m<maxMultiTimers;m++) {
	  _vMHist[r.recordType()][m][0][k]->Fill(w[i]->timeDifference(m).deltaTime());
	  _vMHist[r.recordType()][m][1][k]->Fill(w[i]->timeDifference(m).deltaTime());
	  _vMHist[r.recordType()][m][2][k]->Fill(w[i]->timeDifference(m).deltaTime());
	  _vMHist[r.recordType()][m][3][k]->Fill(w[i]->timeDifference(m).deltaTime());
	  }
	}
	
	// Special cases
	
	// Get spill poll times
	if(r.recordType()==RcdHeader::spillStart) {
	  std::vector<const CrcLocationData<CrcBeTrgPollDataV1>* >
	    v(accessor.access< CrcLocationData<CrcBeTrgPollDataV1> >());
	  
	  for(unsigned i(0);i<v.size();i++) {
	    _splHist[0]->Fill(v[i]->data()->actualTime().deltaTime());
	    _splHist[1]->Fill(v[i]->data()->actualTime().deltaTime());
	    _splHist[2]->Fill(v[i]->data()->actualTime().deltaTime());
	    _splHist[3]->Fill(v[i]->data()->actualTime().deltaTime());
	  }
	}
	
	
	// Get trigger poll times
	if(r.recordType()==RcdHeader::trigger) {
	  std::vector<const CrcLocationData<CrcBeTrgPollDataV1>* >
	    v(accessor.access< CrcLocationData<CrcBeTrgPollDataV1> >());
	  
	  for(unsigned i(0);i<v.size();i++) {
	    //if(v[i]->data()->actualTime().deltaTime()>0.001) 
	    // v[i]->print(std::cout," LONG POLL ");

	    _trgHist[0]->Fill(v[i]->data()->actualTime().deltaTime());
	    _trgHist[1]->Fill(v[i]->data()->actualTime().deltaTime());
	    _trgHist[2]->Fill(v[i]->data()->actualTime().deltaTime());
	    _trgHist[3]->Fill(v[i]->data()->actualTime().deltaTime());
	  }
	}
      }
    }

    _lastHeader=r;

    return true;
  }

private:
  const UtlPack _records;
  const UtlPack _configurations;
  unsigned _nConfiguration;
  
  std::vector<unsigned> _vId[RcdHeader::endOfRecordTypeEnum];
  std::vector<TH1F*>  _vHist[RcdHeader::endOfRecordTypeEnum][4];
  std::vector<unsigned> _vMId[RcdHeader::endOfRecordTypeEnum];
  std::vector<TH1F*>  _vMHist[RcdHeader::endOfRecordTypeEnum][maxMultiTimers][4];

  RcdHeader _lastHeader;
  TH1F* _rcdHist[RcdHeader::endOfRecordTypeEnum][4];
  TH1F* _splHist[4];
  TH1F* _trgHist[4];
};

#endif
