#ifndef HstVfeDac_HH
#define HstVfeDac_HH

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

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

#include "UtlAverage.hh"
#include "RcdRecord.hh"
#include "CrcLocationData.hh"
#include "CrcVlinkEventData.hh"
#include "DaqEvent.hh"
#include "SubAccessor.hh"
#include "HstBase.hh"
#include "HstTGraphErrors.hh"

bool enabled(unsigned enable, unsigned chip, unsigned chan) {
  const unsigned channel(18*chip+chan);

  enable%=6;
  if(enable==0) {
    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==1) {
    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==2) {
    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;
  }
  if(enable==3) {
    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==4) {
    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==5) {
    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;
  }
  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);

}

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

  if(x[0]<par[1]) return par[0]+(x[0]-par[1])*par[4];

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

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

}


class HstVfeDac : public HstBase {

public:
  HstVfeDac(CrcLocation d, CrcFeConfigurationData::Connector c, 
	    CrcFeRunData::VfeType v, unsigned h=35, bool s=true,
	    bool i=true) : 
    HstBase(i), _cld(d), _cnn(c), _vfe(v), _value(h), _holdScan(s) {

    gStyle->SetOptFit(1);

    _validCfg=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);

    unsigned nChips(12);
    if(_vfe!=CrcFeRunData::ecalFull) nChips=6;

    for(unsigned chip(0);chip<nChips;chip++) {
      for(unsigned chan(0);chan<18;chan++) {
	for(unsigned enable(0);enable<6;enable++) {
	  for(unsigned i(0);i<3;i++) {
	    std::ostringstream sout;
	    sout << "Chip " << chip << ", Channel " << chan 
		 << ", Enable " << enable;
	    if(enabled(enable,chip,chan)) sout << "!";
	    if(_holdScan) sout << ", DAC=" << _value << ", ADC ";
	    else          sout << ", Hold=" << _value << ", ADC ";
	    if(i==0) sout << "Mean vs ";
	    if(i==1) sout << "Noise vs ";
	    if(i==2) sout << "Residuals vs ";
	    if(_holdScan) sout << "Hold";
	    else          sout << "DAC";	    
	    _scan[chip][chan][enable][i].SetTitle(sout.str().c_str());
	  }
	}
      }
    }

    for(unsigned enable(0);enable<7;enable++) {
      for(unsigned i(0);i<4;i++) {
	std::ostringstream sout;
	if(enable<6) {
	  sout << "Disabled " << enable << ", ";
	} else {
	  sout << "Channel Enabled, ";
	}
	if(_holdScan) {
	  sout << "DAC=" << _value << ", ";
	  if(i==0) sout << "Fit Baseline vs 18*Chip+Chan";
	  if(i==1) sout << "Fit Start Time vs 18*Chip+Chan";
	  if(i==2) sout << "Fit Normalisation vs 18*Chip+Chan";
	  if(i==3) sout << "Fit Shaping Time vs 18*Chip+Chan";
	} else {
	  sout << "Hold=" << _value << ", ";
	  if(i==0) sout << "Fit Intercept vs 18*Chip+Chan";
	  if(i==1) sout << "Fit Slope vs 18*Chip+Chan";
	  if(i>=2) sout << "Unused " << i;
	}
        _summary[enable][i].SetTitle(sout.str().c_str());
      }
    }


