#ifndef CrcFeConfigurationData_HH
#define CrcFeConfigurationData_HH

// Configuration data for each CRC FE

#include <string>
#include <iostream>

// records/inc/utl
#include "UtlPack.hh"


class CrcFeConfigurationData {

public:
  enum {
    versionNumber=0
  };

  enum Connector {
    bot=0,
    top=1,
    boardA=0,
    boardB=1
  };

  CrcFeConfigurationData();

  bool calibEnable() const;
  void calibEnable(bool e);

  unsigned calibControl() const;
  void     calibControl(unsigned n);
  unsigned calibStart() const;
  unsigned calibWidth() const;
  void     calibWidth(unsigned n);
  unsigned calibEnd() const;
  void     calibEnd(unsigned n);

  unsigned holdStart() const;
  void     holdStart(unsigned n);
  unsigned holdWidth() const;
  void     holdWidth(unsigned n);
  unsigned holdEnd() const;
  void     holdEnd(unsigned n);

  bool holdInvert() const;
  void holdInvert(bool e);
  bool holdControl() const;
  void holdControl(unsigned n);

  unsigned vfeResetStart() const;
  void     vfeResetStart(unsigned n);
  unsigned vfeResetWidth() const;
  void     vfeResetWidth(unsigned n);
  unsigned vfeResetEnd() const;
  void     vfeResetEnd(unsigned n);

  unsigned vfeSrinStart() const;
  void     vfeSrinStart(unsigned n);
  unsigned vfeSrinWidth() const;
  void     vfeSrinWidth(unsigned n);
  unsigned vfeSrinEnd() const;
  void     vfeSrinEnd(unsigned n);

  unsigned vfeMplexClockStart() const;
  void     vfeMplexClockStart(unsigned n);
  unsigned vfeMplexClockMark() const;
  void     vfeMplexClockMark(unsigned n);
  unsigned vfeMplexClockSpace() const;
  void     vfeMplexClockSpace(unsigned n);
  unsigned vfeMplexClockPulses() const;
  void     vfeMplexClockPulses(unsigned n);
  unsigned vfeMplexClockEnd() const;

  unsigned adcStart() const;
  void     adcStart(unsigned n);
  unsigned adcWidth() const;
  void     adcWidth(unsigned n);
  unsigned adcEnd() const;
  void     adcEnd(unsigned n);
  unsigned adcDelay() const;
  void     adcDelay(unsigned n);

  unsigned adcStartBeforeClock() const;
  void     adcStartBeforeClock(unsigned n);
  unsigned adcEndAfterClock() const;
  void     adcEndAfterClock(unsigned n);

  bool internalDacEnable() const;
  void internalDacEnable(bool e);

  unsigned adcControl() const;
  void     adcControl(unsigned char c);

  unsigned dacData(Connector c) const;
  void     dacData(Connector c, unsigned short n);

  unsigned frameSyncDelay() const;
  void     frameSyncDelay(unsigned n);
  unsigned readoutSyncDelay() const;
  void     readoutSyncDelay(unsigned n);
  unsigned qdrDataDelay() const;
  void     qdrDataDelay(unsigned n);

  UtlPack vfeControl() const;
  void    vfeControl(UtlPack v);

  void sequenceDelay(unsigned n);

  bool operator!=(const CrcFeConfigurationData &c);
  bool operator==(const CrcFeConfigurationData &c);

  void setDhe();

  std::ostream& print(std::ostream &o, std::string s="") const;

  /*
  void write(std::string fileName) const;
  void write(std::ostream &o) const;

  void read(std::string fileName);
  void read(std::istream &i);

  const unsigned* data() const;
  unsigned* data();
  */


private:
  unsigned _calibWidth;
  UtlPack  _calibEnable;
  unsigned _holdStart;
  unsigned _holdWidth;
  UtlPack  _holdInvert;
  unsigned _vfeResetStart;
  unsigned _vfeResetEnd;
  unsigned _vfeSrinStart;
  unsigned _vfeSrinEnd;
  unsigned _vfeMplexClockStart;
  unsigned _vfeMplexClockMark;
  unsigned _vfeMplexClockSpace;
  unsigned _vfeMplexClockPulses;
  unsigned _adcStart;
  unsigned _adcEnd;
  UtlPack  _adcControl;
  unsigned _adcDelay;
  UtlPack  _dacData[2];
  unsigned _frameSyncDelay;
  UtlPack  _qdrDataDelay;
  UtlPack  _vfeControl;
};


