#ifndef BmlCaen767OpcodeData_HH
#define BmlCaen767OpcodeData_HH

#include <iostream>
#include <fstream>

#include "UtlPack.hh"


class BmlCaen767OpcodeData {

public:
  enum {
    versionNumber=0
  };

  BmlCaen767OpcodeData();

  bool memoryTest() const;
  void memoryTest(bool b);

  bool stopTriggerMatching() const;
  bool startTriggerMatching() const;
  bool startGating() const;
  bool continuousStorage() const;
  void acquisitionMode(unsigned n);

  bool autoLoad() const;
  void autoLoad(bool b);

  bool enable(unsigned c) const;
  void enable(unsigned c, bool b);
  unsigned short enablePattern(unsigned b) const;
  void           enablePattern(unsigned b, unsigned short n);

  unsigned short windowWidth() const;
  void           windowWidth(unsigned short w);
  short windowOffset() const;
  void  windowOffset(short w);
  unsigned short triggerLatency() const;
  void           triggerLatency(unsigned short w);

  bool subtractTriggerTime() const;
  bool overlappingTriggers() const;
  void triggerConfiguration(unsigned short c);

  bool readoutStartTime() const;
  bool readout4StartTimes() const;
  bool subtractStartTime() const;
  bool emptyStart() const;
  void startConfiguration(unsigned short c);

  unsigned short globalOffset() const;
  void           globalOffset(unsigned short g);
  bool allAdjusts() const;
  void allAdjusts(bool b);

  bool risingEdgeAllChannels() const;
  bool fallingEdgeAllChannels() const;
  bool risingOddFallingEven() const;
  bool risingEvenFallingOdd() const;
  bool unknownChannelDetection() const;
  bool risingEdgeStart() const;
  bool fallingEdgeStart() const;
  bool unknownStartDetection() const;
  bool bothEdgesAllChannels() const;
  void detection(unsigned b, unsigned short d);

  bool dataReadyEventReady() const;
  bool dataReadyBufferAlmostFull() const;
  bool dataReadyBufferNotEmpty() const;
  bool dataReadyUnknown() const;
  void dataReadyMode(unsigned short m);
  unsigned short almostFullLevel() const;
  void           almostFullLevel(unsigned short g);

  unsigned short controlRegister2() const;

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

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


private:
  UtlPack _bits;
  UtlPack _enablePattern[4];
  UtlPack _window;
  UtlPack _latencyOffset;
  UtlPack _almostFull;
};


#ifdef CALICE_DAQ_ICC

#include <cstring>

#include "UtlPrintHex.hh"


BmlCaen767OpcodeData::BmlCaen767OpcodeData() {
  memset(this,0,sizeof(BmlCaen767OpcodeData));
  //memoryTest(true);
  //acquisitionMode(1);
  _enablePattern[0].halfWord(0,0x3ff); // Channels 0-9

  windowWidth(200); // 5us
  //windowWidth(500); // 12.5us: TEMP TO SEE FNAL BEAM STRUCTURE
  windowOffset(-20); // 500ns

  triggerConfiguration(1);
  //detection(0,3); // Leading and fallling edges
  //detection(1,3);
  //detection(2,3);
  detection(0,1); // Leading edge only
  detection(1,1);
  detection(2,1);


  //dataReadyMode(1);
  //almostFullLevel(2); // Minimum allowed
  //almostFullLevel(32767); // Maximum allowed
  //almostFullLevel(32000); // CERN
  almostFullLevel(29000);// FNAL 16 words x 200 triggers below 32k
}

bool BmlCaen767OpcodeData::memoryTest() const {
  return _bits.bit(0);
}

void BmlCaen767OpcodeData::memoryTest(bool b) {
  _bits.bit(0,b);
}

bool BmlCaen767OpcodeData::stopTriggerMatching() const {
  return _bits.bits(1,2)==0;
}

bool BmlCaen767OpcodeData::startTriggerMatching() const {
  return _bits.bits(1,2)==1;
}

bool BmlCaen767OpcodeData::startGating() const {
  return _bits.bits(1,2)==2;
}

bool BmlCaen767OpcodeData::continuousStorage() const {
  return _bits.bits(1,2)==3;
}

void BmlCaen767OpcodeData::acquisitionMode(unsigned n) {
  _bits.bits(1,2,n);
}

bool BmlCaen767OpcodeData::autoLoad() const {
  return _bits.bit(3);
}

void BmlCaen767OpcodeData::autoLoad(bool b) {
  _bits.bit(3,b);
}

unsigned short BmlCaen767OpcodeData::enablePattern(unsigned b) const {
  assert(b<8);
  return _enablePattern[b/2].halfWord(b%2);
}

