#ifndef IlcRunType_HH
#define IlcRunType_HH

/**********************************************************************
 * IlcRunType - defines run types available
 *
 * Whenever you add or change the run types, you must also edit the
 * DaqConfiguration.hh and corresponding XxxConfiguration.hh file
 *
 **********************************************************************/

#include <string>
#include <iostream>

#include "UtlPack.hh"


class IlcRunType {

public:
  enum MajorType {
    daq,      // DAQ tests only; no readout
    slw,      // Slow controls and readout only
    usb,      // USB_DAQ board
    mps,      // MAPS
    mps1,     // MAPS continuation
    endOfMajorTypeEnum
  };

  enum Type {
    // daq
    daqTest=0x10*daq,
    daqDummy,
    endOfDaqTypeEnum,

     // slow
    slwTest=0x10*slw,
    slwMonitor,
 
   // usb
    usbTest=0x10*usb,
    usbCableTest,
    usbMemoryTest,
    usbPmtThreshold,
    usbPmtThresholdScan,

    // mps
    mpsTest=0x10*mps,
    mpsExpert,
    mpsNoise,
    mpsPcbConfigurationScan,
    mpsThreshold,
    mpsThresholdScan,
    mpsTrim,
    mpsTrimScan,
    mpsHitOverride,
    mpsBeam,
    mpsCosmics,
    mpsSource,
    mpsLaser,
    mpsLaserPosition,
    mpsLaserPositionScan,
    mpsLaserThreshold,
    mpsLaserThresholdScan,
    mpsMonostableLengthScan,
    mpsSourceThresholdScan,
    mpsConfigurationTest,
    mpsUsbDaqConfigurationScan,
    mpsLaserCoordinates,
    mpsBeamThresholdScan,
    mpsCosmicsThresholdScan,
    mpsMaskThresholdScan,
    mpsLaserTime,
    mpsLaserTimeScan,
    mpsLaserTrim,
    mpsLaserTrimScan,

    endOfTypeEnum
  };


  IlcRunType();
  IlcRunType(Type t, unsigned char m=0, unsigned char v=0);
  IlcRunType(std::string s, unsigned char m=0, unsigned char v=0);

  Type type() const;
  void type(Type s);

  MajorType majorType() const;

  std::string        typeName() const;
  static std::string typeName(Type t);

  std::string        typeComment() const;
  static std::string typeComment(Type t);

  std::string        majorTypeName() const;
  static std::string majorTypeName(MajorType t);

  bool        knownType() const;
  static bool knownType(Type t);

  static Type typeNumber(std::string s);

  unsigned char printLevel() const;
  void          printLevel(unsigned char m);

  unsigned char switches() const;
  void          switches(unsigned char m);

  bool bufferRun() const;
  void bufferRun(bool b);

  bool spillRun() const;
  void spillRun(bool b);

  bool transferRun() const;
  void transferRun(bool b);

  bool endlessRun() const;
  void endlessRun(bool b);

  bool histogramRun() const;
  void histogramRun(bool b);

  bool ascWriteRun() const;
  void ascWriteRun(bool b);

  bool writeRun() const;
  void writeRun(bool b);

  unsigned char version() const;
  void          version(unsigned char v);

  unsigned char        defaultVersion() const;
  static unsigned char defaultVersion(Type t);

  UtlPack data() const;
  void    data(UtlPack n);

  bool beamType() const;
  bool cosmicsType() const;

  static void dumpTypes(const std::string &f);

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


private:
  static const std::string _majorTypeName[endOfMajorTypeEnum];
  static const std::string _typeName[endOfTypeEnum];
  static const std::string _typeComment[endOfTypeEnum];
  static const unsigned char _defaultVersion[endOfTypeEnum];

  // Byte 0 = version number
  // Byte 1 = bit switches (bits 8-10, 12-15)
  // Byte 2 = print level
  // Byte 3 = type (major type = bits 28-31)
  UtlPack _data;
};


#ifdef CALICE_DAQ_ICC

#include <fstream>


IlcRunType::IlcRunType() : _data(0) {
}