#ifdef CALICE_DAQ_ICC

#include <cstring>

// records/inc/utl
#include "UtlPrintHex.hh"


CrcFeConfigurationData::CrcFeConfigurationData() {
  memset(this,0,sizeof(CrcFeConfigurationData));
  
  /*
  // These are in 160 MHz ticks = 6.25ns
  _calibPulseWidth=16;
  _holdStart=32;
  _holdDuration=12800;
  
  // These are in 40 MHz ticks = 40ns
  _vfeResetStart=80/4;
  _vfeResetStop=120/4;
  _vfeSrinStart=160/4;
  _vfeSrinStop=320/4;
  _vfeMplexClockStart=240/4;
  _vfeMplexClockMark=480/4;
  _vfeMplexClockSpace=160/4;
  _adcStart=16/4;
  _adcStop=32/4;
  
  CercFeAdcConfigurationData d;
  adcControl(d);
  adcNumberOfConversions(18);
  
  vfeTopAddress(true);
  vfeBotAddress(true);
  */
  
  _calibWidth=0x0080;
  _calibEnable.word(0x0000);
  _vfeMplexClockPulses=18;
  _holdStart=0x0001;
  _holdWidth=0x4000;
  _vfeResetStart=100;
  _vfeResetEnd=200;
  _vfeSrinStart=300;
  _vfeSrinEnd=500;
  _vfeMplexClockStart=400;
  _vfeMplexClockMark=80;
  _vfeMplexClockSpace=80;
  _adcStart=300;
  _adcEnd=13500;
  _adcControl=0x8;
  _adcDelay=30;

  //_frameSyncDelay=16; // 0x0d000093
  _frameSyncDelay=4500;  // 0x0dff6193

  readoutSyncDelay(5000); // 0x0dff6193 only

  //qdrDataDelay(3); // 0x0d000093
  qdrDataDelay(1); // 0x0dff6193

  _vfeControl=0;
}


// TCALIB calibration timing pulse; units of 6.25ns

bool CrcFeConfigurationData::calibEnable() const {
  return _calibEnable.bit(0);
}

void CrcFeConfigurationData::calibEnable(bool e) {
  _calibEnable.word(0);
  _calibEnable.bit(0,e);
}

unsigned CrcFeConfigurationData::calibControl() const {
  return _calibEnable.word();
}

void CrcFeConfigurationData::calibControl(unsigned n) {
  _calibEnable.word(n);
}

unsigned CrcFeConfigurationData::calibStart() const {
  return 0; // Starts immediately after trigger
}

unsigned CrcFeConfigurationData::calibWidth() const {
  return _calibWidth;
}

void CrcFeConfigurationData::calibWidth(unsigned n) {
  _calibWidth=n;
}

unsigned CrcFeConfigurationData::calibEnd() const {
  return _calibWidth;
}

void CrcFeConfigurationData::calibEnd(unsigned n) {
  _calibWidth=n;
}


// HOLD sample-and-hold; units of 6.25ns

unsigned CrcFeConfigurationData::holdStart() const {
  return _holdStart;
}

void CrcFeConfigurationData::holdStart(unsigned n) {
  _holdStart=n;
}

unsigned CrcFeConfigurationData::holdWidth() const {
  return _holdWidth;
}

void CrcFeConfigurationData::holdWidth(unsigned n) {
  _holdWidth=n;
}

unsigned CrcFeConfigurationData::holdEnd() const {
  return _holdStart+_holdWidth;
}

void CrcFeConfigurationData::holdEnd(unsigned n) {
  _holdWidth=n-_holdStart;
}

bool CrcFeConfigurationData::holdInvert() const {
  return _holdInvert.bit(0);
}

void CrcFeConfigurationData::holdInvert(bool e) {
  _holdInvert.word(0);
  _holdInvert.bit(0,e);
}

bool CrcFeConfigurationData::holdControl() const {
  return _holdInvert.word();
}

void CrcFeConfigurationData::holdControl(unsigned n) {
  _holdInvert.word(n);
}


