#ifndef HstIntDac_HH
#define HstIntDac_HH

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

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

// dual/inc/utl
#include "UtlAverage.hh"

// dual/inc/rcd
#include "RcdRecord.hh"

// dual/inc/crc
#include "CrcLocationData.hh"
#include "CrcVlinkEventData.hh"

// dual/inc/daq
#include "DaqEvent.hh"

// dual/inc/sub
#include "SubAccessor.hh"

// dual/inc/hst
#include "HstBase.hh"

Double_t QuadErr(Double_t *x, Double_t *par) {
  return sqrt(par[0]*par[0]+x[0]*x[0]*par[1]*par[1]);
}

class HstIntDac : public HstBase {

public:
  HstIntDac(unsigned s=7, unsigned f=3, unsigned c=9, bool inter=false) : 
    HstBase(inter),
    //    _application("Noise Application",0,0),
    _slot(s), _fe(f), _chip(c) {

    //	       _canvas("Noise Canvas","Noise Canvas",500,10,600,800) {

    //gROOT->Reset();
    //gROOT->SetStyle("Plain");
    gStyle->SetOptFit(1);

    //_canvas[0].SetCanvasSize(600,800);
    //_canvas[0].SetWindowSize(600,800);
    _canvas[0]=new TCanvas("IntDac Canvas[0]","IntDac Canvas[0]",10,10,600,800),
    _canvas[1]=new TCanvas("IntDac Canvas[1]","IntDac Canvas[1]",20,20,600,800),
    _canvas[2]=new TCanvas("IntDac Canvas[2]","IntDac Canvas[2]",30,30,600,800),
    _canvas[3]=new TCanvas("IntDac Canvas[3]","IntDac Canvas[3]",30,30,400,600),

    _canvas[0]->Divide(1,3);
    _canvas[1]->Divide(1,3);
    _canvas[2]->Divide(1,3);
    _canvas[3]->Divide(1,3);

    for(unsigned fe(0);fe<8;fe++) {
      for(unsigned chip(0);chip<12;chip++) {
	for(unsigned dac(0);dac<2;dac++) {
	  std::ostringstream sout[4];
	  sout[0] << "FE" << fe << ", Chip " << chip << ", ADC mean vs DAC" << dac;
	  sout[1] << "FE" << fe << ", Chip " << chip << ", ADC noise vs DAC" << dac;
	  sout[2] << "FE" << fe << ", Chip " << chip << ", ADC mean residuals vs DAC" << dac;

	  sout[3] << "FE" << fe << ", Chip " << chip << ", ADC noise residuals vs DAC" << dac;

	  for(unsigned i(0);i<4;i++) {
	    _graph[fe][chip][dac][i].SetTitle(sout[i].str().c_str());
	    _graph[fe][chip][dac][i].SetMarkerColor(1);
	    _graph[fe][chip][dac][i].SetMarkerStyle(20);
	  }
	}
      }
    }

    _summary[0][0].Set(96);
    _summary[0][0].SetTitle("On Fit Intercept vs FE/Chip");
    _summary[0][0].SetMarkerColor(1);
    _summary[0][0].SetMarkerStyle(20);

    _summary[0][1].Set(96);
    _summary[0][1].SetTitle("On Fit Slope vs FE/Chip");
    _summary[0][1].SetMarkerColor(1);
    _summary[0][1].SetMarkerStyle(20);

    _summary[0][2].Set(96);
    _summary[0][2].SetTitle("On Fit Chi-Sq/Ndof vs FE/Chip");
    _summary[0][2].SetMarkerColor(1);
    _summary[0][2].SetMarkerStyle(20);

    _summary[1][0].Set(96);
    _summary[1][0].SetTitle("Off Fit Intercept vs FE/Chip");
    _summary[1][0].SetMarkerColor(1);
    _summary[1][0].SetMarkerStyle(20);

    _summary[1][1].Set(96);
    _summary[1][1].SetTitle("Off Fit Slope vs FE/Chip");
    _summary[1][1].SetMarkerColor(1);
    _summary[1][1].SetMarkerStyle(20);

    _summary[1][2].Set(96);
    _summary[1][2].SetTitle("Off Fit Chi-Sq/Ndof vs FE/Chip");
    _summary[1][2].SetMarkerColor(1);
    _summary[1][2].SetMarkerStyle(20);

    _summary[2][0].Set(96);
    _summary[2][0].SetTitle("On Error Fit Baseline Noise vs FE/Chip");
    _summary[2][0].SetMarkerColor(1);
    _summary[2][0].SetMarkerStyle(20);

    _summary[2][1].Set(96);
    _summary[2][1].SetTitle("On Error Fit DAC Noise vs FE/Chip");
    _summary[2][1].SetMarkerColor(1);
    _summary[2][1].SetMarkerStyle(20);

    _summary[2][2].Set(96);
    _summary[2][2].SetTitle("On Error Fit Chi-Sq/Ndof vs FE/Chip");
    _summary[2][2].SetMarkerColor(1);
    _summary[2][2].SetMarkerStyle(20);

    _summary[3][0].Set(96);
    _summary[3][0].SetTitle("Off Error Fit Baseline Noise vs FE/Chip");
    _summary[3][0].SetMarkerColor(1);
    _summary[3][0].SetMarkerStyle(20);

    _summary[3][1].Set(96);
    _summary[3][1].SetTitle("Off Error Fit DAC Noise vs FE/Chip");
    _summary[3][1].SetMarkerColor(1);
    _summary[3][1].SetMarkerStyle(20);

    _summary[3][2].Set(96);
    _summary[3][2].SetTitle("Off Error Fit Chi-Sq/Ndof vs FE/Chip");
    _summary[3][2].SetMarkerColor(1);
    _summary[3][2].SetMarkerStyle(20);
  }

