#ifndef HstVfeDac_HH
#define HstVfeDac_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"

#include "UtlAverage.hh"
#include "RcdRecord.hh"
#include "EmcCercFeEventData.hh"
#include "HodData.hh"
#include "HodTrack.hh"
#include "DaqEvent.hh"
#include "SubExtracter.hh"

bool enabled(unsigned enable, unsigned channel) {
  enable%=6;
  if(enable==0) {
    if((channel%36)== 1) return true;
    if((channel%36)==10) return true;
    if((channel%36)==18) return true;
    if((channel%36)==20) return true;
    if((channel%36)==27) return true;
    if((channel%36)==29) return true;
  }
  if(enable==1) {
    if((channel%36)== 4) return true;
    if((channel%36)==13) return true;
    if((channel%36)==21) return true;
    if((channel%36)==23) return true;
    if((channel%36)==30) return true;
    if((channel%36)==32) return true;
  }
  if(enable==2) {
    if((channel%36)== 7) return true;
    if((channel%36)==16) return true;
    if((channel%36)==24) return true;
    if((channel%36)==26) return true;
    if((channel%36)==33) return true;
    if((channel%36)==35) return true;
  }
  if(enable==3) {
    if((channel%36)== 0) return true;
    if((channel%36)== 2) return true;
    if((channel%36)== 9) return true;
    if((channel%36)==11) return true;
    if((channel%36)==19) return true;
    if((channel%36)==28) return true;
  }
  if(enable==4) {
    if((channel%36)== 3) return true;
    if((channel%36)== 5) return true;
    if((channel%36)==12) return true;
    if((channel%36)==14) return true;
    if((channel%36)==22) return true;
    if((channel%36)==31) return true;
  }
  if(enable==5) {
    if((channel%36)== 6) return true;
    if((channel%36)== 8) return true;
    if((channel%36)==15) return true;
    if((channel%36)==17) return true;
    if((channel%36)==25) return true;
    if((channel%36)==34) return true;
  }
  return false;
}

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

  if(x[0]<par[1]) return par[0];

  Double_t tShape(par[3]);
  if(tShape<=0.0) tShape=1.0e-6;

  Double_t t=(x[0]-par[1])/tShape;
  return par[0]+par[2]*t*exp(1.0-t);

}


class HstVfeDac {

public:
  HstVfeDac(unsigned f=0, unsigned e=6, unsigned c=0) : 
    _application("Noise Application",0,0),
    _fe(f), _enable(e), _chan(c) {

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

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

    _holdScan=false;

    //_canvas[0].SetCanvasSize(600,800);
    //_canvas[0].SetWindowSize(600,800);
    _canvas[0]=new TCanvas("ExtDac Canvas[0]","ExtDac Canvas[0]",10,10,600,800),
    _canvas[1]=new TCanvas("ExtDac Canvas[1]","ExtDac Canvas[1]",20,20,600,800),

    _canvas[0]->Divide(1,3);
    if(_holdScan) _canvas[1]->Divide(1,4);
    else          _canvas[1]->Divide(1,2);

    for(unsigned chan(0);chan<216;chan++) {
      std::ostringstream sout[3];
      sout[0] << "FE0, Channel " << chan << ", ADC mean vs ";
      sout[1] << "FE0, Channel " << chan << ", ADC noise vs ";
      sout[2] << "FE0, Channel " << chan << ", ADC residuals vs ";
      
      if(_holdScan) {
	sout[0] << "hold";
	sout[1] << "hold";
	sout[2] << "hold";
      } else {
	sout[0] << "DAC0";
	sout[1] << "DAC0";
	sout[2] << "DAC0";
      }
      
      for(unsigned i(0);i<3;i++) {
	_graph[chan][i].SetTitle(sout[i].str().c_str());
	_graph[chan][i].SetMarkerColor(1);
	_graph[chan][i].SetMarkerStyle(20);
      }
    }

    _summary[0].Set(216);
    if(_holdScan) {
      _summary[0].SetTitle("Fit Baseline vs FE0/Channel");
    } else {
      _summary[0].SetTitle("Fit Intercept vs FE0/Channel");
    }
    _summary[0].SetMarkerColor(1);
    _summary[0].SetMarkerStyle(20);

    _summary[1].Set(216);
    if(_holdScan) {
      _summary[1].SetTitle("Fit Start Time vs FE0/Channel");
    } else {
      _summary[1].SetTitle("Fit Slope vs FE0/Channel");
    }
    _summary[1].SetMarkerColor(1);
    _summary[1].SetMarkerStyle(20);

    if(_holdScan) {
      _summary[2].Set(216);
      _summary[2].SetTitle("Fit Normalisation vs FE0/Channel");
      _summary[2].SetMarkerColor(1);
      _summary[2].SetMarkerStyle(20);

      _summary[3].Set(216);
      _summary[3].SetTitle("Fit Peaking Time vs FE0/Channel");
      _summary[3].SetMarkerColor(1);
      _summary[3].SetMarkerStyle(20);
    }
  }