// VFE multiplexer timing; units of 25ns

unsigned CrcFeConfigurationData::vfeResetStart() const {
  return _vfeResetStart;
}

void CrcFeConfigurationData::vfeResetStart(unsigned n) {
  _vfeResetStart=n;
}

unsigned CrcFeConfigurationData::vfeResetWidth() const {
  return _vfeResetEnd-_vfeResetStart;
}

void CrcFeConfigurationData::vfeResetWidth(unsigned n) {
  _vfeResetEnd=_vfeResetStart+n;
}

unsigned CrcFeConfigurationData::vfeResetEnd() const {
  return _vfeResetEnd;
}

void CrcFeConfigurationData::vfeResetEnd(unsigned n) {
  _vfeResetEnd=n;
}


unsigned CrcFeConfigurationData::vfeSrinStart() const {
  return _vfeSrinStart;
}

void CrcFeConfigurationData::vfeSrinStart(unsigned n) {
  _vfeSrinStart=n;
}

unsigned CrcFeConfigurationData::vfeSrinWidth() const {
  return _vfeSrinEnd-_vfeSrinStart;
}

void CrcFeConfigurationData::vfeSrinWidth(unsigned n) {
  _vfeSrinEnd=_vfeSrinStart+n;
}

unsigned CrcFeConfigurationData::vfeSrinEnd() const {
  return _vfeSrinEnd;
}

void CrcFeConfigurationData::vfeSrinEnd(unsigned n) {
  _vfeSrinEnd=n;
}


unsigned CrcFeConfigurationData::vfeMplexClockStart() const {
  return _vfeMplexClockStart;
}

void CrcFeConfigurationData::vfeMplexClockStart(unsigned n) {
  _vfeMplexClockStart=n;
}

unsigned CrcFeConfigurationData::vfeMplexClockMark() const {
  return _vfeMplexClockMark;
}

void CrcFeConfigurationData::vfeMplexClockMark(unsigned n) {
  _vfeMplexClockMark=n;
}

unsigned CrcFeConfigurationData::vfeMplexClockSpace() const {
  return _vfeMplexClockSpace;
}

void CrcFeConfigurationData::vfeMplexClockSpace(unsigned n) {
  _vfeMplexClockSpace=n;
}

unsigned CrcFeConfigurationData::vfeMplexClockPulses() const {
  return _vfeMplexClockPulses;
}

void CrcFeConfigurationData::vfeMplexClockPulses(unsigned n) {
  _vfeMplexClockPulses=n;
}

unsigned CrcFeConfigurationData::vfeMplexClockEnd() const {
  return _vfeMplexClockStart+_vfeMplexClockPulses*(_vfeMplexClockMark+_vfeMplexClockSpace);
}


// ADC chip enable timing; units of 25ns

unsigned CrcFeConfigurationData::adcStart() const {
  return _adcStart;
}

void CrcFeConfigurationData::adcStart(unsigned n) {
  _adcStart=n;
}

unsigned CrcFeConfigurationData::adcWidth() const {
  return _adcEnd-_adcStart;
}

void CrcFeConfigurationData::adcWidth(unsigned n) {
  _adcEnd=_adcStart+n;
}

unsigned CrcFeConfigurationData::adcEnd() const {
  return _adcEnd;
}

void CrcFeConfigurationData::adcEnd(unsigned n) {
  _adcEnd=n;
}

unsigned CrcFeConfigurationData::adcDelay() const {
  return _adcDelay;
}

void CrcFeConfigurationData::adcDelay(unsigned n) {
  _adcDelay=n;
}

unsigned CrcFeConfigurationData::adcStartBeforeClock() const {
  for(unsigned i(0);i<_vfeMplexClockPulses;i++) {
    if(_adcStart<_vfeMplexClockStart+i*(_vfeMplexClockMark+_vfeMplexClockSpace)) return i;
  }
  return _vfeMplexClockPulses;
}

void CrcFeConfigurationData::adcStartBeforeClock(unsigned n) {
  // Must change in middle of clock space, not mark
  _adcStart=_vfeMplexClockStart+n*(_vfeMplexClockMark+_vfeMplexClockSpace)-_vfeMplexClockSpace/2;
}

