#ifndef HstCosmics4_HH
#define HstCosmics4_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 "EmcEventEnergy.hh"
#include "CrcFeEventData.hh"
#include "CrcLocationData.hh"
#include "BmlHodEventData.hh"
#include "HodTrack.hh"
#include "DaqEvent.hh"
#include "SubAccessor.hh"

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

  if(par[0]<0.0) return 1.0e6;

  double mean0=par[1];
  double sigma0=par[0];
  double norm0=par[2];

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

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 HstCosmics4 : public HstBase {

public:
  HstCosmics4(unsigned c, double x, double y, bool i=true) :
    HstBase(i), _canvas("Canvas","Canvas",10,10,600,400) {

    gStyle->SetOptFit(1);

    _hTotal=new TH1D("ADC values for neighbouring hits","ADC values for neighbouring hits",100,-50.0,150.0);

    // Layers
    _layerSummary[0].SetTitle("Layer summary for signal");
    _layerSummary[1].SetTitle("Layer summary for signal/noise");
    _layerSummary[2].SetTitle("Layer summary for pedestal");
    _layerSummary[3].SetTitle("Layer summary for signal width");
    _layerSummary[4].SetTitle("Layer summary for noise peak");
    _layerSummary[5].SetTitle("Layer summary for signal peak");

    for(unsigned z(0);z<10;z++) {
      std::ostringstream sout;
      sout << "Layer " << z << " ADC values for neighbouring hits";
      _hLayer[z]=new TH1D(sout.str().c_str(),sout.str().c_str(),100,-50.0,150.0);

      std::ostringstream sout2;
      sout2 << "Layer " << z << " Channel hit occupancy";
      _hOccupancy[z]=new TH1D(sout2.str().c_str(),sout2.str().c_str(),18*18,0.0,18.0*18.0);
    }

    // Channels
    for(unsigned z(0);z<10;z++) {
      std::ostringstream sout;
      sout << "Layer " << z;
      _channelSummary[z][0].SetTitle((sout.str()+" Channel summary for signal").c_str());
      _channelSummary[z][1].SetTitle((sout.str()+" Channel summary for signal/noise").c_str());
      _channelSummary[z][2].SetTitle((sout.str()+" Channel summary for pedestal").c_str());
      _channelSummary[z][3].SetTitle((sout.str()+" Channel summary for signal width").c_str());
      _channelSummary[z][4].SetTitle((sout.str()+" Channel summary for noise peak").c_str());
      _channelSummary[z][5].SetTitle((sout.str()+" Channel summary for signal peak").c_str());

      for(unsigned x(0);x<18;x++) {
	for(unsigned y(6);y<18;y++) {
	  std::ostringstream sout;
	  sout << "Layer " << z
	       << " X " << x
	       << " Y " << y;
	  //	       << " ADC values";
	  _hChannel[x][y][z]=new TH1D(sout.str().c_str(),sout.str().c_str(),100,-50.0,150.0);
	}
      }
    }

    /*
    TH1D *p(0);
    for(unsigned z(0);z<10;z++) {
      for(unsigned h(1);h<30;h+=4) {
	std::ostringstream sout;
	sout << "Layer " ADC values for neighbouring hits";


    _hist1Vector.push_back(new TH1D((std::string("TH1D[")+oss.str()+std::string("]")).c_str(),
				    t.c_str(),n,l,h));
	p=hist1(sout.str(),100,-50.0,150.0);
      }

      std::ostringstream sout2;
      sout2 << "Layer " << z;
      _summaryc[z][0].SetTitle((sout2.str()+" Fit Signal vs HOLD").c_str());
      _summaryc[z][1].SetTitle((sout2.str()+" Fit Signal/Noise vs HOLD").c_str());
    }

    p=hist1("Record Types",20,0.0,20.0);

    TH2D *q(0);
    //q=hist2("Hodoscope interpolation",140,-15.0,55.0,82,0.0,41.0);
    */

    /*
    _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++) {
      std::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++) {
	std::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 ~HstCosmics4() {
  }

  bool record(const RcdRecord &r) {
    return false;
  }

  bool event(const RcdRecord &r, const EmcEventEnergy en) {
    if(r.recordType()!=RcdHeader::event) return true;

    double thresh(30.0);

    for(unsigned x(0);x<18;x++) {
      for(unsigned y(6);y<18;y++) {
	for(unsigned z(0);z<10;z++) {
	  if(en.energy(EmcPhysicalPad(x,y,z))>thresh) {
	    _hOccupancy[z]->Fill(x+18*y+0.5);
	  }

	  bool neighbourHit(false);
	  if(z>0         && en.energy(EmcPhysicalPad(x,y  ,z-1))>thresh) neighbourHit=true;
	  if(z>0 && y> 0 && en.energy(EmcPhysicalPad(x,y-1,z-1))>thresh) neighbourHit=true;
	  if(z>0 && y<17 && en.energy(EmcPhysicalPad(x,y+1,z-1))>thresh) neighbourHit=true;
	  if(z<9         && en.energy(EmcPhysicalPad(x,y  ,z+1))>thresh) neighbourHit=true;
	  if(z<9 && y> 0 && en.energy(EmcPhysicalPad(x,y-1,z+1))>thresh) neighbourHit=true;
	  if(z<9 && y<17 && en.energy(EmcPhysicalPad(x,y+1,z+1))>thresh) neighbourHit=true;

	  if(neighbourHit) {
	    double e(en.energy(EmcPhysicalPad(x,y,z)));
	    _hTotal->Fill(e);
	    _hLayer[z]->Fill(e);
	    _hChannel[x][y][z]->Fill(e);
	  }
	}
      }
    }

    return true;
  }

  bool fitDoubleG(TH1D *h, const char *opt, double *par, double *err) {
    TF1 funs("SingleG",SingleG,-50,150,3);
    TF1 func("DoubleG",DoubleG,-50,150,6);

    Double_t *p,*e;

    Double_t parn[3];
    parn[0]=7.0;
    parn[1]=0.0;
    parn[2] =h->GetBinContent(h->FindBin(-1.0));
    parn[2]+=h->GetBinContent(h->FindBin( 1.0));
    parn[2]+=h->GetBinContent(h->FindBin( 3.0));
    parn[2]/=3.0;
    funs.SetParameters(parn);

    h->Fit("SingleG",opt,0,-20.0,20.0);
    p=funs.GetParameters();
    parn[0]=p[0];
    parn[1]=p[1];
    parn[2]=p[2];

    Double_t pars[3];
    pars[0]=10.0;
    pars[1]=50.0;
    pars[2] =h->GetBinContent(h->FindBin(49.0));
    pars[2]+=h->GetBinContent(h->FindBin(51.0));
    pars[2]+=h->GetBinContent(h->FindBin(53.0));
    pars[2]/=3.0;
    funs.SetParameters(pars);

    h->Fit("SingleG",opt,0,30.0,70.0);
    p=funs.GetParameters();
    pars[0]=p[0];
    pars[1]=p[1];
    pars[2]=p[2];

    Double_t pard[6];
    pard[0]=pars[1];
    pard[1]=pars[1]/parn[0];
    pard[2]=parn[1];
    pard[3]=pars[0];
    pard[4]=parn[2];
    pard[5]=pars[2];
    func.SetParameters(pard);

    h->Fit("DoubleG",opt,0,-20.0,65.0);
    p=func.GetParameters();
    e=func.GetParErrors();

    for(unsigned i(0);i<6;i++) {
      par[i]=p[i];
      err[i]=e[i];
    }

    return true;
  }

  bool fit() {
    std::cout << "Fitting..." << std::endl;

    double par[6],err[6];

    fitDoubleG(_hTotal,"",par,err);

    // Layers
    for(unsigned z(0);z<10;z++) {
      fitDoubleG(_hLayer[z],"Q",par,err);
      for(unsigned i(0);i<6;i++) {
	_layerSummary[i].AddPoint(z+0.5,par[i],0,err[i]);
      }
    }

    // Channels
    for(unsigned z(0);z<10;z++) {
      for(unsigned x(0);x<18;x++) {
	for(unsigned y(6);y<18;y++) {
	  _gChannel[x][y][z]=false;

	  if(_hChannel[x][y][z]->GetEntries()>20) {
	    fitDoubleG(_hChannel[x][y][z],"Q",par,err);

	    if((par[0]-err[0])>-100.0 && (par[0]+err[0])<200.0) {
	      _gChannel[x][y][z]=true;
	      for(unsigned i(0);i<6;i++) {
		_channelSummary[z][i].AddPoint(x+18*y+0.5,par[i],0,err[i]);
	      }

	    } else {
	      std::cout << "X,Y,Z = " << x << " " << y << " " << z << " fitted pars ="
			<< " " << par[0] << " " << par[1] << " " << par[2]
			<< " " << par[3] << " " << par[4] << " " << par[5] << std::endl;
	    }
	  }
	}
      }
    }

    return true;
  }

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

    //_canvas.Clear();
    _hTotal->Draw();
    _canvas.Update();

    for(unsigned i(0);i<6;i++) {    
      _canvas.Clear();
      _layerSummary[i].Draw("AP");
      _canvas.Update();
    }

    for(unsigned z(0);z<10;z++) {
      //_canvas.Clear();
      _hLayer[z]->Draw();
      _canvas.Update();
    }

    for(unsigned z(0);z<10;z++) {
      //_canvas.Clear();
      _hOccupancy[z]->Draw();
      _canvas.Update();
    }

    for(unsigned z(0);z<10;z++) {
      for(unsigned i(0);i<6;i++) {
	_canvas.Clear();
	_channelSummary[z][i].Draw("AP");
	_canvas.Update();
      }
    }

    for(unsigned z(0);z<10;z++) {
      for(unsigned x(0);x<18;x++) {
	for(unsigned y(6);y<18;y++) {
	  //_canvas.Clear();
	  if(!_gChannel[x][y][z]) {
	    _hChannel[x][y][z]->Draw();
	    _canvas.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();
    }

    for(unsigned z(0);z<10;z++) {    
      c.Clear();
      c.Divide(1,2);
      c.cd(1);
      _summaryc[z][0].Draw("AP");
      c.cd(2);
      _summaryc[z][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 << "HstCosmics4::postscript()  Writing to file "
	      << file << std::endl;

    fit();

    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());
    std::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());
    std::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];
*/

  TCanvas _canvas;
  HstTGraphErrors _layerSummary[6];
  HstTGraphErrors _channelSummary[10][6];

  TH1D *_hTotal;
  TH1D *_hLayer[10];
  TH1D *_hChannel[18][18][10];
  TH1D *_hOccupancy[10];
  bool _gChannel[18][18][10];
};

#endif
