#ifndef UtlPack_HH
#define UtlPack_HH

#include <string>
#include <iostream>


class UtlPack {

public:
  UtlPack();  
  UtlPack(unsigned w);
  
  bool bit(unsigned b) const;
  void bit(unsigned b, bool c);

  unsigned bits(unsigned bLo, unsigned bHi) const;
  void     bits(unsigned bLo, unsigned bHi, unsigned b);
  
  unsigned char halfByte(unsigned b) const;  
  void          halfByte(unsigned b, unsigned char c);
  
  unsigned char byte(unsigned b) const;
  void          byte(unsigned b, unsigned char c);

  unsigned short halfWord(unsigned b) const;
  void           halfWord(unsigned b, unsigned short s);
  
  unsigned word() const;  
  void     word(unsigned w);

  bool operator==(const UtlPack &d) const;  
  bool operator!=(const UtlPack &d) const;  

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


private:
  unsigned _word;
};


#ifdef CALICE_DAQ_ICC

#include <assert.h>

#include "UtlPrintHex.hh"


UtlPack::UtlPack() : _word(0) {
}

UtlPack::UtlPack(unsigned w) : _word(w) {
}

bool UtlPack::bit(unsigned b) const {
  assert(b<32);
  return (_word>>b)&0x1;
}
  
void UtlPack::bit(unsigned b, bool c) {
  assert(b<32);
  _word&=~(1<<b);
  if(c) _word|=1<<b;
}

unsigned UtlPack::bits(unsigned bLo, unsigned bHi) const {
  assert(bHi<32 && bHi>=bLo);
  //std::cout << "Utlpack reports " << printHex((_word<<(31-bHi))>>(31-bHi+bLo)) << std::endl;
  return (_word<<(31-bHi))>>(31-bHi+bLo);
}

void UtlPack::bits(unsigned bLo, unsigned bHi, unsigned b) {
  assert(bHi<32 && bHi>=bLo);

  for(unsigned i=bLo;i<=bHi;i++) bit(i,false);

  _word=((_word>>bHi)<<bHi) | (b<<(31-bHi+bLo))>>(31-bHi) | ((_word<<(31-bLo))>>(31-bLo));
  //std::cout << "Utlpack set to " << printHex(_word) << std::endl;
}

unsigned char UtlPack::halfByte(unsigned b) const {
  assert(b<8);
  return (_word>>(4*b))&0xf;
}
  
void UtlPack::halfByte(unsigned b, unsigned char c) {
  assert(b<8);
  _word&=~(0xf<<(4*b));
  _word|=(c&0xf)<<(4*b);
}
  
unsigned char UtlPack::byte(unsigned b) const {
  assert(b<4);
  return (_word>>(8*b))&0xff;
}
  
void UtlPack::byte(unsigned b, unsigned char c) {
  assert(b<4);
    _word&=~(0xff<<(8*b));
    _word|=c<<(8*b);
}

unsigned short UtlPack::halfWord(unsigned b) const {
  assert(b<2);
  return (_word>>(16*b))&0xffff;
}  

void UtlPack::halfWord(unsigned b, unsigned short s) {
  assert(b<2);
  _word&=~(0xffff<<(16*b));
    _word|=s<<(16*b);
}

unsigned UtlPack::word() const {
  return _word;
}
  
void UtlPack::word(unsigned w) {
  _word=w;
}
  
bool UtlPack::operator==(const UtlPack &d) const {
  return _word==d._word;
}
  
bool UtlPack::operator!=(const UtlPack &d) const {
  return _word!=d._word;
}

std::ostream& UtlPack::print(std::ostream &o, std::string s) {
  o << s << "UtlPack::print()" << std::endl;
  o << s << " Word: "
    << printHex(word()) << std::endl;
  o << s << " Half words: "
    << printHex(halfWord(1)) << ", "
    << printHex(halfWord(0)) << std::endl;
  o << s << " Bytes: "
    << printHex(byte(3)) << ", "
    << printHex(byte(2)) << ", "
    << printHex(byte(1)) << ", "
    << printHex(byte(0)) << std::endl;
  o << s << " Half bytes: "
    << printHex(halfByte(7)) << ", "
    << printHex(halfByte(6)) << ", "
    << printHex(halfByte(5)) << ", "
    << printHex(halfByte(4)) << ", "
    << printHex(halfByte(3)) << ", "
    << printHex(halfByte(2)) << ", "
    << printHex(halfByte(1)) << ", "
    << printHex(halfByte(0)) << std::endl;
  return o;
}

std::string printHex(UtlPack n, bool b=true) {
  return printHex(n.word(),b);
}

#endif
#endif
