#ifndef EmcCalibration_HH
#define EmcCalibration_HH

#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>

#include "UtlPrintHex.hh"
#include "EmcPhysicalPad.hh"


class EmcCalibration {

public:
  EmcCalibration() {
    for(unsigned x(0);x<18;x++) {
      for(unsigned y(0);y<18;y++) {
	for(unsigned z(0);z<30;z++) {
	  _pedestal[18*(18*z+y)+x]=(x/3)+(y/6)+z-2.5-1.0-14.5;
	  _linear[18*(18*z+y)+x]=0.004;
	  _quadratic[18*(18*z+y)+x]=0.0;
	  _threshold[18*(18*z+y)+x]=(short int)(_pedestal[18*(18*z+y)+x]+15.0);
	}
      }
    }
  }

  double pedestal(EmcPhysicalPad p) const {
    return _pedestal[p.value()];
  }

  void pedestal(EmcPhysicalPad p, double v) {
    _pedestal[p.value()]=v;
  }

  double linear(EmcPhysicalPad p) const {
    return _linear[p.value()];
  }

  void linear(EmcPhysicalPad p, double v) {
    _linear[p.value()]=v;
  }

  double quadratic(EmcPhysicalPad p) const {
    return _quadratic[p.value()];
  }

  void quadratic(EmcPhysicalPad p, double v) {
    _quadratic[p.value()]=v;
  }

  short int threshold(EmcPhysicalPad p) const {
    return _threshold[p.value()];
  }

  void threshold(EmcPhysicalPad p, short int a) {
    _threshold[p.value()]=a;
  }

  void threshold(EmcPhysicalPad p, double a) {
    if(a<0.0) a-=1.0;
    _threshold[p.value()]=(short int)a;
  }

  bool aboveThreshold(EmcPhysicalPad p, short int a) const {
    return a>_threshold[p.value()];
  }

  double energy(EmcPhysicalPad p, short int adc) const {
    double signal(adc-_pedestal[p.value()]);
    return signal*(_linear[p.value()]+signal*_quadratic[p.value()]);
  }

  short int adc(EmcPhysicalPad p, double v) const {
    double x(-v*_quadratic[p.value()]/_linear[p.value()]);
    double a(_pedestal[p.value()]+v*(1.0+x*(1.0+2.0*x))/_linear[p.value()]);

    if(a>32767.0) {
      return 32767;
    }
    if(a<-32768.0) {
      return -32768;
    }
    if(a<0.0) a-=1.0;
    return (short int)a;
  }

  std::ostream& print(std::ostream &o, std::string s="") const {
    o << s << "EmcCalibration::print()" << std::endl;

    for(unsigned z(0);z<30;z++) {
      for(unsigned y(0);y<18;y++) {
	for(unsigned x(0);x<18;x++) {
	  o << s << " X,Y,Z " << std::setw(3) << x << std::setw(3) << y << std::setw(3) << z 
	    << " = P,L,Q "
	    << std::setw(12) << _pedestal[18*(18*z+y)+x]
	    << std::setw(12) << _linear[18*(18*z+y)+x]
	    << std::setw(12) << _quadratic[18*(18*z+y)+x]
	    << std::setw(8) << _threshold[18*(18*z+y)+x] << std::endl;
	}
      }
    }

    return o;
  }

  bool write(std::string fileName) const {
    std::cout << "EmcCalibration::write()  Opening " << fileName << std::endl;
    std::ofstream o(fileName.c_str());
    return write(o);
  }

  bool write(std::ostream &o) const {
    for(unsigned i(0);i<18*18*30;i++) {
      o << std::setw(4) << i << " "
	<< _pedestal[i] << " "
	<< _linear[i] << " "
	<< _quadratic[i] << " "
	<< _threshold[i] << " " << std::endl;
    }
    return true;
  }

  bool read(std::string fileName) {
    std::cout << "EmcCalibration::read()  Opening " << fileName << std::endl;
    std::ifstream i(fileName.c_str(),std::ios::in);
    if(!i) return false;
    return read(i);
  }

  bool read(std::istream &i) {
    unsigned v;
    i >> v;

    while(i) {
      assert(v<18*18*30);
      i >> _pedestal[v] >> _linear[v] >> _quadratic[v] >> _threshold[v];
      if(!i) return false;
      i >> v;
    }

    return true;
  }


private:
  double _pedestal[18*18*30];
  double _linear[18*18*30];
  double _quadratic[18*18*30];
  short int _threshold[18*18*30];
};

#endif