unsigned CrcFeConfigurationData::adcEndAfterClock() const {
  for(unsigned i(0);i<_vfeMplexClockPulses;i++) {
    if(_adcEnd<_vfeMplexClockStart+(i+1)*(_vfeMplexClockMark+_vfeMplexClockSpace)+_vfeMplexClockMark) return i;
  }
  return _vfeMplexClockPulses;
}

void CrcFeConfigurationData::adcEndAfterClock(unsigned n) {
  // Must change in middle of clock space, not mark
  _adcEnd=_vfeMplexClockStart+(n+1)*(_vfeMplexClockMark+_vfeMplexClockSpace)-_vfeMplexClockSpace/2;
  //_adcDelay=n; ??? Why was this here???
}


// ADC control

bool CrcFeConfigurationData::internalDacEnable() const {
  return _adcControl.bit(0);
}

void CrcFeConfigurationData::internalDacEnable(bool e) {
  return _adcControl.bit(0,e);
}

unsigned CrcFeConfigurationData::adcControl() const {
  return _adcControl.halfByte(0);
}

void CrcFeConfigurationData::adcControl(unsigned char c) {
  _adcControl.halfByte(0,c&0xf);
}


// DAC control

unsigned CrcFeConfigurationData::dacData(Connector c) const {
  return _dacData[c].halfWord(0);
}

void CrcFeConfigurationData::dacData(Connector c, unsigned short n) {
  _dacData[c].word(0);
  _dacData[c].halfWord(0,n);
}


// Vlink timing

unsigned CrcFeConfigurationData::frameSyncDelay() const {
  return _frameSyncDelay;
}

void CrcFeConfigurationData::frameSyncDelay(unsigned n) {
  _frameSyncDelay=n;
}

unsigned CrcFeConfigurationData::readoutSyncDelay() const {
  return _qdrDataDelay.bits(8,31);
}

void CrcFeConfigurationData::readoutSyncDelay(unsigned n) {
  _qdrDataDelay.bits(8,31,n&0xffffff);
}

unsigned CrcFeConfigurationData::qdrDataDelay() const {
  return _qdrDataDelay.byte(0);
}

void CrcFeConfigurationData::qdrDataDelay(unsigned n) {
  _qdrDataDelay.byte(0,n&0x7);
}

UtlPack CrcFeConfigurationData::vfeControl() const {
  return _vfeControl;
}

void CrcFeConfigurationData::vfeControl(UtlPack v) {
  _vfeControl=v;
}


// Overall sequence delay

void CrcFeConfigurationData::sequenceDelay(unsigned n) {

  // 6.25ns signals
  _calibWidth+=n;
  _holdStart+=n;
  _holdWidth+=n;

  // 25ns signals
  unsigned m(n/4);
  _vfeResetStart+=m;
  _vfeResetEnd+=m;
  _vfeSrinStart+=m;
  _vfeSrinEnd+=m;
  _vfeMplexClockStart+=m;
  _adcStart+=m;
  _adcEnd+=m;
}


// Comparators

bool CrcFeConfigurationData::operator!=(const CrcFeConfigurationData &c) {
  unsigned *a((unsigned*)this);
  unsigned *b((unsigned*)&c);
  for(unsigned i(0);i<sizeof(CrcFeConfigurationData)/4;i++) {
    if(i!=15) {
      if(a[i]!=b[i]) return true;
    } else {
      if(_adcControl.halfByte(0)!=c._adcControl.halfByte(0)) return true;
    }
  }
  return false;
}

bool CrcFeConfigurationData::operator==(const CrcFeConfigurationData &c) {
  return !operator!=(c);
}

void CrcFeConfigurationData::setDhe() {
  vfeMplexClockPulses(64);

  // Does not work for mark=space times of 56 or less
  vfeMplexClockMark(58);
  vfeMplexClockSpace(58);
  adcEndAfterClock(64);

  vfeSrinEnd(478);
  holdWidth(8000*4);

  // Transfer times to BE; must match BeTrg where specified as 13 bits
  frameSyncDelay(8100);
  readoutSyncDelay(8150);
}