  virtual ~HstIntDac() {
  }

  bool update() {
    update(false);
    return true;
  }

  void update(bool fits=false) {
    std::cout << "Updating..." << std::endl;

    const unsigned nConfig(_vConfig[0].size());

    for(unsigned fe(0);fe<8;fe++) {
      for(unsigned chip(0);chip<12;chip++) {

	unsigned bin(12*fe+chip);
	_vConfig[0][nConfig-1]->SetPoint(bin,bin,_average[fe][chip].average());
	_vConfig[0][nConfig-1]->SetPointError(bin,0.0,_average[fe][chip].errorOnAverage());
	_vConfig[1][nConfig-1]->SetPoint(bin,bin,_average[fe][chip].sigma());
	_vConfig[1][nConfig-1]->SetPointError(bin,0.0,_average[fe][chip].errorOnSigma());
	_vConfig[2][nConfig-1]->SetPoint(bin,bin,_average[fe][chip].number());
	_vConfig[2][nConfig-1]->SetPointError(bin,0.0,0.0);
      }
    }

    double extraError(0.6);

    for(unsigned fe(0);fe<8;fe++) {
      for(unsigned chip(0);chip<12;chip++) {
	for(unsigned dac(0);dac<2;dac++) {
	  unsigned bin(_graph[fe][chip][dac][0].GetN());
	  
	  if(!fits) {
	    if(_dac[(dac+1)%2]==0) {
	      _graph[fe][chip][dac][0].Set(bin);
	      _graph[fe][chip][dac][0].SetPoint(bin,_dac[dac],_average[fe][chip].average());
	      double err(_average[fe][chip].errorOnAverage());
	      if((chip<=5 && dac==0) || (chip>=6 && dac==1)) err=sqrt(err*err+extraError*extraError);
	      _graph[fe][chip][dac][0].SetPointError(bin,0.0,err);
	      
	      _graph[fe][chip][dac][1].Set(bin);
	      _graph[fe][chip][dac][1].SetPoint(bin,_dac[dac],_average[fe][chip].sigma());
	      _graph[fe][chip][dac][1].SetPointError(bin,0.0,_average[fe][chip].errorOnSigma());
	    }
	    



	  } else {

	    _graph[fe][chip][dac][0].Fit("pol1","Q",0,5000.0,50000.0);
	    double *pars=_graph[fe][chip][dac][0].GetFunction("pol1")->GetParameters();
	    double *errs=_graph[fe][chip][dac][0].GetFunction("pol1")->GetParErrors();
	    double chisq=_graph[fe][chip][dac][0].GetFunction("pol1")->GetChisquare();
	    int     nDof=_graph[fe][chip][dac][0].GetFunction("pol1")->GetNDF();
	    if(nDof>0) chisq/=nDof;
	    else       chisq=-1.0;
	    
	    _graph[fe][chip][dac][2].Set(bin);
	    for(unsigned i(0);i<_graph[fe][chip][dac][0].GetN();i++) {
	      double x,y;
	      _graph[fe][chip][dac][0].GetPoint(i,x,y);
	      if(x>=1000.0) {
		_graph[fe][chip][dac][2].SetPoint(i,x,y-pars[0]-x*pars[1]);
		_graph[fe][chip][dac][2].SetPointError(i,0.0,_graph[fe][chip][dac][0].GetErrorY(i));
	      }
	    }
	    
	    if((chip<=5 && dac==0) || (chip>=6 && dac==1)) {
	      _summary[0][0].SetPoint(12*fe+chip,12*fe+chip,pars[0]);
	      _summary[0][0].SetPointError(12*fe+chip,0.0,errs[0]);
	      _summary[0][1].SetPoint(12*fe+chip,12*fe+chip,pars[1]);
	      _summary[0][1].SetPointError(12*fe+chip,0.0,errs[1]);
	      _summary[0][2].SetPoint(12*fe+chip,12*fe+chip,chisq);
	      _summary[0][2].SetPointError(12*fe+chip,0.0,0.0);
	    } else {
	      _summary[1][0].SetPoint(12*fe+chip,12*fe+chip,pars[0]);
	      _summary[1][0].SetPointError(12*fe+chip,0.0,errs[0]);
	      _summary[1][1].SetPoint(12*fe+chip,12*fe+chip,pars[1]);
	      _summary[1][1].SetPointError(12*fe+chip,0.0,errs[1]);
	      _summary[1][2].SetPoint(12*fe+chip,12*fe+chip,chisq);
	      _summary[1][2].SetPointError(12*fe+chip,0.0,0.0);
	    }
	    
	    TF1 quadErr("quadErr",QuadErr,0,100000,2);
	    Double_t qpar[2];
	    qpar[0]=1.3;
	    qpar[1]=0.000036;
	    quadErr.SetParameters(qpar);

	    _graph[fe][chip][dac][1].Fit("quadErr","Q",0,2000.0,50000.0);
	    pars=_graph[fe][chip][dac][1].GetFunction("quadErr")->GetParameters();
	    errs=_graph[fe][chip][dac][1].GetFunction("quadErr")->GetParErrors();
	    chisq=_graph[fe][chip][dac][1].GetFunction("quadErr")->GetChisquare();
	    nDof=_graph[fe][chip][dac][1].GetFunction("quadErr")->GetNDF();
	    if(nDof>0) chisq/=nDof;
	    else       chisq=-1.0;
	    
	    _graph[fe][chip][dac][3].Set(bin);
	    for(unsigned i(0);i<_graph[fe][chip][dac][1].GetN();i++) {
	      double x,y;
	      _graph[fe][chip][dac][1].GetPoint(i,x,y);
	      if(x>=1000.0) {
		_graph[fe][chip][dac][3].SetPoint(i,x,y-QuadErr(&x,pars));
		_graph[fe][chip][dac][3].SetPointError(i,0.0,_graph[fe][chip][dac][1].GetErrorY(i));
	      }
	    }
	    
	    if((chip<=5 && dac==0) || (chip>=6 && dac==1)) {
	      _summary[2][0].SetPoint(12*fe+chip,12*fe+chip,pars[0]);
	      _summary[2][0].SetPointError(12*fe+chip,0.0,errs[0]);
	      _summary[2][1].SetPoint(12*fe+chip,12*fe+chip,pars[1]);
	      _summary[2][1].SetPointError(12*fe+chip,0.0,errs[1]);
	      _summary[2][2].SetPoint(12*fe+chip,12*fe+chip,chisq);
	      _summary[2][2].SetPointError(12*fe+chip,0.0,0.0);
	    } else {
	      _summary[3][0].SetPoint(12*fe+chip,12*fe+chip,pars[0]);
	      _summary[3][0].SetPointError(12*fe+chip,0.0,errs[0]);
	      _summary[3][1].SetPoint(12*fe+chip,12*fe+chip,pars[1]);
	      _summary[3][1].SetPointError(12*fe+chip,0.0,errs[1]);
	      _summary[3][2].SetPoint(12*fe+chip,12*fe+chip,chisq);
	      _summary[3][2].SetPointError(12*fe+chip,0.0,0.0);
	    }
	  }
	}
	_average[fe][chip].reset();
      }
    }
    
    _canvas[0]->cd(1);
    _graph[_fe][_chip][0][0].Draw("AP");
    _canvas[0]->cd(2);
    _graph[_fe][_chip][0][1].Draw("AP");
    //_canvas[0]->cd(3);
    //_graph[_fe][_chip][0][2].Draw("AP");
    _canvas[0]->Update();

    _canvas[1]->cd(1);
    _graph[_fe][_chip][1][0].Draw("AP");
    _canvas[1]->cd(2);
    _graph[_fe][_chip][1][1].Draw("AP");
    //_canvas[1]->cd(3);
    //_graph[_fe][_chip][1][2].Draw("AP");
    _canvas[1]->Update();

    _canvas[2]->cd(1);
    _summary[0][0].Draw("AP");
    _canvas[2]->cd(2);
    _summary[0][1].Draw("AP");
    _canvas[2]->cd(3);
    _summary[0][2].Draw("AP");
    _canvas[2]->Update();

    _canvas[3]->Clear();
    _canvas[3]->Divide(1,3);
    _canvas[3]->cd(1);
    _vConfig[0][nConfig-1]->Draw("AP");
    _canvas[3]->cd(2);
    _vConfig[1][nConfig-1]->Draw("AP");
    _canvas[3]->cd(3);
    _vConfig[2][nConfig-1]->Draw("AP");
    _canvas[3]->Update();
  }

