#ifndef HstCrcNoiseMap_HH
#define HstCrcNoiseMap_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"


class HstCrcNoiseMap : public RcdUserRO {

public:
  HstCrcNoiseMap() {
    for(unsigned c(0);c<2;c++) {
      for(unsigned i(0);i<=21;i++) {
	_number[c][i]=0;
	_unverified[c][i]=0;
	_pedestal[c][i]=0;
	_noise[c][i]=0;
	_numbertot[c][i]=0;
	_unverifiedtot[c][i]=0;
	_pedestaltot[c][i]=0;
	_noisetot[c][i]=0;
      }
    }
  }

  virtual ~HstCrcNoiseMap() {
    for(unsigned c(0);c<2;c++) {
      for(unsigned i(0);i<=21;i++) {
	if(_number[c][i]    !=0) delete _number[c][i];
	if(_unverified[c][i]!=0) delete _unverified[c][i];
	if(_pedestal[c][i]  !=0) delete _pedestal[c][i];
	if(_noise[c][i]     !=0) delete _noise[c][i];
	if(_numbertot[c][i]    !=0) delete _numbertot[c][i];
	if(_unverifiedtot[c][i]!=0) delete _unverifiedtot[c][i];
	if(_pedestaltot[c][i]  !=0) delete _pedestaltot[c][i];
	if(_noisetot[c][i]     !=0) delete _noisetot[c][i];
	
	_number[c][i]=0;
	_unverified[c][i]=0;
	_pedestal[c][i]=0;
	_noise[c][i]=0;
	_numbertot[c][i]=0;
	_unverifiedtot[c][i]=0;
	_pedestaltot[c][i]=0;
	_noisetot[c][i]=0;
      }
    }
  }

