#ifndef HstCrcNoiseShm_HH
#define HstCrcNoiseShm_HH

#include <cassert>

#include "TH1F.h"

#include "DaqRunStart.hh"

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

#include "UtlAverage.hh"
#include "SubAccessor.hh"

#include "HstCrcNoiseStore.hh"
#include "ShmObject.hh"


class HstCrcNoiseShm : public RcdUserRO {

public:
  HstCrcNoiseShm() :
    _shmHstCrcNoiseStore(HstCrcNoiseStore::shmKey),
    _pShm(_shmHstCrcNoiseStore.payload()) {
    assert(_pShm!=0);
  }

  virtual ~HstCrcNoiseShm() {
  }

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

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

    _pShm->fill(r);

    switch (r.recordType()) {
  
    case RcdHeader::runStart: {
      SubAccessor accessor(r);
      std::vector<const CrcLocationData<CrcVmeRunData>*>
        v(accessor.access< CrcLocationData<CrcVmeRunData> >());
 
      _pShm->_crcs[0].word(0);
      _pShm->_crcs[1].word(0);
      _pShm->_crcs[2].word(0);

      for(unsigned i(0);i<v.size();i++) {
        unsigned s(v[i]->slotNumber());
        if(s<=21) {
          if(v[i]->crateNumber()==0xec) _pShm->_crcs[0].bit(s,true);
          if(v[i]->crateNumber()==0xce) _pShm->_crcs[0].bit(s,true);
          if(v[i]->crateNumber()==0xac) _pShm->_crcs[1].bit(s,true);
          if(v[i]->crateNumber()==0xde) _pShm->_crcs[2].bit(s,true);
        }
      }
 
      if(doPrint(r.recordType())) {
	std::cout << " CRC bits set: ECAL   " << printHex(_pShm->_crcs[0]) << std::endl
		  << "               HCAL   " << printHex(_pShm->_crcs[1]) << std::endl
		  << "               DHECAL " << printHex(_pShm->_crcs[2]) << std::endl;
      }

      if(_pShm->_resetLevel==HstFlags::run) _pShm->reset();
      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::acquisitionEnd: {
      for(unsigned c(0);c<3;c++) {
	for(unsigned s(0);s<=21;s++) {
	  for(unsigned fe(0);fe<8;fe++) {
	    for(unsigned chip(0);chip<12;chip++) {
	      if(c<2) {
		for(unsigned chan(0);chan<18;chan++) {
		  _pShm->_mean[c][s][fe].fill((int)_pShm->_average[c][s][fe][chip][chan].average());
		  _pShm->_rms[ c][s][fe].fill((int)_pShm->_average[c][s][fe][chip][chan].sigma());
		}
	      } else {
		for(unsigned chan(0);chan<64;chan++) {
		  _pShm->_mean[c][s][fe].fill((int)_pShm->_average64[s][fe][chip][chan].average());
		  _pShm->_rms[ c][s][fe].fill((int)_pShm->_average64[s][fe][chip][chan].sigma());
		}
	      }
	    }
	  }
	}
      }
      break;
    }
  
    case RcdHeader::event: {
      if(_pShm->_resetLevel==HstFlags::event) _pShm->reset();

      SubAccessor accessor(r);
      std::vector<const CrcLocationData<CrcVlinkEventData>* >
	v(accessor.access< CrcLocationData<CrcVlinkEventData> >());
      
      for(unsigned i(0);i<v.size();i++) {
	if(doPrint(r.recordType(),2)) v[i]->print(std::cout," ") << std::endl;
	
	// Make sure data block is self-consistent
	bool verified(v[i]->verify() && v[i]->data()->verify());
	
	// Get crate number
	unsigned crate(3);
	if(v[i]->crateNumber()==0xec) crate=0;
	if(v[i]->crateNumber()==0xce) crate=0;
	if(v[i]->crateNumber()==0xac) crate=1;
	if(v[i]->crateNumber()==0xde) crate=2;
	if(crate<3) {
	  
	  // Get slot number
	  unsigned slot(v[i]->slotNumber());
	  if(slot<22) {

	    // Label errors
	    if(!verified) _pShm->_unverified[crate][slot]++;

	    // Loop over FE number
	    for(unsigned fe(0);fe<8;fe++) {
	      const CrcVlinkFeData *fd(v[i]->data()->feData(fe));
	      if(fd!=0) {
		  
		// Loop over mplex channel number
		for(unsigned chan(0);chan<v[i]->data()->feNumberOfAdcSamples(fe);chan++) {
		  const CrcVlinkAdcSample *as(fd->adcSample(chan));
		  if(as!=0) {
		      
		    // Loop over chips=ADCs
		    for(unsigned chip(0);chip<12;chip++) {
		      if(crate<2) {
			_pShm->_average[crate][slot][fe][chip][18]+=as->adc(chip);
			if(chan<18) _pShm->_average[crate][slot][fe][chip][chan]+=as->adc(chip);
		      } else {
			_pShm->_average64[slot][fe][chip][64]+=as->adc(chip);
			if(chan<64) _pShm->_average64[slot][fe][chip][chan]+=as->adc(chip);
		      }
		    }
		  }
		}
	      }
	    }
	  }
	}
      }
	
      _pShm->_validEvent=true;
      break;
    }
  
    default: {
      break;
    }
    };
 
    return true;
  }

private:
  ShmObject<HstCrcNoiseStore> _shmHstCrcNoiseStore;
  HstCrcNoiseStore *_pShm;
};

#endif