  bool record(const RcdRecord &r) {
    if(r.recordType()==RcdHeader::configurationEnd ||
       r.recordType()==RcdHeader::configurationStop) {
      update(false);
      return true;
    }

    if(r.recordType()==RcdHeader::runEnd ||
       r.recordType()==RcdHeader::runStop) {
      update(true);
      return true;
    }

    SubAccessor accessor(r);
    
    if(r.recordType()==RcdHeader::configurationStart) {
      _dac[0]=0;
      _dac[1]=0;

      std::vector<const CrcLocationData<EmcFeConfigurationData>*>
	v(accessor.access< CrcLocationData<EmcFeConfigurationData> >());

      for(unsigned i(0);i<v.size();i++) {
	_dac[0]=v[i]->data()->dacData(EmcFeConfigurationData::bot);
	_dac[1]=v[i]->data()->dacData(EmcFeConfigurationData::top);
      }

      std::ostringstream sout[3];
      sout[0] << "Average for DAC0 = " << _dac[0] << ", DAC1 = " << _dac[1];
      sout[1] <<   "Noise for DAC0 = " << _dac[0] << ", DAC1 = " << _dac[1];
      sout[2] <<  "Number for DAC0 = " << _dac[0] << ", DAC1 = " << _dac[1];

      TGraphErrors *p(0);

      for(unsigned i(0);i<3;i++) {
	p=new TGraphErrors();
	p->SetTitle(sout[i].str().c_str());
	p->SetMarkerColor(1);
	p->SetMarkerStyle(20);
	_vConfig[i].push_back(p);
      }

      return true;
    }

    if(r.recordType()!=RcdHeader::event) return true;
    
    std::vector<const CrcLocationData<CrcVlinkEventData>*>
      v(accessor.access<CrcLocationData<CrcVlinkEventData> >());
    //    std::vector<const EmcCercFeEventData*>
    // v(accessor.access<EmcCercFeEventData>());

    for(unsigned i(0);i<v.size();i++) {
      if(v[i]->slotNumber()==_slot) {
	for(unsigned j(0);j<8;j++) {
	  for(unsigned chip(0);chip<12;chip++) {
	    for(unsigned chan(0);chan<v[i]->data()->numberOfSamples(j);chan++) {
	      _average[j][chip].event(v[i]->data()->adc(j,chip,chan));
	    }
	  }
	}
      }
    }

    return true;
  }