  virtual ~HstVfeDac() {
  }

  void update() {
    cout << "Updating..." << endl;


    TF1 *func = new TF1("Shaper",Shaper,6,241,4);

    for(unsigned chan(0);chan<216;chan++) {
      unsigned bin(_graph[chan][0].GetN());
      _graph[chan][0].Set(bin);
      _graph[chan][0].SetPoint(bin,_variable,_average[chan].average());
      _graph[chan][0].SetPointError(bin,0.0,_average[chan].errorOnAverage());
      
      _graph[chan][1].Set(bin);
      _graph[chan][1].SetPoint(bin,_variable,_average[chan].sigma());
      _graph[chan][1].SetPointError(bin,0.0,_average[chan].errorOnSigma());
      
      double *pars=0;
      double *errs=0;
      if(_holdScan) {
	Double_t par[4]={0.0,2.0,10000.0,32.0};
	if(!enabled(_enable,chan)) {
	  par[2]=100.0;
	  par[3]=90.0;
	}
	func->SetParameters(par);
	//    func->SetParLi

	_graph[chan][0].Fit("Shaper","Q",0,6.0,241.0);
	pars=_graph[chan][0].GetFunction("Shaper")->GetParameters();
	errs=_graph[chan][0].GetFunction("Shaper")->GetParErrors();
      } else {
	_graph[chan][0].Fit("pol1","Q",0,500.0,5500.0);
	pars=_graph[chan][0].GetFunction("pol1")->GetParameters();
	errs=_graph[chan][0].GetFunction("pol1")->GetParErrors();
      }
      
      _graph[chan][2].Set(bin);
      for(unsigned i(0);i<_graph[chan][0].GetN();i++) {
	double x,y;
	_graph[chan][0].GetPoint(i,x,y);

	if(_holdScan) {
	  if(x>=6.0 && x<241.0) {
	    _graph[chan][2].SetPoint(i,x,y-func->Eval(x));
	    _graph[chan][2].SetPointError(i,0.0,_graph[chan][0].GetErrorY(i));
	  }
	} else {
	  if(x>=500.0 && x<5500.0) {
	    _graph[chan][2].SetPoint(i,x,y-pars[0]-x*pars[1]);
	    _graph[chan][2].SetPointError(i,0.0,_graph[chan][0].GetErrorY(i));
	  }
	}
      }
      
      if(chan>107) {
	_summary[0].SetPoint(chan,chan,pars[0]);
	_summary[0].SetPointError(chan,0.0,errs[0]);
	_summary[1].SetPoint(chan,chan,pars[1]);
	_summary[1].SetPointError(chan,0.0,errs[1]);
	if(_holdScan) {
	  _summary[2].SetPoint(chan,chan,pars[2]);
	  _summary[2].SetPointError(chan,0.0,errs[2]);
	  _summary[3].SetPoint(chan,chan,pars[2]);
	  _summary[3].SetPointError(chan,0.0,errs[2]);
	}
      }
      _average[chan].reset();
    }

    _canvas[0]->Clear("D");
    _canvas[0]->cd(1);
    _graph[_chan][0].Draw("AP");
    _canvas[0]->cd(2);
    _graph[_chan][1].Draw("AP");
    _canvas[0]->cd(3);
    _graph[_chan][2].Draw("AP");
    _canvas[0]->Update();
    
    _canvas[1]->Clear("D");
    _canvas[1]->cd(1);
    _summary[0].Draw("AP");
    _canvas[1]->cd(2);
    _summary[1].Draw("AP");
    _canvas[1]->Update();
    if(_holdScan) {
      _canvas[1]->cd(3);
      _summary[2].Draw("AP");
      _canvas[1]->Update();
      _canvas[1]->cd(4);
      _summary[3].Draw("AP");
      _canvas[1]->Update();
    }
  }

