#ifndef MpsAnalysis_HH
#define MpsAnalysis_HH

#include <cassert>

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

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


class MpsAnalysis : public RcdUserRO {

public:
  MpsAnalysis() : _rootFile(0) {
  }

  virtual ~MpsAnalysis() {
    // In case no runEnd
    summary();
  }

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

    SubAccessor accessor(r);

    switch (r.recordType()) {
  
    case RcdHeader::runStart: {

      // Get list of IlcRunStart subrecords
      // There should only be one and it should only
      // be in the runStart record
      std::vector<const IlcRunStart*>
        vs(accessor.access<IlcRunStart>());
      assert(vs.size()==1);
      vs[0]->print(std::cout) << std::endl;

      std::ofstream fout("MpsAnalysis.C");
      fout << "{" << std::endl;

      std::ostringstream sFile;
      sFile << "MpsAnalysisRun" << std::setfill('0') << std::setw(6)
	    << vs[0]-> runNumber() << ".root";
      _rootFile = new TFile(sFile.str().c_str(),"RECREATE");

      for(unsigned x(0);x<168;x++) {
	for(unsigned y(0);y<168;y++) {
	  std::ostringstream slab;
	  slab << "Mono" << std::setfill('0')
	       << std::setw(3) << x
	       << std::setw(3) << y;
	  
	  std::ostringstream stit;
	  stit << "X=" << std::setfill('0')
	       << std::setw(3) << x << " Y="
	       << std::setw(3) << y;
	  
	  if(x<84) {
	    _hMono[x][y]=new TH2F((std::string("h")+slab.str()).c_str(),stit.str().c_str(),
				  128,0.0,4096.0,
				  20,0.5,20.5);
	    _pMono[x][y]=new TProfile((std::string("p")+slab.str()).c_str(),stit.str().c_str(),
				  128,0.0,4096.0,
				  0.5,20.5);
	  } else {
	    _hMono[x][y]=new TH2F((std::string("h")+slab.str()).c_str(),stit.str().c_str(),
				  128,0.0,4096.0,
				  20,0.5,20.5);
	    _pMono[x][y]=new TProfile((std::string("p")+slab.str()).c_str(),stit.str().c_str(),
				  128,0.0,4096.0,
				  0.5,20.5);
	  }

	  fout << slab.str() << "->Draw();" << std::endl;
	}
      }

      fout << "}" << std::endl;
      fout.close();

      break;
    }
 
    case RcdHeader::runEnd: {
      // Get list of IlcRunEnd subrecords
      // There should only be one and it should only
      // be in the runEnd record
      std::vector<const IlcRunEnd*>
        ve(accessor.access<IlcRunEnd>());
      assert(ve.size()==1);
      ve[0]->print(std::cout) << std::endl;

      summary();
      break;
    }
  
    case RcdHeader::configurationStart: {
      std::vector<const IlcConfigurationStart*>
        vs(accessor.access<IlcConfigurationStart>());
      assert(vs.size()==1);
      vs[0]->print(std::cout) << std::endl;

      std::vector<const MpsLocationData<MpsPcb1ConfigurationData>* >
	v(accessor.access< MpsLocationData<MpsPcb1ConfigurationData> >());

      assert(v.size()==2);
      _pcb1ConfigurationData=*(v[1]->data());
      _pcb1ConfigurationData.print(std::cout) << std::endl;

      std::vector<const MpsLocationData<MpsUsbDaqConfigurationData>* >
	w(accessor.access< MpsLocationData<MpsUsbDaqConfigurationData> >());

      assert(w.size()==2);
      _usbDaqConfigurationData=*(w[1]->data());
      //_usbDaqConfigurationData.print(std::cout) << std::endl;

      break;
    }

    case RcdHeader::configurationEnd: {
      std::vector<const IlcConfigurationEnd*>
        ve(accessor.access<IlcConfigurationEnd>());
      assert(ve.size()==1);
      ve[0]->print(std::cout) << std::endl;

      break;
    }
  
    case RcdHeader::bunchTrain: {
      std::vector<const MpsLocationData<MpsSensor1BunchTrainData>* >
	v(accessor.access< MpsLocationData<MpsSensor1BunchTrainData> >());
      assert(v.size()==1);

      //v[0]->print(std::cout) << std::endl;
      std::vector<MpsSensor1Hit> vh(v[0]->data()->hitVector());

	/*
	  const MpsSensor1BunchTrainDatum *p(v[i]->data()->data());
	for(unsigned region(0);region<4;region++) {
	  for(unsigned j(0);j<v[i]->data()->numberOfRegionHits(region);j++) {
	    if(region<2) _hMono[0][p->row()]->Fill(_pcb1ConfigurationData.i12MSOBias1(),p->timeStamp());
	    else         _hMono[0][p->row()]->Fill(_pcb1ConfigurationData.i34MSOBias1(),p->timeStamp());
	    p++;
	  }
	}
	*/

      unsigned t[168][168][2];
      memset(&(t[0][0][0]),0x88,2*168*168*sizeof(unsigned));
      //bool a[168][168];
      //memset(&(a[0][0]),0,168*168*sizeof(bool));
      
      for(unsigned j(0);j<vh.size();j++) {
	bool printOut(vh[j].pixelX()==135 && vh[j].pixelY()==64);
	printOut=false;
	if(printOut) vh[j].print();
	
	if(t[vh[j].pixelX()][vh[j].pixelY()][1]==vh[j].timeStamp()+1) {
	  t[vh[j].pixelX()][vh[j].pixelY()][1]=vh[j].timeStamp();
	  if(printOut) std::cout << "Extended; range " << t[vh[j].pixelX()][vh[j].pixelY()][0]
				     << " to " << t[vh[j].pixelX()][vh[j].pixelY()][1] << std::endl;
	} else {
	  if(t[vh[j].pixelX()][vh[j].pixelY()][0]<1000000) {
	    if(printOut) std::cout << "Completed; range " << t[vh[j].pixelX()][vh[j].pixelY()][0]
				   << " to " << t[vh[j].pixelX()][vh[j].pixelY()][1] << std::endl;
	    
	    if(t[vh[j].pixelX()][vh[j].pixelY()][1]>0) { // Only for hits away from start
	      if(vh[j].pixelX()<84) {
		_hMono[vh[j].pixelX()][vh[j].pixelY()]->Fill(_pcb1ConfigurationData.i12MSOBias1(),
							     t[vh[j].pixelX()][vh[j].pixelY()][0]-t[vh[j].pixelX()][vh[j].pixelY()][1]+1);
		_pMono[vh[j].pixelX()][vh[j].pixelY()]->Fill(_pcb1ConfigurationData.i12MSOBias1(),
							     t[vh[j].pixelX()][vh[j].pixelY()][0]-t[vh[j].pixelX()][vh[j].pixelY()][1]+1);
	      } else {
		_hMono[vh[j].pixelX()][vh[j].pixelY()]->Fill(_pcb1ConfigurationData.i34MSOBias1(),
							     t[vh[j].pixelX()][vh[j].pixelY()][0]-t[vh[j].pixelX()][vh[j].pixelY()][1]+1);
		_pMono[vh[j].pixelX()][vh[j].pixelY()]->Fill(_pcb1ConfigurationData.i34MSOBias1(),
							     t[vh[j].pixelX()][vh[j].pixelY()][0]-t[vh[j].pixelX()][vh[j].pixelY()][1]+1);
	      }
	    }
	  }

	  t[vh[j].pixelX()][vh[j].pixelY()][0]=vh[j].timeStamp();
	  t[vh[j].pixelX()][vh[j].pixelY()][1]=vh[j].timeStamp();
	  if(printOut) std::cout << "  Started; range " << t[vh[j].pixelX()][vh[j].pixelY()][0]
				 << " to " << t[vh[j].pixelX()][vh[j].pixelY()][1] << std::endl;
	}
      }

      for(unsigned x(0);x<168;x++) {
	for(unsigned y(0);y<168;y++) {
	  bool printOut(x==135 && y==64);
	printOut=false;

	  if(t[x][y][0]<1000000) {
	    if(printOut) std::cout << "Completed; range " << t[x][y][0]
				   << " to " << t[x][y][1] << std::endl;
	    
	    if(t[x][y][1]>0) {
	      if(x<84) {
		_hMono[x][y]->Fill(_pcb1ConfigurationData.i12MSOBias1(),
				   t[x][y][0]-t[x][y][1]+1);
		_pMono[x][y]->Fill(_pcb1ConfigurationData.i12MSOBias1(),
				   t[x][y][0]-t[x][y][1]+1);
	      } else {
		_hMono[x][y]->Fill(_pcb1ConfigurationData.i34MSOBias1(),
				   t[x][y][0]-t[x][y][1]+1);
		_pMono[x][y]->Fill(_pcb1ConfigurationData.i34MSOBias1(),
				   t[x][y][0]-t[x][y][1]+1);
	      }
	    }
	  }
	}
      }

      break;
    }
  
    default: {
      break;
    }
    };

