#ifndef MpsAnalysisNoise_HH
#define MpsAnalysisNoise_HH

#include <cmath>
#include <cassert>

#include "TFile.h"
#include "TF1.h"
#include "TH1F.h"
#include "TH2F.h"
#include "TProfile.h"

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

#include "MpsAnalysisBase.hh"
#include "MpsSensor1FullTimeStamps.hh"
#include "MpsSensor1HitLists.hh"


class MpsAnalysisNoise : public MpsAnalysisBase {

public:
  MpsAnalysisNoise() : MpsAnalysisBase("MpsAnalysisNoise") {
  }
  
  virtual ~MpsAnalysisNoise() {
    endRoot();
  }
  
  virtual bool mpsAnalysisValidRun(IlcRunType::Type t) const {
    return t==IlcRunType::mpsNoise;
  }

  bool runStart(const RcdRecord &r) {
    memset(_pixels,0,4*168*168);
    
    for(unsigned region(0);region<4;region++) {
      std::ostringstream sLabel;
      sLabel << "Region" << region;
      
      std::ostringstream sTitle;
      sTitle << _runTitle << ", Region " << region;
      
      _hHits[region]=new TH1F((std::string("h")+sLabel.str()+"Hits").c_str(),(sTitle.str()+", Number of hits").c_str(),
			      3200,0.0,3200.0);
      _hHitsVsTemp[region]=new TH2F((std::string("h")+sLabel.str()+"HitsVsTemp").c_str(),(sTitle.str()+", Number of hits vs Temperature").c_str(),
				    20*32,20.0,40.0,3200,0.0,3200.0);
      _pHitsVsTemp[region]=new TProfile((std::string("p")+sLabel.str()+"HitsVsTemp").c_str(),(sTitle.str()+", Number of hits vs Temperature").c_str(),
				    20*32,20.0,40.0);
      _hRows[region]=new TH1F((std::string("h")+sLabel.str()+"Rows").c_str(),(sTitle.str()+", Rows").c_str(),
			      168,0.0,168.0);
      _hRowsVsTime[region]=new TH2F((std::string("h")+sLabel.str()+"RowsVsTime").c_str(),(sTitle.str()+", Rows vs time").c_str(),
				    8192,0.0,8192.0,168,0.0,168.0);
      _hGroups[region]=new TH1F((std::string("h")+sLabel.str()+"Groups").c_str(),(sTitle.str()+", Groups").c_str(),
				8,0.0,8.0);
      _hGroupsVsTime[region]=new TH2F((std::string("h")+sLabel.str()+"GroupsVsTime").c_str(),(sTitle.str()+", Groups vs time").c_str(),
				      8192,0.0,8192.0,8,0.0,8.0);
      _hChannelsWord[region]=new TH1F((std::string("h")+sLabel.str()+"ChannelsWord").c_str(),(sTitle.str()+", Channels word").c_str(),
				      64,0.0,64.0);
      _hChannelsWordVsTime[region]=new TH2F((std::string("h")+sLabel.str()+"ChannelsWordVsTime").c_str(),(sTitle.str()+", Channels word vs time").c_str(),
					    8192,0.0,8192.0,64,0.0,64.0);
      _hChannelsWordAtTime[region][0]=new TH1F((std::string("h")+sLabel.str()+"ChannelsWordAtTime0").c_str(),(sTitle.str()+", Channels word at time 0").c_str(),
					       64,0.0,64.0);
      _hChannelsWordAtTime[region][1]=new TH1F((std::string("h")+sLabel.str()+"ChannelsWordAtTime1").c_str(),(sTitle.str()+", Channels word at time 1").c_str(),
					       64,0.0,64.0);
      _hChannelsWordAtTime[region][2]=new TH1F((std::string("h")+sLabel.str()+"ChannelsWordAtTime2").c_str(),(sTitle.str()+", Channels word at time 2").c_str(),
					       64,0.0,64.0);
      _hChannelsWordAtTime[region][3]=new TH1F((std::string("h")+sLabel.str()+"ChannelsWordAtTime3").c_str(),(sTitle.str()+", Channels word at time 3").c_str(),
					       64,0.0,64.0);
      _hChannelsWordAtTime[region][4]=new TH1F((std::string("h")+sLabel.str()+"ChannelsWordAtTime4").c_str(),(sTitle.str()+", Channels word at time 4").c_str(),
					       64,0.0,64.0);
      _hChannelsWordAtTime[region][5]=new TH1F((std::string("h")+sLabel.str()+"ChannelsWordAtTime5").c_str(),(sTitle.str()+", Channels word at time 5").c_str(),
					       64,0.0,64.0);
      _hChannelsWordAtTime[region][6]=new TH1F((std::string("h")+sLabel.str()+"ChannelsWordAtTime6").c_str(),(sTitle.str()+", Channels word at time 6").c_str(),
					       64,0.0,64.0);
      _hChannelsWordAtTime[region][7]=new TH1F((std::string("h")+sLabel.str()+"ChannelsWordAtTime7").c_str(),(sTitle.str()+", Channels word at time 7").c_str(),
					       64,0.0,64.0);
      
      _hChannels[region]=new TH1F((std::string("h")+sLabel.str()+"Channels").c_str(),(sTitle.str()+", Number of channels").c_str(),
				  19*168*6+1,0.0,19.0*168.0*6.0+1.0);
      _hTimes[region]=new TH1F((std::string("h")+sLabel.str()+"Times").c_str(),(sTitle.str()+", Times").c_str(),
			       8192,0.0,8192.0);
      _pChannelsVsTime[region]=new TProfile((std::string("p")+sLabel.str()+"ChannelsVsTime").c_str(),(sTitle.str()+", Number of channels vs time").c_str(),
					    8192,0.0,8192.0);
      
      _hPixels[region]=new TH2F((std::string("h")+sLabel.str()+"Pixels").c_str(),(sTitle.str()+", Pixels").c_str(),
				42,0.0,42.0,168,0.0,168.0);
      _hPixelsAtTime[region][0]=new TH2F((std::string("h")+sLabel.str()+"PixelsAtTime0").c_str(),(sTitle.str()+", Pixels at time 0").c_str(),
					 42,0.0,42.0,168,0.0,168.0);
      _hPixelsAtTime[region][1]=new TH2F((std::string("h")+sLabel.str()+"PixelsAtTime1").c_str(),(sTitle.str()+", Pixels at time 1").c_str(),
					 42,0.0,42.0,168,0.0,168.0);
      _hPixelsAtTime[region][2]=new TH2F((std::string("h")+sLabel.str()+"PixelsAtTime2").c_str(),(sTitle.str()+", Pixels at time 2").c_str(),
					 42,0.0,42.0,168,0.0,168.0);
      _hPixelsAtTime[region][3]=new TH2F((std::string("h")+sLabel.str()+"PixelsAtTime3").c_str(),(sTitle.str()+", Pixels at time 3").c_str(),
					 42,0.0,42.0,168,0.0,168.0);
      _hPixelsAtTime[region][4]=new TH2F((std::string("h")+sLabel.str()+"PixelsAtTime4").c_str(),(sTitle.str()+", Pixels at time 4").c_str(),
					 42,0.0,42.0,168,0.0,168.0);
      _hPixelsAtTime[region][5]=new TH2F((std::string("h")+sLabel.str()+"PixelsAtTime5").c_str(),(sTitle.str()+", Pixels at time 5").c_str(),
					 42,0.0,42.0,168,0.0,168.0);
      _hPixelsAtTime[region][6]=new TH2F((std::string("h")+sLabel.str()+"PixelsAtTime6").c_str(),(sTitle.str()+", Pixels at time 6").c_str(),
					 42,0.0,42.0,168,0.0,168.0);
      _hPixelsAtTime[region][7]=new TH2F((std::string("h")+sLabel.str()+"PixelsAtTime7").c_str(),(sTitle.str()+", Pixels at time 7").c_str(),
					 42,0.0,42.0,168,0.0,168.0);
      _hOccupancies[region]=new TH1F((std::string("h")+sLabel.str()+"Occupancies").c_str(),(sTitle.str()+", Occupancies").c_str(),
				     1000,0.0,0.1);
      _hProbabilities[region]=new TH1F((std::string("h")+sLabel.str()+"Probabilities").c_str(),(sTitle.str()+", Log10(Probabilities)").c_str(),
				       1000,-10.0,10.0);
    }

    for(unsigned x(0);x<42;x++) {
      std::ostringstream sLabel;
      sLabel << "PixelX" << std::setfill('0') << std::setw(3) << x << "Y000";

      _hHvsT[x]=new TH2F((std::string("h")+sLabel.str()+"HvsT").c_str(),(sLabel.str()+", Hits vs Times").c_str(),
			 8200,0.0,8200.0,20,0.0,20.0);
      _pHvsT[x]=new TProfile((std::string("p")+sLabel.str()+"HvsT").c_str(),(sLabel.str()+", Hits vs Times").c_str(),
			 8200,0.0,8200.0);
    }

    for(unsigned x(0);x<168;x++) {
      for(unsigned y(0);y<168;y++) {
	_nHits[x][y]=0;
	_nTims[x][y]=0;
      }
    }

    _nTimeStampCut=0;

    return true;
  }

