#ifndef HstCaen767Shm_HH
#define HstCaen767Shm_HH

#include <cassert>
#include <cstdlib>

#include "RcdUserRO.hh"
#include "RcdWriterDmy.hh"
#include "RcdWriterAsc.hh"
#include "RcdWriterBin.hh"

#include "UtlAverage.hh"
#include "SubAccessor.hh"
#include "SubInserter.hh" // TEMP!!!

#include "HstCaen767Store.hh"
#include "ShmObject.hh"


class HstCaen767Shm : public RcdUserRO {

public:
  HstCaen767Shm() :
    _shmHstCaen767Store(HstCaen767Store::shmKey),
    _pShm(_shmHstCaen767Store.payload()) {
    assert(_pShm!=0);
    _pShm->_resetLevel=HstFlags::job;
  }

  virtual ~HstCaen767Shm() {
  }

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

    DaqTwoTimer tt;

    // Check for user-requested reset
    if(_pShm->_resetNow) {
      _pShm->reset();
      _pShm->_resetNow=false;
    }

    _pShm->fill(r);

    switch (r.recordType()) {
  
    case RcdHeader::runStart: {
      //if(_pShm->_resetLevel==HstFlags::run) _pShm->reset();
      _pShm->reset();

      ///////////////////////////////////
      /*
      RcdRecord &s((RcdRecord&)r);
      SubInserter inserter(s);

      for(unsigned ii(0);ii<2;ii++) {
	BmlLocationData<BmlCaen767RunData> *zzz=
	  inserter.insert< BmlLocationData<BmlCaen767RunData> >(true);
	zzz->location(BmlLocation(0xec,0x00c0+ii,0));
      }
      */
      ///////////////////////////////////


      SubAccessor accessor(r);

      std::vector<const BmlLocationData<BmlCaen767RunData>*>
        v(accessor.access< BmlLocationData<BmlCaen767RunData> >());

      if(doPrint(r.recordType(),1)) {
	std::cout << " Number of BmlCaen767RunData subrecords found = "
		  << v.size() << std::endl;
      }
      
      _pShm->_numberOfTdcs=0;
      for(unsigned i(0);i<v.size();i++) {
	if(doPrint(r.recordType(),1)) v[i]->print(std::cout," ") << std::endl;
	
	if(_pShm->_numberOfTdcs<_pShm->maximumNumberOfTdcs) {
	  _pShm->_location[_pShm->_numberOfTdcs]=v[i]->location();
	  _pShm->_numberOfTdcs++;
	}
      }

      if(doPrint(r.recordType(),1)) {
	std::cout << " Number of TDCs found = "
		  << _pShm->_numberOfTdcs << std::endl;

	for(unsigned i(0);i<_pShm->_numberOfTdcs;i++) {
	  _pShm->_location[i].print(std::cout,"  ");
	}

	std::cout << std::endl;
      }

      break;
    }
  
    case RcdHeader::configurationStart: {
      if(_pShm->_resetLevel==HstFlags::configuration) _pShm->reset();
      break;
    }
  
    case RcdHeader::acquisitionStart: {
      if(_pShm->_resetLevel==HstFlags::acquisition) _pShm->reset();
      break;
    }
  
    case RcdHeader::event: {
      if(_pShm->_resetLevel==HstFlags::event) _pShm->reset();

      ///////////////////////////////////
      /*
      RcdRecord &s((RcdRecord&)r);
      SubInserter inserter(s);

      for(unsigned ii(0);ii<2;ii++) {
	BmlLocationData<BmlCaen767EventData> *zzz=
	  inserter.insert< BmlLocationData<BmlCaen767EventData> >(true);
	//if(ii==0)
	zzz->location(_pShm->_location[ii]);
	
	//zzz->data()->numberOfWords(rand()%256);
	zzz->data()->numberOfWords(16*(ii+1));
	inserter.extend(zzz->data()->numberOfWords()*4);
	BmlCaen767EventDatum *xxx(zzz->data()->data());
	UtlPack *yyy((UtlPack*)zzz->data()->data());
	yyy[0].bits(21,22,2);
	unsigned chan;
	for(unsigned i(1);i<zzz->data()->numberOfWords()-1;i++) {
	  //yyy[i]=rand();
	  if((i%2)==1) chan=rand()%8;
	  xxx[i].setDatum(chan,false,(i%2)==1,rand()%600);
	  //xxx[i]=
	}
	xxx[zzz->data()->numberOfWords()-1].setEob(0x05,7,zzz->data()->numberOfWords()-2);
	//zzz->print(std::cout) << std::endl;
      }
      */
      ///////////////////////////////////

      SubAccessor accessor(r);

      std::vector<const BmlLocationData<BmlCaen767EventData>*>
        v(accessor.access< BmlLocationData<BmlCaen767EventData> >());

      if(doPrint(r.recordType(),1)) {
	std::cout << " Number of BmlCaen767EventData subrecords found = "
		  << v.size() << std::endl;
      }
      //if(v.size()>0) v[0]->print(std::cout) << std::endl;

      _pShm->_size.fill(v.size());
      
      unsigned nSubRecords[_pShm->maximumNumberOfTdcs];
      memset(nSubRecords,0,_pShm->maximumNumberOfTdcs*sizeof(unsigned));

      for(unsigned j(0);j<v.size();j++) {

	// Find which TDC this is
	unsigned nTdc(_pShm->maximumNumberOfTdcs+1);
	for(unsigned i(0);i<_pShm->_numberOfTdcs;i++) {
	  if(_pShm->_location[i]==v[j]->location()) nTdc=i;
	}

	if(nTdc>1) {
	  v[j]->print(std::cout," ERROR ");	  
	  assert(false);
	}

	_pShm->_tdcs.fill(nTdc);
	
	// Unknown TDC
	if(nTdc==(_pShm->maximumNumberOfTdcs+1)) {
	  
	} else {
	  nSubRecords[nTdc]++;
	  
	  // Total number of words
	  _pShm->_numberOfWords[nTdc].fill(v[j]->data()->numberOfWords());
	  
	  unsigned nLabel[3];
	  memset(nLabel,0,3*sizeof(unsigned));
	  
	  unsigned nChan[HstCaen767Store::maximumNumberOfChannels][3];
	  memset(&(nChan[0][0]),0,HstCaen767Store::maximumNumberOfChannels*3*sizeof(unsigned));
	  
	  
	  const BmlCaen767EventDatum *p(v[j]->data()->data());
	  
	  for(unsigned k(0);k<v[j]->data()->numberOfWords();k++) {
	    if     (p[k].label()==BmlCaen767EventDatum::header)  nLabel[0]++;
	    else if(p[k].label()==BmlCaen767EventDatum::eob)     nLabel[1]++;
	    else if(p[k].label()==BmlCaen767EventDatum::invalid) nLabel[2]++;

	    if(nLabel[0]>1 || nLabel[1]>1) {
	      v[j]->print(std::cout," ERROR ");
	      assert(false);
	    }

	    // Real TDC data
	    if(p[k].label()==BmlCaen767EventDatum::datum) {
	      unsigned chn(p[k].channelNumber());
	      
	      if(p[k].startTime()) {
		nChan[chn][2]++;
		
	      } else {
	      
		_pShm->_channels[nTdc].fill(chn);
		
		if(chn<HstCaen767Store::maximumNumberOfChannels) {
		
		  unsigned time(p[k].time());
		  //if(time>9999) time=9999; // Don't store overflows
		  
		  if(p[k].leadingEdge()) {
		    nChan[chn][0]++;
		    _pShm->_tdcValues[nTdc][chn][0].fill(time);// leading edge
		  } else {
		    nChan[chn][1]++;
		    _pShm->_tdcValues[nTdc][chn][1].fill(time);// falling edge
		  }
		}
	      }
	    }
	  }

	  for(unsigned k(0);k<3;k++) {
	    _pShm->_labels[nTdc][k].fill(nLabel[k]);
	  }
	
	  for(unsigned k(0);k<HstCaen767Store::maximumNumberOfChannels;k++) {
	    _pShm->_numberOfWordsPerChannel[nTdc][k][0].fill(nChan[k][0]);
	    _pShm->_numberOfWordsPerChannel[nTdc][k][1].fill(nChan[k][1]);
	    _pShm->_numberOfWordsPerChannel[nTdc][k][2].fill(nChan[k][2]);
	  }
	
	}
      }

      for(unsigned i(0);i<_pShm->_numberOfTdcs;i++) {
	_pShm->_sizePerTdc[i].fill(nSubRecords[i]);
      }
      	
      _pShm->_validEvent=true;
      break;
    }
  
    default: {
      break;
    }
    };
 
    tt.setEndTime();

    UtlTimeDifference td(tt.timeDifference());
    if     (td.seconds()<0) _pShm->_processTime[r.recordType()].fillUnderflow();
    else if(td.seconds()>0) _pShm->_processTime[r.recordType()].fillOverflow();
    else                    _pShm->_processTime[r.recordType()].fill(td.microseconds()/100);

    return true;
  }

private:
  ShmObject<HstCaen767Store> _shmHstCaen767Store;
  HstCaen767Store *_pShm;

};

#endif
