#ifndef CrcReadoutConfigurationDataV1_HH
#define CrcReadoutConfigurationDataV1_HH

#include <string>
#include <iostream>

#include "UtlPack.hh"


class CrcReadoutConfigurationDataV1 {

public:
  enum {
    versionNumber=1
  };

  CrcReadoutConfigurationDataV1();
  CrcReadoutConfigurationDataV1(const CrcReadoutConfigurationDataV0 &c);

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

  bool slotFeEnable(unsigned s, unsigned f) const;
  void slotFeEnable(unsigned s, unsigned f, bool b);

  unsigned char slotFeEnables(unsigned s) const;
  void          slotFeEnables(unsigned s, unsigned char b);

  bool slotEnable(unsigned s) const;
  void slotEnable(unsigned s, bool b);

  unsigned vmePeriod() const;
  void     vmePeriod(unsigned n);

  unsigned bePeriod() const;
  void     bePeriod(unsigned n);

  unsigned becPeriod() const;
  void     becPeriod(unsigned n);

  bool beSoftTrigger() const;
  void beSoftTrigger(bool e);

  unsigned fePeriod() const;
  void     fePeriod(unsigned n);

  bool feBroadcastSoftTrigger() const;
  void feBroadcastSoftTrigger(bool e);

  bool feSoftTrigger(unsigned f) const;
  void feSoftTrigger(unsigned f, bool e);

  unsigned char vlinkMode() const;
  void          vlinkMode(unsigned char n);

  bool vlinkBlt() const;
  void vlinkBlt(bool b);

  bool vlinkFlag() const;
  void vlinkFlag(bool b);

  UtlPack mode() const;

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


private:
  UtlPack _numbers;
  UtlPack _slotEnable;
  UtlPack _slotFeEnable[5];
  unsigned _vmePeriod;
  unsigned _bePeriod;
  unsigned _becPeriod;
  unsigned _fePeriod;
  UtlPack _softTrigger;
  UtlPack _mode;
};


#ifdef CALICE_DAQ_ICC

#include <cstring>

CrcReadoutConfigurationDataV1::CrcReadoutConfigurationDataV1() {
  memset(this,0,sizeof(CrcReadoutConfigurationDataV1));

  for(unsigned i(2);i<=21;i++) {
    slotEnable(i,true);
    for(unsigned f(0);f<8;f++) slotFeEnable(i,f,true);
  }

  vlinkBlt(true);
}

CrcReadoutConfigurationDataV1::CrcReadoutConfigurationDataV1(const CrcReadoutConfigurationDataV0 &c) {
  _vmePeriod=c.vmePeriod();
  _bePeriod=c.bePeriod();
  _becPeriod=0;
  _fePeriod=c.fePeriod();

  _mode=0;
  vlinkBlt(c.vlinkBlt());

  // Undefined
  _softTrigger=0;
}

unsigned char CrcReadoutConfigurationDataV1::crateNumber() const {
  return _numbers.byte(0);
}

void CrcReadoutConfigurationDataV1::crateNumber(unsigned char c) {
  _numbers.byte(0,c);
}

// Only handles slots 2-21

bool CrcReadoutConfigurationDataV1::slotFeEnable(unsigned s, unsigned f) const {
  assert(s>=2 && s<=21 && f<8);
  return _slotFeEnable[(s-2)/4].bit(8*((s-2)%4)+f);
}

void CrcReadoutConfigurationDataV1::slotFeEnable(unsigned s, unsigned f, bool b) {
  assert(s>=2 && s<=21 && f<8);
  _slotFeEnable[(s-2)/4].bit(8*((s-2)%4)+f,b);
}

unsigned char CrcReadoutConfigurationDataV1::slotFeEnables(unsigned s) const {
  assert(s>=2 && s<=21);
  return _slotFeEnable[(s-2)/4].byte((s-2)%4);
}

void CrcReadoutConfigurationDataV1::slotFeEnables(unsigned s, unsigned char b) {
  assert(s>=2 && s<=21);
  _slotFeEnable[(s-2)/4].byte((s-2)%4,b);
}

bool CrcReadoutConfigurationDataV1::slotEnable(unsigned s) const { 
  assert(s>=2 && s<=21);
  return _slotEnable.bit(s);
}

void CrcReadoutConfigurationDataV1::slotEnable(unsigned s, bool b) { 
  assert(s>=2 && s<=21);
  if(!b) for(unsigned f(0);f<8;f++) slotFeEnable(s,f,false);
  _slotEnable.bit(s,b);
}