  bool configurationStart(const RcdRecord &r) {
    for(unsigned i(0);i<_vLocation.size();i++) {
      if(_validSensorConfigurationData[i]) {      

	unsigned n(0);
	for(unsigned y(0);y<168;y++) {
	  for(unsigned x(0);x<168;x++) {
	    if(_vSensorConfigurationData[i].mask(x,y)) {
	      //std::cout << "Masked! Region " << x/42
	      //<< ", x,y = " << x << ", " << y << std::endl;
	      n++;
	    }
	  }
	}
	
	//_sensorConfigurationData.print();
	std::cout << _sensorTitle[i] << ", total masked = " << n << std::endl;

      } else {
	std::cout << _usbDaqTitle[i]
		  << " has invalid sensor configuration data";
      }
    }

    return true;
  }

  bool slowReadout(const RcdRecord &r) {
    SubAccessor accessor(r);

    std::vector<const MpsLocationData<MpsPcb1SlowReadoutData>* >
      v(accessor.access< MpsLocationData<MpsPcb1SlowReadoutData> >());
    //assert(v.size()==_vLocation.size());
    if(doPrint(r.recordType(),1)) v[0]->print(std::cout) << std::endl;
    _pcbSlowReadoutData=*(v[0]->data());

    return true;
  }

  bool bunchTrain(const RcdRecord &r) {
    SubAccessor accessor(r);

    std::vector<const MpsLocationData<MpsSensor1BunchTrainData>* >
      v(accessor.access< MpsLocationData<MpsSensor1BunchTrainData> >());
    //assert(v.size()==_vLocation.size());
    if(doPrint(r.recordType(),1)) v[0]->print(std::cout) << std::endl;
    
    unsigned eHits[42],eTims[42];
    memset(eHits,0,4*42);
    memset(eTims,0,4*42);

    MpsSensor1FullTimeStamps fts(*(v[0]->data()));
    for(unsigned x(0);x<168;x++) {
      for(unsigned y(0);y<168;y++) {
	if(fts.fullTimeStamp(x,y)<_nTimeStampCut) _nTims[x][y]+=0;
	else                                      _nTims[x][y]+=fts.fullTimeStamp(x,y)-_nTimeStampCut;
	if(fts.fullTimeStamp(x,y)==0)             _nTims[x][y]+=_vUsbDaqConfigurationData[0].spillCycleCount()+1-_nTimeStampCut;
	if(x<42 && y==0) {
	  if(fts.fullTimeStamp(x,y)<_nTimeStampCut) eTims[x]+=0;
	  else                                      eTims[x]+=fts.fullTimeStamp(x,y)-_nTimeStampCut;
	  if(fts.fullTimeStamp(x,y)==0)             eTims[x]+=_vUsbDaqConfigurationData[0].spillCycleCount()+1-_nTimeStampCut;
	}
      }
    }

    for(unsigned region(0);region<4;region++) {
      if(true) {
	//if(v[0]->data()->numberOfRegionHits(region)<100) {
      //if(v[0]->data()->numberOfRegionHits(region)>=100 &&
      // v[0]->data()->numberOfRegionHits(region)<3000) {
      //if(v[0]->data()->numberOfRegionHits(region)>=3000) {

      _hHits[region]->Fill(v[0]->data()->numberOfRegionHits(region));
      _hHitsVsTemp[region]->Fill(_pcbSlowReadoutData.celcius(),v[0]->data()->numberOfRegionHits(region));
      _pHitsVsTemp[region]->Fill(_pcbSlowReadoutData.celcius(),v[0]->data()->numberOfRegionHits(region));

      memset(_chans,0,4*8192);
      const MpsSensor1BunchTrainDatum *p(v[0]->data()->regionData(region));
      for(unsigned j(0);j<v[0]->data()->numberOfRegionHits(region);j++) {
	//if(region==0) p[j].print();



	if(p[j].timeStamp()>=_nTimeStampCut) {

	  _hTimes[region]->Fill(p[j].timeStamp());
	  
	  _hRows[region]->Fill(p[j].row());
	  _hRowsVsTime[region]->Fill(p[j].timeStamp(),p[j].row());
	  
	  _hGroups[region]->Fill(p[j].group());
	  _hGroupsVsTime[region]->Fill(p[j].timeStamp(),p[j].group());
	  
	  _hChannelsWord[region]->Fill(p[j].channels());
	  _hChannelsWordVsTime[region]->Fill(p[j].timeStamp(),p[j].channels());
	  if(p[j].timeStamp()<8) {
	    _hChannelsWordAtTime[region][p[j].timeStamp()]->Fill(p[j].channels());
	  }
	  
	  for(unsigned c(0);c<6;c++) {
	    if(p[j].channel(c)) {
	      _chans[p[j].timeStamp()]++;
	      
	      if((42*region+6*p[j].group()+c)<168 && p[j].row()<168) {
		_pixels[42*region+6*p[j].group()+c][p[j].row()]++;
		_hPixels[region]->Fill(6*p[j].group()+c,p[j].row());
		if(p[j].timeStamp()<8) _hPixelsAtTime[region][p[j].timeStamp()]->Fill(6*p[j].group()+c,p[j].row());
		
		_nHits[42*region+6*p[j].group()+c][p[j].row()]++;
		if(region==0 && p[j].row()==0) eHits[6*p[j].group()+c]++;
		
	      } else {
		//p[j].print(std::cout,"ERROR ");
	      }
	    }
	  }
	}
      }
      
      unsigned chans(0);
      for(unsigned t(0);t<8192;t++) {
	if(_chans[t]>0) {
	  chans+=_chans[t];
	  _pChannelsVsTime[region]->Fill(t,_chans[t]);
	}
      }
      _hChannels[region]->Fill(chans);
    }
    }

    for(unsigned x(0);x<42;x++) {
      if(eHits[x]>=19 && eTims[x]==_vUsbDaqConfigurationData[0].spillCycleCount()+1) {
	_bunchTrain.print();
	v[0]->print(std::cout) << std::endl;
	assert(false);
      }
      _hHvsT[x]->Fill(eTims[x],eHits[x]);
      _pHvsT[x]->Fill(eTims[x],eHits[x]);
    }

    return true;
  }