  bool record(const RcdRecord &r) {

    if(r.recordType()==RcdHeader::runStart) {
      for(unsigned c(0);c<2;c++) {
	for(unsigned i(0);i<=21;i++) {
	  if(_number[c][i]    !=0) delete _number[c][i];
	  if(_unverified[c][i]!=0) delete _unverified[c][i];
	  if(_pedestal[c][i]  !=0) delete _pedestal[c][i];
	  if(_noise[c][i]     !=0) delete _noise[c][i];
	  if(_numbertot[c][i]     !=0) delete _numbertot[c][i];
	  if(_unverifiedtot[c][i]!=0) delete _unverifiedtot[c][i];
	  if(_pedestaltot[c][i]   !=0) delete _pedestaltot[c][i];
	  if(_noisetot[c][i]      !=0) delete _noisetot[c][i];
	  
	  _number[c][i]=0;
	  _unverified[c][i]=0;
	  _pedestal[c][i]=0;
	  _noise[c][i]=0;
	  _numbertot[c][i]=0;
	  _unverifiedtot[c][i]=0;
	  _pedestaltot[c][i]=0;
	  _noisetot[c][i]=0;
	}
      }

      SubAccessor accessor(r);
      std::vector<const CrcLocationData<CrcVmeRunData>* >
	v(accessor.access< CrcLocationData<CrcVmeRunData> >());
 
      for(unsigned i(0);i<v.size();i++) {

	// Get crate number
	unsigned crate(2);
	if(v[i]->crateNumber()==0xec) crate=0;
	if(v[i]->crateNumber()==0xac) crate=1;
	if(crate<2) {

	  // Get slot number
	  if(v[i]->slotNumber()<22) {
	    unsigned slot(v[i]->slotNumber());

	    std::ostringstream label;
	    label << "HstCrcNoiseCrate" << crate << "Slot";
	    if(slot<10) label << "0";
	    label << slot;
	    
	    std::ostringstream title;
	    if(crate==0) title << "ECAL Crate, ";
	    if(crate==1) title << "AHCAL Crate, ";
	    title << " Slot " << std::setw(2) << slot;

	    _number[crate][slot]=new 
	      TH1F((label.str()+"Number").c_str(),
		   (title.str()+", Number of reads vs 12*FE+ADC").c_str(),
		   96,0,96);
	    
	    _unverified[crate][slot]=new 
	      TH1F((label.str()+"Unverified").c_str(),
		   (title.str()+", Number of corrupted reads vs 12*FE+ADC").c_str(),
		   96,0,96);
	    
	    _pedestal[crate][slot]=new 
	      TH1F((label.str()+"Average").c_str(),
		   (title.str()+", Average vs 12*FE+ADC").c_str(),
		   96,0,96);
	    
	    _noise[crate][slot]=new 
	      TH1F((label.str()+"Rms").c_str(),
		   (title.str()+", RMS vs 12*FE+ADC").c_str(),
		   96,0,96);

	    std::ostringstream labeltot;
	    labeltot << "HstCrcNoiseCrate" << crate << "Slot";
	    if(slot<10) labeltot << "0";
	    labeltot << slot << "Tot";
	    
	    std::ostringstream titletot;
	    if(crate==0) titletot << "ECAL Crate, ";
	    if(crate==1) titletot << "AHCAL Crate, ";
	    titletot << " Slot " << std::setw(2) << slot ;

	    _numbertot[crate][slot]=new 
	      TH1F((labeltot.str()+"Number").c_str(),
		   (titletot.str()+", Number of reads vs 18*12*FE+18*ADC+channel").c_str(),
		   1728,0,1728);
	    
	    _unverifiedtot[crate][slot]=new 
	      TH1F((labeltot.str()+"Unverified").c_str(),
		   (titletot.str()+", Number of corrupted reads vs 18*12*FE+18*ADC+channel").c_str(),
		   1728,0,1728);
	    
	    _pedestaltot[crate][slot]=new 
	      TH1F((labeltot.str()+"Average").c_str(),
		   (titletot.str()+", Average vs 18*12*FE+18*ADC+channel").c_str(),
		   1728,0,1728);
	    
	    _noisetot[crate][slot]=new 
	      TH1F((labeltot.str()+"Rms").c_str(),
		   (titletot.str()+", RMS vs 18*12*FE+18*ADC+channel").c_str(),
		   1728,0,1728);
	    
	    for(unsigned fe(0);fe<8;fe++) {
	      for(unsigned chip(0);chip<12;chip++) {
		_average[crate][slot][fe][chip].reset();
		for(unsigned channel(0);channel<18;channel++) {
		  _averagetot[crate][slot][fe][chip][channel].reset();
		}
	      }
	    }
	  }
	}
      }

      return true;
    }

    // HOPEFULLY ONLY TEMPORARY!
    if(r.recordType()==RcdHeader::configurationStart) {
      _skipNextEvent=true; // Skip first event in every configuration (Marius error)
      return true;
    }


    if(r.recordType()==RcdHeader::acquisitionEnd) {
      for(unsigned c(0);c<2;c++) {
	for(unsigned i(0);i<=21;i++) {
	  if(_number[c][i]!=0 && _unverified[c][i]!=0 && 
	     _pedestal[c][i]!=0 && _noise[c][i]!=0 && 
	     _numbertot[c][i]!=0 && _unverifiedtot[c][i]!=0 && 
	     _pedestaltot[c][i]!=0 && _noisetot[c][i]!=0) {

	    _number[c][i]->Reset();
	    _unverified[c][i]->Reset();
	    _pedestal[c][i]->Reset();
	    _noise[c][i]->Reset();
	    _numbertot[c][i]->Reset();
	    _unverifiedtot[c][i]->Reset();
	    _pedestaltot[c][i]->Reset();
	    _noisetot[c][i]->Reset();
	    
	    for(unsigned fe(0);fe<8;fe++) {
	      for(unsigned chip(0);chip<12;chip++) {
		_unverified[c][i]->SetBinContent(12*fe+chip+1,_unvCount[c][i][fe][chip]);

		_number[c][i]->SetBinContent(  12*fe+chip+1,_average[c][i][fe][chip].number());
		_pedestal[c][i]->SetBinContent(12*fe+chip+1,_average[c][i][fe][chip].average());
		_pedestal[c][i]->SetBinError(  12*fe+chip+1,_average[c][i][fe][chip].errorOnAverage());
		_noise[c][i]->SetBinContent(   12*fe+chip+1,_average[c][i][fe][chip].sigma());
		_noise[c][i]->SetBinError(     12*fe+chip+1,_average[c][i][fe][chip].errorOnSigma());

		_unvCount[c][i][fe][chip]=0;
		_average[c][i][fe][chip].reset();


		for(unsigned channel(0);channel<18;channel++) {
		_unverifiedtot[c][i]->SetBinContent(18*12*fe+18*chip+channel+1,_unvCounttot[c][i][fe][chip][channel]);

		_numbertot[c][i]->SetBinContent(  18*12*fe+18*chip+channel+1,
						  _averagetot[c][i][fe][chip][channel].number());
		_pedestaltot[c][i]->SetBinContent(18*12*fe+18*chip+channel+1,
						  _averagetot[c][i][fe][chip][channel].average());
		_pedestaltot[c][i]->SetBinError(  18*12*fe+18*chip+channel+1,
						  _averagetot[c][i][fe][chip][channel].errorOnAverage());
		_noisetot[c][i]->SetBinContent(   18*12*fe+18*chip+channel+1,
						  _averagetot[c][i][fe][chip][channel].sigma());
		_noisetot[c][i]->SetBinError(     18*12*fe+18*chip+channel+1,
						  _averagetot[c][i][fe][chip][channel].errorOnSigma());

		_unvCounttot[c][i][fe][chip][channel]=0;
		_averagetot[c][i][fe][chip][channel].reset();
		}
	      }
	    }
	  }
	}
      }
      return true;
    }

    if(r.recordType()==RcdHeader::event) {
      //r.RcdHeader::print(std::cout);

      // HOPEFULLY ONLY TEMPORARY!
      if(_skipNextEvent) {
	_skipNextEvent=false;
	return true;
      }

      SubAccessor accessor(r);
      std::vector<const CrcLocationData<CrcVlinkEventData>* >
	v(accessor.access< CrcLocationData<CrcVlinkEventData> >());

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

	// Make sure data block is self-consistent and not duplicated
	//if(!v[i]->ignore() && v[i]->verify() && v[i]->data()->verify()) {
	bool verified(v[i]->verify() && v[i]->data()->verify());

	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    
	    // Get slot number
	    if(v[i]->slotNumber()<22) {
	      unsigned slot(v[i]->slotNumber());
	      if(_number[crate][slot]!=0 &&
	         _unverified[crate][slot]!=0 &&
		 _pedestal[crate][slot]!=0 &&
		 _noise[crate][slot]!=0 &&
		 _numbertot[crate][slot]!=0 &&
	         _unverifiedtot[crate][slot]!=0 &&
		 _pedestaltot[crate][slot]!=0 &&
		 _noisetot[crate][slot]!=0) {
		
		// Loop over FE number
		for(unsigned fe(0);fe<8;fe++) {
		  const CrcVlinkFeHeader *fh(v[i]->data()->feHeader(fe));
		  if(fh!=0) {
		    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(!verified) _unvCount[crate][slot][fe][chip]++;
			    _average[crate][slot][fe][chip]+=as->adc(chip);
			    
			    if(chan<18) {
			      if(!verified) _unvCounttot[crate][slot][fe][chip][chan]++;
			      if(chan<18)_averagetot[crate][slot][fe][chip][chan]+=as->adc(chip);
			    }
			  }
			}
		      }
		    }
		  }
		}
	      }
	    }
	  }
      //}
      }
    }
    return true;
  }

private:
  bool _skipNextEvent; // HOPEFULLY ONLY TEMPORARY!

  TH1F *_number[2][22];
  TH1F *_unverified[2][22];
  TH1F *_pedestal[2][22];
  TH1F *_noise[2][22];
  TH1F *_numbertot[2][22];
  TH1F *_unverifiedtot[2][22];
  TH1F *_pedestaltot[2][22];
  TH1F *_noisetot[2][22];

  unsigned _unvCount[2][22][8][12];
  unsigned _unvCounttot[2][22][8][12][18];
  UtlAverage _average[2][22][8][12];
  UtlAverage _averagetot[2][22][8][12][18];
};

#endif
