#ifndef HstCosmics_HH
#define HstCosmics_HH

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>

#include "TROOT.h"
#include "TStyle.h"
#include "TCanvas.h"
#include "TF1.h"
#include "TH1D.h"
#include "TH2D.h"
#include "TPostScript.h"

#include "UtlRollingAverage.hh"
#include "HstBase.hh"
#include "HstTGraphErrors.hh"

#include "CrcFeEventData.hh"
#include "CrcLocationData.hh"
#include "BmlHodEventData.hh"
#include "HodTrack.hh"
#include "DaqEvent.hh"
#include "SubAccessor.hh"

Double_t DoubleG(Double_t *x, Double_t *par) {

  double mean0=par[2];
  double mean1=par[2]+par[0];
  double sigma0=par[0]/par[1];
  double sigma1=par[3];
  double norm0=par[4];
  double norm1=par[5];

  return norm0*exp(-0.5*(x[0]-mean0)*(x[0]-mean0)/(sigma0*sigma0))
        +norm1*exp(-0.5*(x[0]-mean1)*(x[0]-mean1)/(sigma1*sigma1));
}

class HstCosmics : public HstBase {

public:
  HstCosmics(unsigned c, double x, double y, bool i=true) :
    HstBase(i), _firstRecordTime(0), _chipMask(c), _deltax(x), _deltay(y) {

    gStyle->SetOptFit(1);

    TH1D *p(0);
    p=hist1("Record Types",20,0.0,20.0);
    p=hist1("Hodoscope occupancy",80,0.0,80.0);
    p=hist1("Hodoscope occupancy for good tracks",80,0.0,80.0);
    p=hist1("Number of high ADC values",20,0.0,20.0);
    p=hist1("Channel occupancy for high ADC values",216,0.0,216.0);
    p=hist1("High ADC x - interpolation x",100,-25.0,25.0);
    p=hist1("High ADC y - interpolation y",100,-25.0,25.0);
    p=hist1("High ADC x shifted - interpolation x",100,-5.0,5.0);
    p=hist1("High ADC y shifted - interpolation y",100,-5.0,5.0);
    p=hist1("ADC values for close good tracks",100,-50.0,150.0);
    p=hist1("Hodoscope efficiency for good tracks",80,0.0,80.0);
    p=hist1("Channel occupancy for odd ADC values",216,0.0,216.0);

    TH2D *q(0);
    q=hist2("Hodoscope interpolation",140,-15.0,55.0,82,0.0,41.0);
    q=hist2("Hodoscope interpolation for high ADC values",140,-15.0,55.0,82,0.0,41.0);
    q=hist2("Channel ADC values",216,0.0,216.0,60,-1000.0,2000.0);
    q=hist2("Channel ADC values above pedestal",216,0.0,216.0,100,-50.0,150.0);

    _summaryc[0].SetTitle("Fit Signal vs Chip");
    _summaryc[1].SetTitle("Fit Signal/Noise vs Chip");
    _summarycc[0].SetTitle("Fit Signal vs 18*Chip+Chan");
    _summarycc[1].SetTitle("Fit Signal/Noise vs 18*Chip+Chan");

    for(unsigned chip(0);chip<12;chip++) {
      ostringstream sout;
      sout << "Chip " << chip << ", ADC values for close good tracks";
      _histc[chip]=new TH1D(sout.str().c_str(),sout.str().c_str(),
			    100,-50.0,150.0);

      for(unsigned chan(0);chan<18;chan++) {
	ostringstream sout;
	sout << "Chip " << chip << ", Channel " << chan << ", ";

	std::string s(sout.str()+"ADC values for close good tracks");
	_histcc[chip][chan]=new TH1D(s.c_str(),s.c_str(),100,-50.0,150.0);

	std::string g(sout.str()+" Pedestal vs Time (Days)");
	_graph[chip][chan][0].SetTitle(g.c_str());
	_graph[chip][chan][0].SetMarkerColor(1);
	_graph[chip][chan][0].SetMarkerStyle(20);

	std::string n(sout.str()+" Noise vs Time (Days)");
	_graph[chip][chan][1].SetTitle(n.c_str());
	_graph[chip][chan][1].SetMarkerColor(1);
	_graph[chip][chan][1].SetMarkerStyle(20);
      }
    }

    _nAdc=0;
  }

  virtual ~HstCosmics() {
  }