  void event(RcdRecord &r) {
    if(r.recordType()==SubHeader::configurationEnd ||
       r.recordType()==SubHeader::configurationStop) {
      //if(_validEnable) update();
      return;
    }

    SubExtracter extracter(r);
    
    if(r.recordType()==SubHeader::configurationStart) {
      //_variable=0;
      //_validEnable=false;

      std::vector<const EmcCercFeConfigurationData*> v(extracter.extract<EmcCercFeConfigurationData>());

      for(unsigned i(0);i<v.size();i++) {
	for(unsigned j(0);j<12;j++) {
	  if(v[i]->data()->vfeEnable(j)) {
	    _lastCfg[v[i]->feNumber()]=new HstVfeDacCfg;
	    _vCfg[v[i]->feNumber()][j].push_back(_lastCfg[v[i]->feNumber()]);
	  }
	}

	_lastCfg[v[i]->feNumber()]->dac(v[i]->data()->dacData(0));
	_lastCfg[v[i]->feNumber()]->hold(v[i]->data()->holdStart());

	//_validEnable=v[i]->data()->vfeEnable(_enable);
	//if(_holdScan) _variable=v[i]->data()->holdStart();
	//else          _variable=v[i]->data()->dacData(0);
      }

      for(unsigned k(0);k<8;k++) {
	for(unsigned l(0);l<12;l++) {
	  if(_vCfg[k][l].size()>0) cout << "FE" << k << ", enable " << l << ", Cfg vector size = " << _vCfg[k][l].size() << endl;
	}
      }

      return;
    }


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

    //if(_validEnable) {
      std::vector<const EmcCercFeEventData*> v(extracter.extract<EmcCercFeEventData>());

      for(unsigned i(0);i<v.size();i++) {
	//if(v[i]->feNumber()==0) {
	  for(unsigned chip(0);chip<12;chip++) {
	    for(unsigned chan(0);chan<v[i]->numberOfSamples();chan++) {
	      //_average[18*chip+chan].event(v[i]->adc(chip,chan));
	      _lastCfg[v[i]->feNumber()]->average(chip,chan).event(v[i]->adc(chip,chan));
	    }
	  }
	  //}
	  //      }
      }
  }

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

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

    for(unsigned chan(0);chan<216;chan++) {
      for(unsigned i(0);i<3;i++) {
	c.Clear();
	_graph[chan][i].Draw("AP");
	c.Update();
      }
    }

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

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

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

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

    ps.Close();
  }

private:

  class HstVfeDacCfg {
  public:
    HstVfeDacCfg() {
    }

    unsigned dac() const {
      return _dac;
    }

    void dac(unsigned d) {
      _dac=d;
    }

    unsigned hold() const {
      return _hold;
    }

    void hold(unsigned h) {
      _hold=h;
    }

    UtlAverage& average(unsigned chip, unsigned chan) {
      return _average[chip][chan];
    }

  private:
    unsigned _dac,_hold;
    UtlAverage _average[12][18];
  };


  TApplication _application;
  TCanvas *_canvas[2];
  TGraphErrors _graph[216][3];
  TGraphErrors _summary[4];
  UtlAverage _average[216];
  unsigned _fe,_enable,_chan;
  unsigned _variable;
  bool _holdScan,_validEnable;

  HstVfeDacCfg *_lastCfg[8];
  std::vector<HstVfeDacCfg*> _vCfg[8][12];



};

#endif