std::ostream& CrcFeConfigurationData::print(std::ostream &o, std::string s) const {
  o << s << "CrcFeConfigurationData::print()" << std::endl;
  
  o << s << " Calibration trigger pulse start " << calibStart()
    << " x 6.25ns = " << 6.25*calibStart() << "ns";
  // Add one here as calibWidth=0 gives a pulse of 1x6.25ns, etc.
  o << " Calibration trigger pulse width " << calibWidth()+1 << " x 6.25ns";
  o << " Calibration trigger pulse end   " << calibEnd() << " x 6.25ns" << std::endl;

  o << s << " Calibration enable word " << printHex(_calibEnable) << std::endl;
  if(calibEnable()) o << s << "  Calibration enabled" << std::endl;
  else              o << s << "  Calibration disabled" << std::endl; 
  
  o << s << " Hold start " << _holdStart << " x 6.25ns"
    << ", duration " << _holdWidth << " x 6.25ns" << std::endl;

  o << s << " Hold invert word " << printHex(_calibEnable) << std::endl;
  if(holdInvert()) o << s << "  Hold inverted" << std::endl;
  else             o << s << "  Hold not inverted" << std::endl; 
  
  o << s << " VFE reset start " << _vfeResetStart << " x 25ns"
    << ", stop " << _vfeResetEnd << " x 25ns" << std::endl;
  
  o << s << " VFE srin start " << _vfeSrinStart << " x 25ns"
    << ", stop " << _vfeSrinEnd << " x 25ns" << std::endl;
  
  o << s << " VFE mplex clock start " << _vfeMplexClockStart << " x 25ns"
    << ", mark " << _vfeMplexClockMark << " x 25ns"
    << ", space " << _vfeMplexClockSpace << " x 25ns"
    << ", clocks " << _vfeMplexClockPulses << std::endl;
  
  o << s << " ADC start " << _adcStart << " x 25ns"
    << ", end " << _adcEnd << " x 25ns" << std::endl;
  o << s << "  ADC start before clock " << std::setw(2) << adcStartBeforeClock()
    << ", end after clock " << std::setw(2) << adcEndAfterClock() <<std::endl;
  
  o << s << " ADC control word " << printHex(_adcControl) << std::endl;
  if(internalDacEnable()) o << s << "  DAC internal loopback enabled" << std::endl;
  else                    o << s << "  DAC internal loopback disabled" << std::endl; 
  
  o << s << " ADC delay " << _adcDelay << " x 25ns" << std::endl;
  
  o << s << " Bot DAC data " << std::setw(5) << dacData(bot) << std::endl;
  o << s << " Top DAC data " << std::setw(5) << dacData(top) << std::endl;
  
  o << s << " FrameSync delay " << _frameSyncDelay << " x 25ns" << std::endl;
  o << s << " ReadoutSync delay " << readoutSyncDelay() << " x 25ns" << std::endl;
  o << s << " QDR data delay " << qdrDataDelay() << " x 12.5ns" << std::endl;
  o << s << " VFE control bits " << printHex(vfeControl()) << std::endl;

  /*  
  unsigned *d((unsigned*)this);
  for(unsigned i(0);i<sizeof(CrcFeConfigurationData)/4;i++) {
    o << s << std::setw(3) << i << " " << printHex(d[i]) << std::endl;
  }
  */

  return o;
}

/*
void CrcFeConfigurationData::write(std::string fileName) const {
  std::ofstream o(fileName.c_str());
  write(o);
}

void CrcFeConfigurationData::write(std::ostream &o) const {
  unsigned *d((unsigned*)this);
  for(unsigned i(0);i<sizeof(CrcFeConfigurationData)/4;i++) {
    o << i << " " << d[i] << std::endl;
  }
}

void CrcFeConfigurationData::read(std::string fileName) {
  std::ifstream i(fileName.c_str());
  read(i);
}

void CrcFeConfigurationData::read(std::istream &i) {
  unsigned *d((unsigned*)this);
  unsigned n;
  for(unsigned j(0);j<sizeof(CrcFeConfigurationData)/4;j++) {
    i >> n >> d[j];
    if(n!=j) std::cout << "CrcFeConfigurationData::read()  error "
		       << n << " != " << j << std::endl;
  }
}

  const unsigned* data() const {
  return &_calibWidth;
  }
  
  unsigned* data() {
  return &_calibWidth;
  }
*/

#endif
#endif