  bool record(const RcdRecord &r) {
    _hist1Vector[0]->Fill(r.recordType());

    if(r.recordType()!=RcdHeader::event) return true;

    if(_firstRecordTime==0) {
      _firstRecordTime=r.recordTime().seconds();
      _pTime=_firstRecordTime;
    }
    const unsigned rTime(r.recordTime().seconds());

    SubAccessor extracter(r);

    //std::vector<const DaqEvent*> z(extracter.extract<DaqEvent>());
    //assert(z.size()==1);
    //if(z[0]->eventNumberInRun()>13626) r.print(std::cout);

    std::vector<const CrcLocationData<CrcBeTrgPollData>*>
      vp(extracter.extract< CrcLocationData<CrcBeTrgPollData> >());
    assert(vp.size()==1);
    //vp[0]->print(cout);


    std::vector<const BmlHodEventData*> x(extracter.extract<BmlHodEventData>());
    assert(x.size()==1);

    HodTrack t(*x[0]);
    double xy[2];
    bool goodTrack(t.track(xy));

    if(vp[0]->data()->tries()==0) {
      cout << "*** No of tries = 0" << endl;
      vp[0]->print(cout);
      goodTrack=false;
    }

    for(unsigned i(0);i<16;i++) {
      if(x[0]->hit(BmlHodEventData::x1,i)) _hist1Vector[1]->Fill(i   );
      if(x[0]->hit(BmlHodEventData::y1,i)) _hist1Vector[1]->Fill(i+20);
      if(x[0]->hit(BmlHodEventData::x2,i)) _hist1Vector[1]->Fill(i+40);
      if(x[0]->hit(BmlHodEventData::y2,i)) _hist1Vector[1]->Fill(i+60);

      if(goodTrack) {
	if(x[0]->hit(BmlHodEventData::x1,i)) _hist1Vector[2]->Fill(i   );
	if(x[0]->hit(BmlHodEventData::y1,i)) _hist1Vector[2]->Fill(i+20);
	if(x[0]->hit(BmlHodEventData::x2,i)) _hist1Vector[2]->Fill(i+40);
	if(x[0]->hit(BmlHodEventData::y2,i)) _hist1Vector[2]->Fill(i+60);
      }
    }

    if(goodTrack) _hist2Vector[0]->Fill(xy[0],xy[1]);

    std::vector<double> ghx,ghy;
    std::vector<const CrcLocationData<CrcVlinkEventData>*>
      v(extracter.extract< CrcLocationData<CrcVlinkEventData> >());

    //for(unsigned i(0);i<v.size();i++) {
    unsigned i(0); {
      //for(unsigned fe(1);fe<5;fe+=2) {
      unsigned fe(1); {
	const CrcVlinkFeData *fd(v[i]->data()->feData(fe));
	if(fd!=0) {
	  for(unsigned chan(0);chan<v[i]->data()->feNumberOfAdcSamples(fe) && chan<18;chan++) {
	    //unsigned chan(0); {
	    const CrcVlinkAdcSample *as(fd->adcSample(chan));
	    if(as!=0) {
	      for(unsigned chip(0);chip<12;chip++) {
		//unsigned chip(6); {
		
		unsigned ix=3*(chip%6)+(chan%3);
		unsigned iy=6*(chip/6)-(chan/3)+5;
		if(fe==3) iy=11-iy;
		
		double x=ix+(ix/6)*0.21;
		double y=iy+(iy/6)*0.21;
		
		int xx=as->adc(chip);
		_hist2Vector[2]->Fill(18*chip+chan+0.5,xx);
		
		// Find signal etc if enough events for good pedestal
		if(_average[chip][chan].totalNumber()>100) {
		  double signal(xx-_average[chip][chan].average());

		  _hist2Vector[3]->Fill(18*chip+chan+0.5,signal);
		  
		  if((_chipMask&(1<<chip))!=0 && signal>40.0) {
		    if(signal>100.0) {
		      //cout << "***** Odd hit Chip/Chan = " << chip << "/"
		      //   << chan << " signal = " << signal 
		      //   << " adc = " << xx << endl;
		      _hist1Vector[11]->Fill(18*chip+chan+0.5);
		    } else {
		      _hist1Vector[4]->Fill(18*chip+chan+0.5);
		      ghx.push_back(x);
		      ghy.push_back(y);
		    }
		  }

		  if(goodTrack && fabs(x-_deltax-xy[0])<0.9 && fabs(y-_deltay-xy[1])<0.9) {
		    _hist1Vector[9]->Fill(signal);
		    _histc[chip]->Fill(signal);
		    _histcc[chip][chan]->Fill(signal);
		  }
		}

		// Calculate pedestal if far away
		if(goodTrack && fabs(x-_deltax-xy[0])>4.0 && fabs(y-_deltay-xy[1])>4.0) {
		  _average[chip][chan]+=xx;
		  _pedestal[chip][chan]+=xx;
		}

		/*		
		_adc[chip][chan][_nAdc%100]=xx;
		_ped[chip][chan]=0.0;
		//for(unsigned j(0);j<100;j++) _ped[chip][chan]+=xx; ???
		for(unsigned j(0);j<100;j++) _ped[chip][chan]+=_adc[chip][chan][j];
		_ped[chip][chan]*=0.01;
		*/
	      }
	    }
	  }
	}
      }
      _nAdc++;
    }
  
    _hist1Vector[3]->Fill(ghx.size());

    if(goodTrack) {
      if(ghx.size()>0) _hist2Vector[1]->Fill(xy[0],xy[1]);
    
      for(unsigned i(0);i<ghx.size();i++) {
	_hist1Vector[5]->Fill(ghx[i]-xy[0]);
	_hist1Vector[6]->Fill(ghy[i]-xy[1]);
	_hist1Vector[7]->Fill(ghx[i]-_deltax-xy[0]);
	_hist1Vector[8]->Fill(ghy[i]-_deltay-xy[1]);
      }
    }

    if((rTime-_pTime)>1800) {
      double t((0.5*(rTime+_pTime)-_firstRecordTime)/(24.0*3600.0));
      for(unsigned chip(0);chip<12;chip++) {
	for(unsigned chan(0);chan<18;chan++) {
	  //if(chip==0 && chan==0) {
	    _graph[chip][chan][0].AddPoint(t,_pedestal[chip][chan].average(),0,_pedestal[chip][chan].errorOnAverage());
	    _graph[chip][chan][1].AddPoint(t,_pedestal[chip][chan].sigma()  ,0,_pedestal[chip][chan].errorOnSigma());
	    //} else {
	    //_graph[chip][chan][0].AddPoint(t,_average[chip][chan].average(),0,_average[chip][chan].errorOnAverage());
	    //_graph[chip][chan][1].AddPoint(t,_average[chip][chan].sigma()  ,0,_average[chip][chan].errorOnSigma());
	    //_average[chip][chan].reset();
	    //}
	}
      }
      _pTime=rTime;
    }

    return true;
  }