IlcRunType::IlcRunType(Type t, unsigned char m, unsigned char v) :
  _data(0) {
  type(t);
  switches(m);
  version(v);
}

IlcRunType::IlcRunType(std::string s, unsigned char m, unsigned char v) :
  _data(0) {
  type(typeNumber(s));
  switches(m);
  version(v);
}

IlcRunType::Type IlcRunType::type() const {
  return (Type)_data.byte(3);
}

void IlcRunType::type(Type n) {
  _data.byte(3,(unsigned char)n);
  _data.byte(0,defaultVersion(n));
}

IlcRunType::MajorType IlcRunType::majorType() const {
  MajorType m((MajorType)_data.halfByte(7));
  if(m==mps1) m=mps; // Catch second range of MAPS
  return m;
}

std::string IlcRunType::typeName() const {
  return typeName(type());
}
 
std::string IlcRunType::typeName(Type t) {
  if(t<endOfTypeEnum) return _typeName[t];
  return "unknown";
}

std::string IlcRunType::typeComment() const {
  return typeComment(type());
}
 
std::string IlcRunType::typeComment(Type t) {
  if(t<endOfTypeEnum) return _typeComment[t];
  return "unknown";
}

std::string IlcRunType::majorTypeName() const {
  return majorTypeName(majorType());
}
 
std::string IlcRunType::majorTypeName(MajorType t) {
  if(t<endOfMajorTypeEnum) return _majorTypeName[t];
  return "unknown";
}

bool IlcRunType::knownType() const {
  return knownType(type());
}

bool IlcRunType::knownType(Type t) {
  return typeName(t)!="unknown";
}

IlcRunType::Type IlcRunType::typeNumber(std::string s) {
  for(unsigned t(0);t<endOfTypeEnum;t++) {
    if(s==typeName((Type)t)) return (Type)t;
  }
  return (Type)255;
}

unsigned char IlcRunType::printLevel() const {
  return _data.byte(2);
}

void IlcRunType::printLevel(unsigned char m) {
  _data.byte(2,m);
}

unsigned char IlcRunType::switches() const {
  return _data.byte(1);
}

void IlcRunType::switches(unsigned char m) {
  _data.byte(1,m);
}

bool IlcRunType::bufferRun() const {
  return _data.bit(8);
}

void IlcRunType::bufferRun(bool b) {
  return _data.bit(8,b);
}

bool IlcRunType::spillRun() const {
  return _data.bit(9);
}

void IlcRunType::spillRun(bool b) {
  return _data.bit(9,b);
}

bool IlcRunType::transferRun() const {
  return _data.bit(10);
}

void IlcRunType::transferRun(bool b) {
  return _data.bit(10,b);
}

bool IlcRunType::endlessRun() const {
  return _data.bit(12);
}

void IlcRunType::endlessRun(bool b) {
  return _data.bit(12,b);
}

bool IlcRunType::histogramRun() const {
  return _data.bit(13);
}

void IlcRunType::histogramRun(bool b) {
  return _data.bit(13,b);
}

bool IlcRunType::ascWriteRun() const {
  return _data.bit(14);
}

void IlcRunType::ascWriteRun(bool b) {
  return _data.bit(14,b);
}

bool IlcRunType::writeRun() const {
  return _data.bit(15);
}

void IlcRunType::writeRun(bool b) {
  return _data.bit(15,b);
}

unsigned char IlcRunType::version() const {
  return _data.byte(0);
}

void IlcRunType::version(unsigned char v) {
  _data.byte(0,v);
}

unsigned char IlcRunType::defaultVersion() const {
  return defaultVersion(type());
}
 
unsigned char IlcRunType::defaultVersion(Type t) {
  if(t<endOfTypeEnum) return _defaultVersion[t];
  return 0;
}

UtlPack IlcRunType::data() const {
  return _data;
}
 
void IlcRunType::data(UtlPack d) {
  _data=d;
}

bool IlcRunType::beamType() const {
  Type t(type());
  return t==IlcRunType::mpsBeam;
}