  virtual void endRoot() {
    if(_rootFile!=0) {
      std::ostringstream sout;
      sout << "MpsAnalysisNoise" << std::setfill('0')
	   << std::setw(6) << _runStart.runNumber() << ".txt";
      std::ofstream fout(sout.str().c_str());

      unsigned tot[4]={0,0,0,0};
      for(unsigned y(0);y<168;y++) {
	for(unsigned x(0);x<168;x++) {
	  tot[x/42]+=_pixels[x][y];
	}
      }

      for(unsigned y(0);y<168;y++) {
	for(unsigned x(0);x<168;x++) {
	  if(_nTims[x][y]>0) {
	    if(_nHits[x][y]>0) {
	      double prb((double)_nHits[x][y]/_nTims[x][y]);
	      if(prb>0.00001) {
		std::cout << "x,y = " << x << ", " << y
			  << ", hits,times = " << _nHits[x][y]
			  << ", " << _nTims[x][y] << ", prob = "
			  << prb << std::endl;
	      }
	      
	      _hProbabilities[x/42]->Fill(std::log10(prb));	      
	      fout << x << " " << y << " " << prb << std::endl;
	    } else {
	      _hProbabilities[x/42]->Fill(std::log10((double)_nTims[x][y]));
	      fout << x << " " << y << " " << _nTims[x][y] << std::endl;
	    }
	  } else {
	    fout << x << " " << y << " " << 0.0 << std::endl;
	  }

	  /*
	  if(tot[x/42]>0) {
	    double occ(_pixels[x][y]/((double)tot[x/42]));
	    _hOccupancies[x/42]->Fill(occ);

	    if(occ>1.0/700.0) {
	      std::cout << "X,Y  " << x << ", " << y
			<< " hits = " << _pixels[x][y]
			<< ", total = " << tot[x/42] << ", occupancy "
			<< occ << std::endl;
	    }
	  }
	  */
	}
      }      
      fout.close();

      MpsAnalysisBase::endRoot();
    }
  }


private:
  bool _noise;

  unsigned _nTimeStampCut;
  MpsPcb1SlowReadoutData _pcbSlowReadoutData;

  unsigned _chans[8192];
  unsigned _pixels[168][168];
  unsigned _nHits[168][168];
  unsigned _nTims[168][168];

  TH1F *_hHits[4];
  TH2F *_hHitsVsTemp[4];
  TProfile *_pHitsVsTemp[4];

  TH1F *_hTimes[4];

  TH1F *_hRows[4];
  TH2F *_hRowsVsTime[4];

  TH1F *_hGroups[4];
  TH2F *_hGroupsVsTime[4];

  TH1F *_hChannelsWord[4];
  TH2F *_hChannelsWordVsTime[4];
  TH1F *_hChannelsWordAtTime[4][8];

  TH1F *_hChannels[4];
  TProfile *_pChannelsVsTime[4];

  TH2F *_hPixels[4];
  TH2F *_hPixelsAtTime[4][8];
  TH1F *_hOccupancies[4];
  TH1F *_hProbabilities[4];

  TH2F *_hHvsT[42];
  TProfile *_pHvsT[42];
};

#endif