void BmlCaen767OpcodeData::enablePattern(unsigned b, unsigned short n) {
  assert(b<8);
  _enablePattern[b/2].halfWord(b%2,n);
}

unsigned short BmlCaen767OpcodeData::windowWidth() const  {
  return _window.halfWord(0);
}

void BmlCaen767OpcodeData::windowWidth(unsigned short w)  {
  _window.halfWord(0,w);
}

short BmlCaen767OpcodeData::windowOffset() const  {
  return (short)_window.halfWord(1);
}

void BmlCaen767OpcodeData::windowOffset(short w)  {
  _window.halfWord(1,(unsigned short)w);
}

unsigned short BmlCaen767OpcodeData::triggerLatency() const  {
  return _latencyOffset.halfWord(0);
}

void BmlCaen767OpcodeData::triggerLatency(unsigned short w)  {
  _latencyOffset.halfWord(0,w);
}

bool BmlCaen767OpcodeData::subtractTriggerTime() const {
  return _bits.bit(4);
}

bool BmlCaen767OpcodeData::overlappingTriggers() const {
  return _bits.bit(5);
}

void BmlCaen767OpcodeData::triggerConfiguration(unsigned short c) {
  _bits.bits(4,5,c);
}

bool BmlCaen767OpcodeData::readoutStartTime() const {
  return _bits.bits(6,7)==1;
}

bool BmlCaen767OpcodeData::readout4StartTimes() const {
  return _bits.bits(6,7)==2;
}

bool BmlCaen767OpcodeData::subtractStartTime() const {
  return _bits.bit(8);
}

bool BmlCaen767OpcodeData::emptyStart() const {
  return _bits.bit(9);
}

void BmlCaen767OpcodeData::startConfiguration(unsigned short c) {
  _bits.bits(6,9,c);
}

unsigned short BmlCaen767OpcodeData::globalOffset() const {
  return _latencyOffset.halfWord(1);
}

void BmlCaen767OpcodeData::globalOffset(unsigned short g) {
  _latencyOffset.halfWord(1,g);
}

bool BmlCaen767OpcodeData::allAdjusts() const {
  return _bits.bit(10);
}

void BmlCaen767OpcodeData::allAdjusts(bool b) {
  _bits.bit(10,b);
}

bool BmlCaen767OpcodeData::risingEdgeAllChannels() const {
  return _bits.bit(11) && !_bits.bit(12) && _bits.bit(13) && !_bits.bit(14);
}

bool BmlCaen767OpcodeData::fallingEdgeAllChannels() const {
  return !_bits.bit(11) && _bits.bit(12) && !_bits.bit(13) && _bits.bit(14);
}

bool BmlCaen767OpcodeData::risingOddFallingEven() const {
  return !_bits.bit(11) && _bits.bit(12) && _bits.bit(13) && !_bits.bit(14);
}

bool BmlCaen767OpcodeData::risingEvenFallingOdd() const {
  return _bits.bit(11) && !_bits.bit(12) && !_bits.bit(13) && _bits.bit(14);
}

bool BmlCaen767OpcodeData::unknownChannelDetection() const {
  return !risingEdgeAllChannels() && !fallingEdgeAllChannels()
    && !risingOddFallingEven() && !risingEvenFallingOdd() && (_bits.bits(11,14)!=0xf);
}

bool BmlCaen767OpcodeData::risingEdgeStart() const {
  return _bits.bit(15) && !_bits.bit(16);
}

bool BmlCaen767OpcodeData::fallingEdgeStart() const {
  return !_bits.bit(15) && _bits.bit(16);
}

bool BmlCaen767OpcodeData::unknownStartDetection() const {
  return _bits.bits(15,16)==0;
}

bool BmlCaen767OpcodeData::bothEdgesAllChannels() const {
  return _bits.bits(11,16)==0x3f;
}

void BmlCaen767OpcodeData::detection(unsigned b, unsigned short d) {
  assert(b<3);
  _bits.bits(11+2*b,12+2*b,d);
}

bool BmlCaen767OpcodeData::dataReadyEventReady() const {
  return _bits.bits(17,18)==0;
}

bool BmlCaen767OpcodeData::dataReadyBufferAlmostFull() const {
  return _bits.bits(17,18)==1;
}

bool BmlCaen767OpcodeData::dataReadyBufferNotEmpty() const {
  return _bits.bits(17,18)==2;
}

bool BmlCaen767OpcodeData::dataReadyUnknown() const {
  return _bits.bits(17,18)==3;
}

void BmlCaen767OpcodeData::dataReadyMode(unsigned short m) {
  _bits.bits(17,18,m);
}

unsigned short BmlCaen767OpcodeData::almostFullLevel() const {
  return _almostFull.halfWord(0);
}

void BmlCaen767OpcodeData::almostFullLevel(unsigned short g) {
  _almostFull.halfWord(0,g);
}