  bool update() {
    std::cout << "Updating..." << std::endl;

    _hist1Vector[10]->Divide(_hist1Vector[2],_hist1Vector[1]);

    TF1 *func = new TF1("DoubleG",DoubleG,-50,150,6);
    Double_t par[6]={50.0,8.0,0.0,15.0,1600.0,50.0};
    func->SetParameters(par);
    _hist1Vector[9]->Fit("DoubleG","",0,-20.0,70.0);
    
    for(unsigned chip(0);chip<12;chip++) {
      Double_t par[6]={50.0,8.0,0.0,15.0,1000.0,100.0};
      func->SetParameters(par);
      _histc[chip]->Fit("DoubleG","Q",0,-20.0,70.0);

      double *p(func->GetParameters());       
      double *e(func->GetParErrors());        
      if((p[0]-e[0]>0.0) && (p[0]+e[0])<100.0) _summaryc[0].AddPoint(chip+0.5,p[0],0,e[0]);
      if((p[1]-e[1]>0.0) && (p[1]+e[1])<10.0)  _summaryc[1].AddPoint(chip+0.5,p[1],0,e[1]);

      for(unsigned chan(0);chan<18;chan++) {
	Double_t par[6]={50.0,8.0,0.0,15.0,100.0,10.0};
	func->SetParameters(par);
	_histcc[chip][chan]->Fit("DoubleG","Q",0,-20.0,70.0);

	double *p(func->GetParameters());       
	double *e(func->GetParErrors());        
	if((p[0]-e[0]>0.0) && (p[0]+e[0])<100.0) _summarycc[0].AddPoint(18*chip+chan+0.5,p[0],0,e[0]);
	if((p[1]-e[1]>0.0) && (p[1]+e[1])<10.0)  _summarycc[1].AddPoint(18*chip+chan+0.5,p[1],0,e[1]);
      }
    }

    for(unsigned i(0);i<_canvas1Vector.size();i++) {
      _canvas1Vector[i]->cd();
      _hist1Vector[i]->Draw();
      _canvas1Vector[i]->Update();
    }

    for(unsigned i(0);i<_canvas2Vector.size();i++) {
      _canvas2Vector[i]->cd();
      _hist2Vector[i]->Draw("box");
      _canvas2Vector[i]->Update();
    }

    TCanvas c("HC","HC",0,0,600,400);
    c.cd();

    c.Clear();
    c.Divide(4,3);
    for(unsigned chip(0);chip<12;chip++) {
      c.cd(chip+1);
      _histc[chip]->Draw();
    }      
    c.Update();

    for(unsigned chip(0);chip<12;chip++) {
      c.Clear();
      c.Divide(6,3);
      for(unsigned chan(0);chan<18;chan++) {
	c.cd(chan+1);
	_histcc[chip][chan]->Draw();
      }
      c.Update();
    }      

    for(unsigned chip(0);chip<12;chip++) {
      c.Clear();
      c.Divide(6,3);
      for(unsigned chan(0);chan<18;chan++) {
	c.cd(chan+1);
	_graph[chip][chan][0].Draw("AP");
      }
      c.Update();

      c.Clear();
      c.Divide(6,3);
      for(unsigned chan(0);chan<18;chan++) {
	c.cd(chan+1);
	_graph[chip][chan][1].Draw("AP");
      }
      c.Update();
    }

    c.Clear();
    c.Divide(1,2);
    c.cd(1);
    _summaryc[0].Draw("AP");
    c.cd(2);
    _summaryc[1].Draw("AP");
    c.Update();

    c.Clear();
    c.Divide(1,2);
    c.cd(1);
    _summarycc[0].Draw("AP");
    c.cd(2);
    _summarycc[1].Draw("AP");
    c.Update();

    return true;
  }