  bool postscript(std::string file) {
    TCanvas c("PS Canvas","PS Canvas",10,10,600,400);
    c.cd();

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

    for(unsigned fe(0);fe<8;fe++) {
      for(unsigned chip(0);chip<12;chip++) {
	for(unsigned dac(0);dac<2;dac++) {
	  for(unsigned i(0);i<4;i++) {
	    c.Clear();
	    _graph[fe][chip][dac][i].Draw("AP");
	    c.Update();
	  }
	}
      }
    }

    c.Clear();
    _summary[0][0].Draw("AP");
    c.Update();

    c.Clear();
    _summary[0][1].Draw("AP");
    c.Update();

    c.Clear();
    _summary[0][2].Draw("AP");
    c.Update();


    c.Clear();
    _summary[1][0].Draw("AP");
    c.Update();

    c.Clear();
    _summary[1][1].Draw("AP");
    c.Update();

    c.Clear();
    _summary[1][2].Draw("AP");
    c.Update();

    c.Clear();
    _summary[2][0].Draw("AP");
    c.Update();

    c.Clear();
    _summary[2][1].Draw("AP");
    c.Update();

    c.Clear();
    _summary[2][2].Draw("AP");
    c.Update();


    c.Clear();
    _summary[3][0].Draw("AP");
    c.Update();

    c.Clear();
    _summary[3][1].Draw("AP");
    c.Update();

    c.Clear();
    _summary[3][2].Draw("AP");
    c.Update();

    ps.Close();

    return true;
  }

private:
  //TApplication _application;
  TCanvas *_canvas[4];
  TGraphErrors _graph[8][12][2][4];
  TGraphErrors _summary[4][3];
  std::vector<TGraphErrors*> _vConfig[3];
  UtlAverage _average[8][12];
  unsigned _slot,_fe,_chip;
  unsigned _dac[2];
};

#endif