unsigned short BmlCaen767OpcodeData::controlRegister2() const {
  UtlPack reply(0);
  reply.bits(0,1,_bits.bits(1,2));   // Acquisition mode
  reply.bits(2,3,_bits.bits(17,18)); // Data ready mode
  reply.bit(4,_bits.bit(0));         // Memory test mode
  return reply.halfWord(0);
}

bool BmlCaen767OpcodeData::operator!=(const BmlCaen767OpcodeData &c) const {
  if(_bits!=c._bits) return true;
  if(_enablePattern[0]!=c._enablePattern[0]) return true;
  if(_enablePattern[1]!=c._enablePattern[1]) return true;
  if(_enablePattern[2]!=c._enablePattern[2]) return true;
  if(_enablePattern[3]!=c._enablePattern[3]) return true;
  if(_window!=c._window) return true;
  if(_latencyOffset!=c._latencyOffset) return true;
  if(_almostFull!=c._almostFull) return true;
  return false;
}
 
bool BmlCaen767OpcodeData::operator==(const BmlCaen767OpcodeData &c) const {
  return !operator!=(c);
}

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

  o << s << " Bits         = " << printHex(_bits) << std::endl;

  if(memoryTest()) o << s << "  Memory test enabled" << std::endl;
  else             o << s << "  Memory test disabled" << std::endl;

  if(stopTriggerMatching())   o << s << "  Stop trigger matching enabled" << std::endl;
  if(startTriggerMatching())  o << s << "  Start trigger matching enabled" << std::endl;
  if(startGating())           o << s << "  Start gating enabled" << std::endl;
  if(continuousStorage())     o << s << "  Continuous storage enabled" << std::endl;
  if(autoLoad()) o << s << "  Autoload enabled" << std::endl;
  else           o << s << "  Autoload disabled" << std::endl;

  if(subtractTriggerTime()) o << s << "  Subtraction of trigger time enabled" << std::endl;
  else                      o << s << "  Subtraction of trigger time disabled" << std::endl;
  if(overlappingTriggers()) o << s << "  Overlapping triggers enabled" << std::endl;
  else                      o << s << "  Overlapping triggers disabled" << std::endl;

  if(readoutStartTime())        o << s << "  Readout of start time enabled" << std::endl;
  else if(readout4StartTimes()) o << s << "  Readout of 4 start times enabled" << std::endl;
  else                          o << s << "  Readout of start time disabled" << std::endl;
  if(subtractStartTime()) o << s << "  Subtraction of start time enabled" << std::endl;
  else                    o << s << "  Subtraction of start time disabled" << std::endl;
  if(emptyStart()) o << s << "  Empty start enabled" << std::endl;
  else             o << s << "  Empty start disabled" << std::endl;
  if(allAdjusts()) o << s << "  All adjusts enabled" << std::endl;
  else             o << s << "  All adjusts disabled" << std::endl;

  if(risingEdgeAllChannels())   o << s << "  Rising edge detection only on all channels" << std::endl;
  if(fallingEdgeAllChannels())  o << s << "  Falling edge detection only on all channels" << std::endl;
  if(risingOddFallingEven())    o << s << "  Rising edge detection on odd channels, falling on even" << std::endl;
  if(risingEvenFallingOdd())    o << s << "  Rising edge detection on even channels, falling on odd" << std::endl;
  if(unknownChannelDetection()) o << s << "  Detection on channels unknown" << std::endl;
  if(risingEdgeStart())         o << s << "  Rising edge detection only on start" << std::endl;
  if(fallingEdgeStart())        o << s << "  Falling edge detection only on start" << std::endl;
  if(unknownStartDetection())   o << s << "  Detection on start unknown" << std::endl;
  if(bothEdgesAllChannels())    o << s << "  Rising and falling edge detection on all channels and start" << std::endl;
  
  if(dataReadyEventReady())       o << s << "  Data ready = event ready" << std::endl;
  if(dataReadyBufferAlmostFull()) o << s << "  Data ready = buffer almost full" << std::endl;
  if(dataReadyBufferNotEmpty())   o << s << "  Data ready = buffer not empty" << std::endl;
  if(dataReadyUnknown())          o << s << "  Data ready = unknown" << std::endl;
  

  for(unsigned i(0);i<4;i++) {
    o << s << " Enable pattern " << i << " = " << printHex(_enablePattern[i]) << std::endl;
  }

  o << s << " Window width = " << windowWidth()
    << ", offset = " << windowOffset()
    << ", trigger latency = " << triggerLatency() << std::endl;

  o << s << " Global offset = " << globalOffset() << std::endl;

  o << s << " Almost full level = " << almostFullLevel() << std::endl;
  

  return o;
}

#endif
#endif
