#ifndef CrcBeTrgConfigurationDataV1_HH
#define CrcBeTrgConfigurationDataV1_HH

#include <string>
#include <iostream>


class CrcBeTrgConfigurationDataV1 {

public:
  enum {
    versionNumber=1
  };

  CrcBeTrgConfigurationDataV1();

  unsigned inputEnable() const;
  void     inputEnable(unsigned n);

  bool oscillatorEnable() const;
  void oscillatorEnable(bool b);

  unsigned generalEnable() const;
  void     generalEnable(unsigned n);

  unsigned oscillationPeriod() const;
  void     oscillationPeriod(unsigned n);

  unsigned burstCounter() const;
  void     burstCounter(unsigned n);

  unsigned configuration() const;
  void     configuration(unsigned n);

  unsigned short fifoIdleDepth() const;
  void           fifoIdleDepth(unsigned short n);

  unsigned busyTimeout() const;
  void     busyTimeout(unsigned n);

  unsigned burstTimeout() const;
  void     burstTimeout(unsigned n);

  unsigned andEnable(unsigned a) const;
  void     andEnable(unsigned a, unsigned n);

  unsigned extBeamMode() const;
  void     extBeamMode(unsigned n);

  unsigned inputInvert() const;
  void     inputInvert(unsigned n);

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

  unsigned sequencerControl() const;
  void     sequencerControl(unsigned n);

  bool operator!=(const CrcBeTrgConfigurationDataV1 &c);
  bool operator==(const CrcBeTrgConfigurationDataV1 &c);
  
  std::ostream& print(std::ostream &o=std::cout, std::string s="") const;


private:
  UtlPack  _inputEnable;       // Reg  1
  UtlPack  _generalEnable;     // Reg  4
  unsigned _oscillationPeriod; // Reg  6
  unsigned _burstCounter;      // Reg  7
  UtlPack  _configuration;     // Reg 15
  unsigned _busyTimeout;       // Reg 16
  unsigned _burstTimeout;      // Reg 17
  unsigned _andEnable[4];      // Reg 19, 20, 21, 22
  unsigned _extBeamMode;       // Reg 23
  unsigned _inputInvert;       // Reg 24
  UtlPack  _qdrConfiguration;  // Reg 25
  unsigned _sequencerControl;  // Reg 28
};


#ifdef CALICE_DAQ_ICC

#include <cstring>

CrcBeTrgConfigurationDataV1::CrcBeTrgConfigurationDataV1() {
  memset(this,0,sizeof(CrcBeTrgConfigurationDataV1));
  _inputEnable.bit(24,true);
  _generalEnable.bit(0,true);
  _oscillationPeriod=40000;
  _burstCounter=500;
  _configuration=(200<<16)+0;
  _busyTimeout=20000;
  _burstTimeout=1000;
  _qdrConfiguration=(5001<<19)+(3<<16)+(4501);
}

unsigned CrcBeTrgConfigurationDataV1::inputEnable() const {
  return _inputEnable.word();
}

void CrcBeTrgConfigurationDataV1::inputEnable(unsigned n) {
  _inputEnable.word(n);
}

bool CrcBeTrgConfigurationDataV1::oscillatorEnable() const {
  return _inputEnable.bit(24);
}

void CrcBeTrgConfigurationDataV1::oscillatorEnable(bool b) {
  _inputEnable.bit(24,b);
}

unsigned CrcBeTrgConfigurationDataV1::generalEnable() const {
  return _generalEnable.word();
}

void CrcBeTrgConfigurationDataV1::generalEnable(unsigned n) {
  _generalEnable.word(n);
}

unsigned CrcBeTrgConfigurationDataV1::oscillationPeriod() const {
  return _oscillationPeriod;
}

void CrcBeTrgConfigurationDataV1::oscillationPeriod(unsigned n) {
  _oscillationPeriod=n;
}

unsigned CrcBeTrgConfigurationDataV1::burstCounter() const {
  return _burstCounter;
}

void CrcBeTrgConfigurationDataV1::burstCounter(unsigned n) {
  _burstCounter=n;
}

unsigned CrcBeTrgConfigurationDataV1::configuration() const {
  return _configuration.word();
}

void CrcBeTrgConfigurationDataV1::configuration(unsigned n) {
  _configuration=n;
}

unsigned short CrcBeTrgConfigurationDataV1::fifoIdleDepth() const {
  return _configuration.halfWord(1);
}

void CrcBeTrgConfigurationDataV1::fifoIdleDepth(unsigned short n) {
  _configuration.halfWord(1,n);
}

unsigned CrcBeTrgConfigurationDataV1::busyTimeout() const {
  return _busyTimeout;
}

void CrcBeTrgConfigurationDataV1::busyTimeout(unsigned n) {
  _busyTimeout=n;
}

unsigned CrcBeTrgConfigurationDataV1::burstTimeout() const {
  return _burstTimeout;
}

void CrcBeTrgConfigurationDataV1::burstTimeout(unsigned n) {
  _burstTimeout=n;
}

unsigned CrcBeTrgConfigurationDataV1::andEnable(unsigned a) const {
  assert(a<4);
  return _andEnable[a];
}

void CrcBeTrgConfigurationDataV1::andEnable(unsigned a, unsigned n) {
  assert(a<4);
  _andEnable[a]=n;
}

unsigned CrcBeTrgConfigurationDataV1::extBeamMode() const {
  return _extBeamMode;
}