    return true;
  }

  void summary() {
    if(_rootFile!=0) {
      //TF1 line("line","pol1",0,100000);
      TF1 line("expo","expo",0,100000);
      _hMonoSummary=new TH1F("hMonoSummary","Summary",168*168,0.0,168.0*168.0);
      
      for(unsigned x(0);x<168;x++) {
	for(unsigned y(0);y<168;y++) {
	  _pMono[x][y]->Approximate(kTRUE);
	  //_pMono[x][y]->Fit("line");
	  _pMono[x][y]->Fit("expo","","",2000,3000);
	  double *p(line.GetParameters());       
	  double *e(line.GetParErrors());    
	  if(line.GetChisquare()>0.0 && e[1]<0.1) {
	    _hMonoSummary->SetBinContent(168*x+y+1,p[1]);
	    _hMonoSummary->SetBinError(168*x+y+1,e[1]);
	  }
	}
      }
      
      _rootFile->Write();
      _rootFile->Close();
      delete _rootFile;
      _rootFile=0;
    }
  }


private:
  MpsPcb1ConfigurationData _pcb1ConfigurationData;
  MpsUsbDaqConfigurationData _usbDaqConfigurationData;

  TFile* _rootFile;
  TH1F *_hMonoSummary;
  TH2F *_hMono[168][168];
  TProfile *_pMono[168][168];
};

#endif
