#ifndef CrcVlinkFeHeader_HH
#define CrcVlinkFeHeader_HH

#include <string>
#include <iostream>


class CrcVlinkFeHeader {

public:
  CrcVlinkFeHeader();

  unsigned frameSyncOutPacketHalfWord(unsigned i) const;

  bool fixedData() const;
  bool adcData() const;
  bool trgData() const;
  bool  noData() const;

  unsigned beStatus() const;

  unsigned short feLength() const;

  unsigned short feWords() const;

  void data(unsigned a, unsigned b, unsigned c, unsigned d);

  bool verify() const;

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


private:
  UtlPack _data[4];
};


#ifdef CALICE_DAQ_ICC

CrcVlinkFeHeader::CrcVlinkFeHeader() {
}

unsigned CrcVlinkFeHeader::frameSyncOutPacketHalfWord(unsigned i) const {
  assert(i<5);
  if(i==0) return _data[1+2*(i/4)].halfWord(i%2);
  if(i==1) return _data[1+2*(i/4)].halfWord(i%2);
  if(i==2) return _data[0+2*(i/4)].halfWord(i%2);
  if(i==3) return _data[0+2*(i/4)].halfWord(i%2);
  //if(i==4)
  return _data[1+2*(i/4)].halfWord(i%2);
}

bool CrcVlinkFeHeader::fixedData() const {
  // These are the "faked" constant data in the BE firmware
  return
    _data[3].halfWord(0)==0x0102 &&
    _data[0].word() ==0x03040506 &&
    _data[1].halfWord(1)==0x0708;
}

bool CrcVlinkFeHeader::adcData() const {
  return fixedData() &&
    (_data[1].halfWord(0)==0x090a ||
     _data[1].halfWord(0)==0x0000);
}

bool CrcVlinkFeHeader::trgData() const {
  return 
    // Original firmware
    (_data[3].halfWord(0)==0x8182 &&
     _data[0].word()==0x83848586 &&
     _data[1].word()==0x8788898a) ||
    // New "faked" header firmware
    (fixedData() &&
     _data[1].halfWord(0)==0x898a);
}

bool CrcVlinkFeHeader::noData() const {
  return _data[3].halfWord(0)==0 &&
    _data[0].word()==0 &&
    _data[1].word()==0;
}

unsigned CrcVlinkFeHeader::beStatus() const {
  return (_data[2].halfWord(0)<<16) + _data[3].halfWord(1);
}

unsigned short CrcVlinkFeHeader::feLength() const {
  return _data[2].halfWord(1);
}

unsigned short CrcVlinkFeHeader::feWords() const {
  return (feLength()+3)/4;
}

void CrcVlinkFeHeader::data(unsigned a, unsigned b, unsigned c, unsigned d) {
  _data[0].word(a);
  _data[1].word(b);
  _data[2].word(c);
  _data[3].word(d);
}

bool CrcVlinkFeHeader::verify() const {
  return (adcData() || trgData() || noData()) && (feLength()%4)==0;
}

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

  for(unsigned i(0);i<5;i++) {
    o << s << "  FrameSyncOut packet half word " << i << " = "
      << printHex(frameSyncOutPacketHalfWord(i)) << std::endl;
  }
  if(adcData())      o << s << "   ADC data FrameSyncOut packet" << std::endl;
  else if(trgData()) o << s << "   Trg data FrameSyncOut packet" << std::endl;
  else if(noData())  o << s << "   No data FrameSyncOut packet" << std::endl;
  else               o << s << "   Unknown FrameSyncOut packet" << std::endl;

  o << s << "  BE status = " << printHex(beStatus()) << std::endl;
  o << s << "  FE length = " << std::setw(5) << feLength() << " bytes = " 
    << std::setw(5) << feWords() << " words" << std::endl;

  return o;
}

#endif
#endif