    /*
    _summaryHist[0].SetNameTitle("summaryHist[0]","Fit Intercept");
    _summaryHist[0].SetBins(100,0.0,1000.0);

    _summary[0].Set(108);
    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);

    _summaryHist[1].SetNameTitle("summaryHist[1]","Fit Slope");
    _summaryHist[1].SetBins(100,0.4,0.6);

    _summary[1].Set(108);
    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() {
  }

  bool record(const RcdRecord &r) {
    SubAccessor extracter(r);
    
    if(r.recordType()==RcdHeader::runStart) {
      std::vector<const CrcLocationData<CrcFeRunData>*>
	v(extracter.extract< CrcLocationData<CrcFeRunData> >());

      bool okay(false);
      for(unsigned i(0);i<v.size();i++) {
	if(*(v[i])==_cld) {
	  _cld.print(std::cout);
	  v[i]->print(std::cout);
	  if(v[i]->data()->vfeType(_cnn)==_vfe) okay=true;
	}
      }
      assert(okay);
      return true;
    }

    if(r.recordType()==RcdHeader::configurationStart) {

      if(_validCfg) {
	EmcVfeControl evc(_cfg.vfeControl());

	unsigned enable(6);
	for(unsigned e(0);e<6;e++) {
	  if(_cnn==CrcFeConfigurationData::top && evc.vfeEnable(CrcFeConfigurationData::top,e)) enable=e;
	  if(_cnn==CrcFeConfigurationData::bot && evc.vfeEnable(CrcFeConfigurationData::bot,e)) enable=e;
	}

	unsigned variable(0);
	if(_holdScan) {
	  variable=_cfg.holdStart();
	} else {
	  if(_cnn==CrcFeConfigurationData::top) variable=_cfg.dacData(CrcFeConfigurationData::top);
	  if(_cnn==CrcFeConfigurationData::bot) variable=_cfg.dacData(CrcFeConfigurationData::bot);
	}

	for(unsigned i(0);i<3;i++) {
	  _vTge[enable][i].push_back(new HstTGraphErrors());

	  std::ostringstream sout;
	  if(enable<6) {
	    sout << "Enable " << enable << ", ";
	    if(_holdScan) sout << "DAC=" << _value << ", Hold=" << variable << ", ";
	    else          sout << "Hold=" << _value << ", DAC=" << variable << ", ";
	  } else {
	    sout << "Enables off, ";
	  }
	  if(i==0) std::cout << sout.str() << "data found" << std::endl;

	  if(i==0) sout << "ADC Mean vs 18*Chip+Chan";
	  if(i==1) sout << "ADC Noise vs 18*Chip+Chan";
	  if(i==2) sout << "ADC Residuals vs 18*Chip+Chan";
	  _vTge[enable][i][_vTge[enable][i].size()-1]->SetTitle(sout.str().c_str());
	}

	unsigned nChips(12),sChips(0);
	if(_vfe!=CrcFeRunData::ecalFull) nChips=6;
	if(_vfe==CrcFeRunData::ecalRight) sChips=6;

	for(unsigned chip(0);chip<nChips;chip++) {
	  for(unsigned chan(0);chan<18;chan++) {
	    if(_average[chip+sChips][chan].number()>0) {
	      if(enable<6) {
		_scan[chip][chan][enable][0].AddPoint(variable,_average[chip+sChips][chan].average(),0,_average[chip+sChips][chan].errorOnAverage());
		_scan[chip][chan][enable][1].AddPoint(variable,_average[chip+sChips][chan].sigma()  ,0,_average[chip+sChips][chan].errorOnSigma());
	      }
	      _vTge[enable][0][_vTge[enable][0].size()-1]->AddPoint(18*chip+chan+0.5,_average[chip+sChips][chan].average(),0,_average[chip+sChips][chan].errorOnAverage());
	      _vTge[enable][1][_vTge[enable][1].size()-1]->AddPoint(18*chip+chan+0.5,_average[chip+sChips][chan].sigma()  ,0,_average[chip+sChips][chan].errorOnSigma());
	    }
	    _average[chip+sChips][chan].reset();
	  }
	}
      }

      _validCfg=false;

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

      for(unsigned i(0);i<v.size();i++) {
	//cout << "Slot " << (unsigned)v[i]->slotNumber() << " found" << endl;
	//if(*(v[i])==_cld) { // Label!
	if(v[i]->slotNumber()==_cld.slotNumber() && 
	   v[i]->crcComponent()==_cld.crcComponent()) { 

	  _cfg=*(v[i]->data());
	  EmcVfeControl evc(_cfg.vfeControl());
	  //_cfg.print(std::cout);

	  unsigned enable(6),nEnable(0);
	  for(unsigned e(0);e<6;e++) {
	    if(_cnn==CrcFeConfigurationData::top && evc.vfeEnable(CrcFeConfigurationData::top,e)) {
	      enable=e;
	      nEnable++;
	    }
	    if(_cnn==CrcFeConfigurationData::bot && evc.vfeEnable(CrcFeConfigurationData::bot,e)) {
	      enable=e;
	      nEnable++;
	    }
	  }

	  //cout << "nEnable = " << nEnable << ", enable = " << enable << endl;
	  if(nEnable>1) return true;

	  if(enable<6 && _holdScan) {
	    if(_cnn==CrcFeConfigurationData::top && _cfg.dacData(CrcFeConfigurationData::top)!=_value) return true;
	    if(_cnn==CrcFeConfigurationData::bot && _cfg.dacData(CrcFeConfigurationData::bot)!=_value) return true;
	  } else {
	    //cout << "value = " << _value << ", holdStart = " << _cfg.holdStart() << endl;
	    if(_cfg.holdStart()!=_value) return true;
	  }

	  _validCfg=true;
	}
      }
      return true;
    }


    //if(_validCfg) cout << "Valid cfg" << endl;
    //else          cout << "Invalid cfg" << endl;

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

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

    for(unsigned i(0);i<v.size();i++) {
      //cout << "Slot " << (unsigned)v[i]->slotNumber() << " found" << endl;
      //if(*(v[i])==_cld) { // Vlink is BE!
      if(v[i]->slotNumber()==_cld.slotNumber()) {
	unsigned fe(_cld.crcComponent());
	//cout << "FE" << fe << " chosen" << endl;
	const CrcVlinkFeData *fd(v[i]->data()->feData(fe));
	if(fd!=0) {
	  //cout << "FE" << fe << " data found" << endl;
	  for(unsigned chan(0);chan<v[i]->data()->feNumberOfAdcSamples(fe);chan++) {
	    const CrcVlinkAdcSample *as(fd->adcSample(chan));
	    if(as!=0) {
	      //cout << "Chan " << chan << " data found" << endl;
	      for(unsigned chip(0);chip<12;chip++) {
		_average[chip][chan]+=as->adc(chip);
		//cout << "Chan " << chan << ", Chip " << chip << " has entries = " << _average[chip][chan].number() << ", average = " << _average[chip][chan].average() << endl;
		//}
	      }
	    }
	  }
	}
      }
    }
    return true;
  }

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

    TF1 line("line","pol1",0,100000);
    TF1 shap("shap",Shaper,0,100000,4);
    TF1 dsdt("dsdt",ShpInt,0,100000,5);
    TF1 *func(0);
    
    unsigned nChips(12);
    if(_vfe!=CrcFeRunData::ecalFull) nChips=6;

    unsigned nPar(2);
    if(_holdScan) nPar=4;
    
    for(unsigned chip(0);chip<nChips;chip++) {
      for(unsigned chan(0);chan<18;chan++) {
	for(unsigned enable(0);enable<6;enable++) {
	  if(_scan[chip][chan][enable][0].GetN()>0) {

	    double x,y;

	    if(_holdScan) {
	      if(enabled(enable,chip,chan)) {
		double par[4];
		par[0]=-300.0;
		par[1]=4.5;
		par[2]=2.6*_value;
		par[3]=31.0;
		func=&shap;
		func->SetParameters(par);
		_scan[chip][chan][enable][0].Fit("shap","Q",0,15.0,240.0);
	      } else {
		double par[5];
		par[0]=400.0;
		par[1]=0.0;
		par[2]=0.05*_value;
		par[3]=30.0;
		par[4]=-0.25;
		func=&dsdt;
		func->SetParameters(par);
		_scan[chip][chan][enable][0].Fit("dsdt","Q",0,15.0,240.0);
	      }

	    } else {
	      func=&line;
	      _scan[chip][chan][enable][0].Fit("line","Q",0,5000.0,40000.0); // Gain x1
	      //_scan[chip][chan][enable][0].Fit("line","Q",0, 800.0, 1400.0); // Gain x10
	      //_scan[chip][chan][enable][0].Fit("line","Q",0,5000.0,55000.0);
	    }

	    double *p(func->GetParameters());	    
	    double *e(func->GetParErrors());	    

	    if(enabled(enable,chip,chan)) {
	      for(unsigned i(0);i<nPar;i++) {
		_summary[6][i].AddPoint(18*chip+chan+0.5,p[i],0,e[i]);
	      }
	      
	      for(unsigned i(0);i<_vTge[6][0].size();i++) {
		_vTge[6][0][i]->GetPoint(18*chip+chan,x,y);
		double s(_vTge[6][0][i]->GetErrorY(18*chip+chan));
		_vTge[6][2][i]->AddPoint(x,y-p[0],0,sqrt(e[0]*e[0]+s*s));
	      }
	      
	    } else {
	      for(unsigned i(0);i<nPar;i++) {
		_summary[enable][i].AddPoint(18*chip+chan+0.5,p[i],0,e[i]);
	      }
	    }

	    for(int i(0);i<_scan[chip][chan][enable][0].GetN();i++) {
	      _scan[chip][chan][enable][0].GetPoint(i,x,y);
	      _scan[chip][chan][enable][2].AddPoint(x,y-func->Eval(x),0,_scan[chip][chan][enable][0].GetErrorY(i));
	    }
	  }
	}
      }
    }
	  
    TCanvas c("PS Canvas","PS Canvas",10,10,600,800);

    bool drawn(false);
    for(unsigned i(0);i<nPar;i++) {
      if(_summary[6][i].GetN()>0) {
	if(!drawn) {
	  c.Clear();
	  c.Divide(nPar/2,2);
	  drawn=true;
	}
	c.cd(i+1);
	_summary[6][i].Draw("AP");
      }
    }
    if(drawn) c.Update();

    for(unsigned enable(0);enable<6;enable++) {
      bool drawn(false);
      for(unsigned i(0);i<nPar;i++) {
	if(_summary[enable][i].GetN()>0) {
	  if(!drawn) {
	    c.Clear();
	    c.Divide(nPar/2,2);
	    drawn=true;
	  }
	  c.cd(i+1);
	  _summary[enable][i].Draw("AP");
	}
      }
      if(drawn) c.Update();
    }
    
    for(unsigned j(0);j<_vTge[6][0].size();j++) {
      bool drawn(false);
      for(unsigned i(0);i<3;i++) {
	if(_vTge[6][i][j]->GetN()>0) {
	  if(!drawn) {
	    c.Clear();
	    c.Divide(1,3);
	    drawn=true;
	  }
	  c.cd(i+1);
	  _vTge[6][i][j]->Draw("AP");
	}
      }
      if(drawn) c.Update();      
    }
    
    for(unsigned enable(0);enable<6;enable++) {
      for(unsigned j(0);j<_vTge[enable][0].size();j++) {

	bool drawn(false);
	for(unsigned i(0);i<2;i++) {
	  if(_vTge[enable][i][j]->GetN()>0) {
	    if(!drawn) {
	      c.Clear();
	      c.Divide(1,2);
	      drawn=true;
	    }
	    c.cd(i+1);
	    _vTge[enable][i][j]->Draw("AP");
	  }
	}
	if(drawn) c.Update();

      }
    }
    
    for(unsigned chip(0);chip<nChips;chip++) {
      for(unsigned chan(0);chan<18;chan++) {
	for(unsigned enable(0);enable<6;enable++) {
	  bool drawn(false);
	  for(unsigned i(0);i<3;i++) {
	    if(_scan[chip][chan][enable][i].GetN()>0) {
	      if(!drawn) {
		c.Clear();
		c.Divide(1,3);
		drawn=true;
	      }
	      c.cd(i+1);
	      _scan[chip][chan][enable][i].Draw("AP");
	    }
	  }
	  if(drawn) c.Update();
	  
	}
      }
    }

    return true;
  }

  bool postscript(std::string file) {


    std::cout << "HstVfeDac::postscript()  Writing to file "
              << file << std::endl;
    
    TPostScript ps(file.c_str(),111);

    update();

    ps.Close();
    return true;
  }

private:
  const CrcLocation _cld;
  const CrcFeConfigurationData::Connector _cnn;
  const CrcFeRunData::VfeType _vfe;
  const unsigned _value;
  const bool _holdScan;

  bool _validCfg;
  UtlAverage _average[12][18];

  CrcFeConfigurationData _cfg;

  TCanvas *_canvas[2];
  HstTGraphErrors _scan[12][18][6][3];
  std::vector<HstTGraphErrors*> _vTge[7][3];
  HstTGraphErrors _summary[7][4];
};

#endif