bool IlcRunType::cosmicsType() const {
  Type t(type());
  return t==IlcRunType::mpsCosmics;
}

void IlcRunType::dumpTypes(const std::string &f) {
  std::ofstream fout(f.c_str());
  if(fout) {

    for(unsigned t(0);t<endOfTypeEnum;t++) {
      if(knownType((Type)t)) {
	fout << std::setw( 3) << t
	     << std::setw( 8) << _majorTypeName[t>>4]
	     << std::setw(30) << _typeName[t]
	     << std::setw( 6) << (unsigned)_defaultVersion[t] << std::endl;
      }
    }

    fout.close();
  }
}

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

  o << s << " Data = " << printHex(_data) << std::endl;

  o << s << "  Type    = " << std::setw(3) << type()
    << " = " << typeName();
  if(beamType()) o << " = beam type";
  if(cosmicsType()) o << " = cosmics type";
  o << ", Major type = " << std::setw(3) << majorType()
    << " = " << majorTypeName() << std::endl;

  o << s << "  Version = " << std::setw(5) << (unsigned)version()
    << " (default = " << std::setw(5) << (unsigned)defaultVersion() << ")";
  o << ", Print level = " << std::setw(3) << (unsigned)printLevel() << std::endl;

  o << s << "  Switches  = " << printHex(switches()) << std::endl;

  if(majorType()!=slw) {
    //o << s << "   Data run" << std::endl;
    if(bufferRun()) {
      o << s << "   Buffering data run" << std::endl;
      if(spillRun())    o << s << "   Multi-event spill run" << std::endl;
      else              o << s << "   Single-event spill run" << std::endl;
      if(transferRun()) o << s << "   Multi-event transfer run" << std::endl;
      else              o << s << "   Single-event transfer run" << std::endl;
    } else {
      o << s << "   Single event data run" << std::endl;
    }
  } else {
    o << s << "   Slow monitoring run" << std::endl;
  }

  if(writeRun()) {
    //o << s << "   Write run" << std::endl;
    if(ascWriteRun()) o << s << "   Ascii write run" << std::endl;
    else              o << s << "   Binary write run" << std::endl;
  } else {
    o << s << "   Dummy write run" << std::endl;
  }

  if(endlessRun()) o << s << "   Endless run enabled" << std::endl;
  else             o << s << "   Endless run disabled" << std::endl;

  if(histogramRun()) o << s << "   Histogram filling enabled" << std::endl;
  else               o << s << "   Histogram filling disabled" << std::endl;

  return o;
}

const std::string IlcRunType::_majorTypeName[]={
  "daq",
  "slw",
  "usb",
  "mps",
  "mps"
};

const std::string IlcRunType::_typeName[]={
  "daqTest",
  "daqDummy",
  "unknown","unknown","unknown","unknown",
  "unknown","unknown","unknown","unknown","unknown",
  "unknown","unknown","unknown","unknown","unknown",

  "slwTest",
  "slwMonitor",
  "unknown","unknown","unknown","unknown",
  "unknown","unknown","unknown","unknown","unknown",
  "unknown","unknown","unknown","unknown","unknown",

  "usbTest",
  "usbCableTest",
  "usbMemoryTest",
  "usbPmtThreshold",
  "usbPmtThresholdScan",
  "unknown",
  "unknown","unknown","unknown","unknown","unknown",
  "unknown","unknown","unknown","unknown","unknown",

  "mpsTest",
  "mpsExpert",
  "mpsNoise",
  "mpsPcbConfigurationScan",
  "mpsThreshold",
  "mpsThresholdScan",
  "mpsTrim",
  "mpsTrimScan",
  "mpsHitOverride",
  "mpsBeam",
  "mpsCosmics",
  "mpsSource",
  "mpsLaser",
  "mpsLaserPosition",
  "mpsLaserPositionScan",
  "mpsLaserThreshold",

  "mpsLaserThresholdScan",
  "mpsMonostableLengthScan",
  "mpsSourceThresholdScan",
  "mpsConfigurationTest",
  "mpsUsbDaqConfigurationScan",
  "mpsLaserCoordinates",
  "mpsBeamThresholdScan",
  "mpsCosmicsThresholdScan",
  "mpsMaskThresholdScan",
  "mpsLaserTime",
  "mpsLaserTimeScan",
  "mpsLaserTrim",
  "mpsLaserTrimScan"
};