void CrcBeTrgConfigurationDataV1::extBeamMode(unsigned n) {
  _extBeamMode=n;
}

unsigned CrcBeTrgConfigurationDataV1::inputInvert() const {
  return _inputInvert;
}

void CrcBeTrgConfigurationDataV1::inputInvert(unsigned n) {
  _inputInvert=n;
}

unsigned CrcBeTrgConfigurationDataV1::qdrConfiguration() const {
  return _qdrConfiguration.word();
}

unsigned CrcBeTrgConfigurationDataV1::readoutSyncDelay() const {
  return _qdrConfiguration.bits(19,31);
}

unsigned CrcBeTrgConfigurationDataV1::qdrDataDelay() const {
  return _qdrConfiguration.bits(16,18);
}

unsigned CrcBeTrgConfigurationDataV1::frameSyncDelay() const {
  return _qdrConfiguration.halfWord(0);
}

void CrcBeTrgConfigurationDataV1::qdrConfiguration(unsigned n) {
  _qdrConfiguration=n;
}

unsigned CrcBeTrgConfigurationDataV1::sequencerControl() const {
  return _sequencerControl;
}

void CrcBeTrgConfigurationDataV1::sequencerControl(unsigned n) {
  _sequencerControl=n;
}

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

std::ostream& CrcBeTrgConfigurationDataV1::print(std::ostream &o, std::string s) const {
  o << s << "CrcBeTrgConfigurationDataV1::print()" << std::endl;
  o << s << " Input enable       = " << printHex(_inputEnable) << std::endl;
  o << s << "  External enables = " << printHex(_inputEnable.halfWord(0)) << std::endl;
  if(oscillatorEnable())     o << s << "  Oscillator enabled" << std::endl;
  if(_inputEnable.bit(25)) o << s << "  Cand0 enabled" << std::endl;
  if(_inputEnable.bit(26)) o << s << "  Cand1 enabled" << std::endl;
  if(_inputEnable.bit(27)) o << s << "  Cand2 enabled" << std::endl;
  if(_inputEnable.bit(28)) o << s << "  Cand3 enabled" << std::endl;

  o << s << " General enable     = " << printHex(_generalEnable) << std::endl;
  if(_generalEnable.bit( 0)) o << s << "  J0 fanout enabled" << std::endl;
  if(_generalEnable.bit( 1)) o << s << "  Busy timeout counter enabled" << std::endl;
  if(_generalEnable.bit( 4)) o << s << "  Burst mode enabled" << std::endl;
  if(_generalEnable.bit( 5)) o << s << "  Burst timeout enabled" << std::endl;
  if(_generalEnable.bit( 6)) o << s << "  Inter-burst timeout enabled" << std::endl;
  if(_generalEnable.bit( 8)) o << s << "  Coincidence logic enabled" << std::endl;
  if(_generalEnable.bit(16)) o << s << "  Sequencer mode enabled" << std::endl;

  o << s << " Oscillation period = " << std::setw(11) << _oscillationPeriod << " x 25ns = "
    << std::setw(10) << 0.000000025*_oscillationPeriod << " s" << std::endl;
  o << s << " Burst counter      = " << printHex(_burstCounter) << std::endl;

  o << s << " Configuration      = " << printHex(_configuration) << std::endl;
  o << s << "  Fifo idle depth   = " << fifoIdleDepth() << std::endl;
  o << s << "  Fifo test pattern = " << printHex(_configuration.byte(0)) << std::endl;

  o << s << " Busy timeout       = " << std::setw(11) << _busyTimeout << " x 25ns = "
    << std::setw(10) << 0.000000025*_busyTimeout << " s" << std::endl;
  o << s << " Burst timeout      = " << std::setw(11) << _burstTimeout << " x 25ns = "
    << std::setw(10) << 0.000000025*_burstTimeout << " s" << std::endl;

  for(unsigned i(0);i<4;i++) {
    o << s << " AND enable " << i << "       = " << printHex(_andEnable[i]) << std::endl;
    if(_andEnable[i]!=0) {
      o << s << "  AND " << i << " inputs      = " << printHex(UtlPack(_andEnable[i]).halfWord(0)) << std::endl;
      o << s << "  AND " << i << " inverts     = " << printHex(UtlPack(_andEnable[i]).halfWord(1)) << std::endl;
    }
  }

  o << s << " Ext beam mode      = " << printHex(_extBeamMode) << std::endl;
  o << s << " Input invert       = " << printHex(_inputInvert) << std::endl;

  o << s << " QDR configuration  = " << printHex(_qdrConfiguration) << std::endl;
  o << s << "  ReadoutSync delay = " << std::setw(5) << readoutSyncDelay() << " x 25ns = "
    << std::setw(10) << 0.000000025*readoutSyncDelay() << " s" << std::endl;
  o << s << "  QDR data delay    = " << std::setw(3) << qdrDataDelay() << " x 12.5ns = "
    << std::setw(10) << 0.0000000125*qdrDataDelay() << " s" << std::endl;
  o << s << "  FrameSync delay   = " << std::setw(5) << frameSyncDelay() << " x 25ns = "
    << std::setw(10) << 0.000000025*frameSyncDelay() << " s" << std::endl;

  o << s << " Sequencer control  = " << printHex(_sequencerControl) << std::endl;

  return o;
}

#endif
#endif