  bool postscript(std::string file) {
    std::cout << "HstCosmics::postscript()  Writing to file "
	      << file << std::endl;

    TPostScript ps(file.c_str(),112);
    update();
    ps.Close();

    return true;
  }

private:
  TH1D* hist1(std::string t, unsigned n, double l, double h) {
    unsigned num(_hist1Vector.size());
    ostringstream oss;
    oss << num;

    _canvas1Vector.push_back(new TCanvas((std::string("Canvas1[")+oss.str()+std::string("]")).c_str(),
					 (t+" canvas").c_str(),10+10*num,10+10*num,600,400));
    _hist1Vector.push_back(new TH1D((std::string("TH1D[")+oss.str()+std::string("]")).c_str(),
				    t.c_str(),n,l,h));
    return _hist1Vector[num];
  }
  TH2D* hist2(std::string t, unsigned nx, double lx, double hx, unsigned ny, double ly, double hy) {
    unsigned num(_hist2Vector.size());
    ostringstream oss;
    oss << num;

    _canvas2Vector.push_back(new TCanvas((std::string("Canvas2[")+oss.str()+std::string("]")).c_str(),
					 (t+" canvas").c_str(),500+10*num,10+10*num,600,400));
    _hist2Vector.push_back(new TH2D((std::string("TH2D[")+oss.str()+std::string("]")).c_str(),
				    t.c_str(),nx,lx,hx,ny,ly,hy));
    return _hist2Vector[num];
  }

  std::vector<TCanvas*> _canvas1Vector;
  std::vector<TCanvas*> _canvas2Vector;
  std::vector<TPad*> _pad1Vector;
  std::vector<TH1D*> _hist1Vector;
  std::vector<TH2D*> _hist2Vector;
  TH1D *_histc[12];
  TH1D *_histcc[12][18];

  unsigned _firstRecordTime,_pTime;
  UtlRollingAverage _average[12][18];
  UtlRollingAverage _pedestal[12][18];
  HstTGraphErrors _graph[12][18][2];
  HstTGraphErrors _summaryc[2];
  HstTGraphErrors _summarycc[2];

  const unsigned _chipMask;
  const double _deltax,_deltay;
  
  unsigned _nAdc;
  int _adc[12][18][100];
  double _ped[12][18];
};

#endif