const unsigned char IlcRunType::_defaultVersion[]={
  // daq
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  // slw
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  // usb
  0,  0,  0,  0, 99,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  // msp
  0,  0,  0,  0,  0,199,  0,  0,  0,  0,  0,  0,  0,  0, 14,  0,
  0,  0,  0,  0, 48, 14,  0,  0,  0,  0,  0,  0,  0
};

const std::string IlcRunType::_typeComment[]={
  std::string("daqTest: ")+
  "Used for run type development; versions undefined",

  std::string("daqDummy: ")+
  "Do-nothing run",

  "unknown","unknown","unknown","unknown",
  "unknown","unknown","unknown","unknown","unknown",
  "unknown","unknown","unknown","unknown","unknown",


  std::string("slowTest: ")+
  "Used for run type development; versions undefined",

  "slowMonitor",
  "unknown","unknown","unknown","unknown",
  "unknown","unknown","unknown","unknown","unknown",
  "unknown","unknown","unknown","unknown","unknown",


  std::string("usbTest: ")+
  "Used for run type development; versions undefined",

  std::string("usbCableTest: ")+
  "USB_DAQ cable test",

  std::string("usbMemoryTest: ")+
  "USB_DAQ memory test",

  std::string("usbPmtThreshold: ")+
  "USB_DAQ PMT threshold",

  std::string("usbPmtThresholdScan: ")+
  "USB_DAQ PMT threshold scan\n"+
  " Number of steps in scan = version+1,\n"+
  " distributed evenly between 0 and 255",

  "unknown",
  "unknown","unknown","unknown","unknown","unknown",
  "unknown","unknown","unknown","unknown","unknown",


  "mpsTest",
  "mpsExpert",

  std::string("mpsNoise: ")+
  "Basic noise run for stand-alone sensor performance\n"+
  "Version number controls data and masking of sensor\n"+
  " Version bits 0-3 = mask off regions 0-3\n"+
  " Version bit 4 = invert all mask bits\n"+
  " Version bit 7 = set HitOverride",

  std::string("mpsPcbConfigurationScan: ")+
  "Scan over sensor PCB DAC values\n"+
  " Version bits 0-4: DAC to scan\n"+
  "  0-31 = Single DAC\n"+
  "  although thresholds and common mode handled\n"+
  "  by values 21,22,23,25,27,30\n"+
  " Version bits 5-6: Number of bunch trains/configuration\n"+
  "  Number = 10^v(5:6)\n"+
  " Version bit 7: Run forever if set",

  "mpsThreshold",
  "mpsThresholdScan",
  "mpsTrim",
  "mpsTrimScan",
  "mpsHitOverride",
  "mpsBeam",
  "mpsCosmics",
  "mpsSource",
  "mpsLaser",
  "mpsLaserPosition",
  "mpsLaserPositionScan",
  "mpsLaserThreshold",
  "mpsLaserThresholdScan",
  "mpsMonostableLengthScan",
  "mpsSourceThresholdScan",
  "mpsConfigurationTest",

  std::string("mpsUsbDaqConfigurationScan: ")+
  "Scan over USB_DAQ configuration data parameters\n"+
  " Version bits 0-3: Parameter to scan\n"+
  "  0 = Reset durations\n"+
  " Version bits 4-6: Number of bunch trains/configuration\n"+
  "  Number = 10^v(4:6)\n"+
  " Version bit 7: Run forever if set",

  "mpsLaserCoordinates",
  "mpsBeamThresholdScan",
  "mpsCosmicsThresholdScan",
  "mpsMaskThresholdScan",
  "mpsLaserTime",
  "mpsLaserTimeScan",
  "mpsLaserTrim",
  "mpsLaserTrimScan"
};

#endif
#endif