unsigned CrcReadoutConfigurationDataV1::vmePeriod() const {
  return _vmePeriod;
}

void CrcReadoutConfigurationDataV1::vmePeriod(unsigned n) {
  _vmePeriod=n;
}

unsigned CrcReadoutConfigurationDataV1::bePeriod() const {
  return _bePeriod;
}

void CrcReadoutConfigurationDataV1::bePeriod(unsigned n) {
  _bePeriod=n;
}

unsigned CrcReadoutConfigurationDataV1::becPeriod() const {
  return _becPeriod;
}

void CrcReadoutConfigurationDataV1::becPeriod(unsigned n) {
  _becPeriod=n;
}

bool CrcReadoutConfigurationDataV1::beSoftTrigger() const {
  return _softTrigger.bit(9);
}

void CrcReadoutConfigurationDataV1::beSoftTrigger(bool e) {
  _softTrigger.bit(9,e);
}

unsigned CrcReadoutConfigurationDataV1::fePeriod() const {
  return _fePeriod;
}

void CrcReadoutConfigurationDataV1::fePeriod(unsigned n) {
  _fePeriod=n;
}

bool CrcReadoutConfigurationDataV1::feBroadcastSoftTrigger() const {
  return _softTrigger.bit(8);
}

void CrcReadoutConfigurationDataV1::feBroadcastSoftTrigger(bool e) {
  _softTrigger.bit(8,e);
}

bool CrcReadoutConfigurationDataV1::feSoftTrigger(unsigned f) const {
  assert(f<8);
  return _softTrigger.bit(f);
}

void CrcReadoutConfigurationDataV1::feSoftTrigger(unsigned f, bool e) {
  assert(f<8);
  _softTrigger.bit(f,e);
}

UtlPack CrcReadoutConfigurationDataV1::mode() const {
  return _mode;
}

unsigned char CrcReadoutConfigurationDataV1::vlinkMode() const {
  return _mode.byte(2);
}

void CrcReadoutConfigurationDataV1::vlinkMode(unsigned char n) {
  _mode.byte(2,n);
}

bool CrcReadoutConfigurationDataV1::vlinkBlt() const {
  return _mode.bit(16);
}

void CrcReadoutConfigurationDataV1::vlinkBlt(bool b) {
  return _mode.bit(16,b);
}

bool CrcReadoutConfigurationDataV1::vlinkFlag() const {
  return _mode.bit(23);
}

void CrcReadoutConfigurationDataV1::vlinkFlag(bool b) {
  return _mode.bit(23,b);
}

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

  o << s << " Crate number  = " << printHex(_numbers.byte(0)) << std::endl;
  for(unsigned i(2);i<=21;i++) {
    if(slotEnable(i)) {
      o << s << "  Slot " << std::setw(2) << i << ", FE enables = "
	<< printHex(_slotFeEnable[(i-2)/4].byte((i-2)%4)) << std::endl;
    }
  }

  o << s << " VME period        = " << _vmePeriod << std::endl;
  o << s << " BE period         = " << _bePeriod << std::endl;
  o << s << " BE counter period = " << _becPeriod << std::endl;
  o << s << " FE period         = " << _fePeriod << std::endl;
  
  o << s << " BE mode     = " << printHex(_mode.byte(0)) << std::endl;
  o << s << " FE mode     = " << printHex(_mode.byte(1)) << std::endl;  
  o << s << " Vlink mode  = " << printHex(_mode.byte(2)) << std::endl;
  if(vlinkBlt()) o << s << "  Vlink readout using block transfer" << std::endl;
  else           o << s << "  Vlink readout using standard transfer" << std::endl;
  if(vlinkFlag()) o << s << "  Vlink readout flag set" << std::endl;
  else            o << s << "  Vlink readout flag not set" << std::endl;
  
  o << s << " Soft trigger = " << printHex(_softTrigger.word()) << std::endl;
  if(beSoftTrigger()) o << s << "  BE soft trigger" << std::endl;
  if(feBroadcastSoftTrigger()) o << s << "  FE broadcast soft trigger" << std::endl;
  for(unsigned f(0);f<8;f++) {
    if(feSoftTrigger(f)) o << s << "  FE" << f << " soft trigger" << std::endl;
  }
  
  return o;
}

#endif
#endif
