#ifndef CrcVlinkEventData_HH
#define CrcVlinkEventData_HH

#include <iostream>
#include <fstream>
#include <string>

#include "UtlPrintHex.hh"
#include "UtlPack.hh"

#include "CrcVlinkHeader.hh"
#include "CrcVlinkTrailer.hh"
#include "CrcVlinkFeHeader.hh"
#include "CrcVlinkFeData.hh"
#include "CrcVlinkTrgData.hh"
#include "CrcVlinkAdcSample.hh"


class CrcVlinkEventData {

public:
  enum {
    versionNumber=0
  };

  CrcVlinkEventData();

  unsigned numberOfWords() const;
  void     numberOfWords(unsigned n);

  const unsigned* data() const;
  unsigned*       data();

  unsigned bufferNumber() const;
  void     bufferNumber(unsigned n);

  const CrcVlinkHeader* header() const;
  CrcVlinkHeader*       header();

  const CrcVlinkFeHeader* feHeader(unsigned f) const;
  CrcVlinkFeHeader*       feHeader(unsigned f);

  const CrcVlinkFeData* feData(unsigned f) const;
  CrcVlinkFeData*       feData(unsigned f);

  const CrcVlinkTrgData* trgData() const;
  CrcVlinkTrgData*       trgData();

  unsigned feNumberOfAdcSamples(unsigned f) const;
  //const CrcVlinkAdcSample* feAdcSample(unsigned f, unsigned s) const;

  const CrcVlinkTrailer* trailer() const;
  CrcVlinkTrailer*       trailer();

  unsigned totalLength() const;
  void     totalLength(unsigned n);

  unsigned numberOfSamples(unsigned f) const;

  int adc(unsigned f, unsigned chip, unsigned chan) const;

  bool verify(bool p=false) const;

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


private:
  unsigned _numberOfWords;
};


#ifdef CALICE_DAQ_ICC

CrcVlinkEventData::CrcVlinkEventData() {
  memset(this,0,sizeof(CrcVlinkEventData));
}

unsigned CrcVlinkEventData::numberOfWords() const {
  return _numberOfWords;
}

void CrcVlinkEventData::numberOfWords(unsigned n) {
  _numberOfWords=n;
}

const unsigned* CrcVlinkEventData::data() const {
  return (&_numberOfWords)+1;
}

unsigned* CrcVlinkEventData::data() {
  return (&_numberOfWords)+1;
}

// Structure dependent

unsigned CrcVlinkEventData::bufferNumber() const {
  if(_numberOfWords==0) return 0;
  return *(data()+0);
}

void CrcVlinkEventData::bufferNumber(unsigned n) {
  *(data()+0)=n;
}

const CrcVlinkHeader* CrcVlinkEventData::header() const {
  if(_numberOfWords<=1) return 0;
  return (const CrcVlinkHeader*)(data()+1);
}

CrcVlinkHeader* CrcVlinkEventData::header() {
  return (CrcVlinkHeader*)(data()+1);
}

const CrcVlinkFeHeader* CrcVlinkEventData::feHeader(unsigned f) const {
  assert(f<8);
  if(_numberOfWords<=(6+4*f)) return 0;
  return (const CrcVlinkFeHeader*)(data()+3+4*f);
}

CrcVlinkFeHeader* CrcVlinkEventData::feHeader(unsigned f) {
  assert(f<8);
  if(_numberOfWords<=(6+4*f)) return 0;
  return (CrcVlinkFeHeader*)(data()+3+4*f);
}

const CrcVlinkFeData* CrcVlinkEventData::feData(unsigned f) const {
  assert(f<=8); // Allow 8 for trailer!

  const CrcVlinkFeHeader *h(feHeader(f));
  if(h==0) return 0;
  if(h->feWords()==0) return 0;

  if(_numberOfWords<=35) return 0;
  unsigned n(35);

  for(unsigned i(0);i<f;i++) {
    const CrcVlinkFeHeader *h(feHeader(i));
    if(h==0) return 0;

    n+=h->feWords();
    if((h->feWords()%2)==1) n++; // Even number of FE words always???
    if(_numberOfWords<=n) return 0;
  }

  return (const CrcVlinkFeData*)(data()+n);
}

CrcVlinkFeData* CrcVlinkEventData::feData(unsigned f) {
  assert(f<=8); // Allow 8 for trailer!

  CrcVlinkFeHeader *h(feHeader(f));
  if(h==0) return 0;
  if(h->feWords()==0) return 0;

  if(_numberOfWords<=35) return 0;
  unsigned n(35);

  for(unsigned i(0);i<f;i++) {
    const CrcVlinkFeHeader *h(feHeader(i));
    if(h==0) return 0;

    n+=h->feWords();
    if((h->feWords()%2)==1) n++; // Even number of FE words always???
    if(_numberOfWords<=n) return 0;
  }

  return (CrcVlinkFeData*)(data()+n);
}

const CrcVlinkTrgData* CrcVlinkEventData::trgData() const {
  const CrcVlinkFeHeader *h(feHeader(0));
  if(h==0) return 0;
  if(h->feWords()==0) return 0;
  if(!h->trgData()) return 0;

  if(_numberOfWords<=35) return 0;
  return (const CrcVlinkTrgData*)(data()+35);
}

CrcVlinkTrgData* CrcVlinkEventData::trgData() {
  CrcVlinkFeHeader *h(feHeader(0));
  if(h==0) return 0;
  if(h->feWords()==0) return 0;
  if(!h->trgData()) return 0;

  if(_numberOfWords<=35) return 0;
  return (CrcVlinkTrgData*)(data()+35);
}

unsigned CrcVlinkEventData::feNumberOfAdcSamples(unsigned f) const {
  assert(f<8);
  const CrcVlinkFeHeader *h(feHeader(f));
  if(h==0) return 0;
  return h->feLength()/(sizeof(CrcVlinkAdcSample));
}

/*
  const CrcVlinkAdcSample* feAdcSample(unsigned f, unsigned s) const {
  assert(f<8);
  const CrcVlinkFeData* feData(unsigned f) const {
  }
*/

const CrcVlinkTrailer* CrcVlinkEventData::trailer() const {
  // Prone to failure if data corrupted
  //return (const CrcVlinkTrailer*)(feData(8));

  return (const CrcVlinkTrailer*)(data()+_numberOfWords-3);
}

CrcVlinkTrailer* CrcVlinkEventData::trailer() {
  // Prone to failure if data corrupted
  //return (CrcVlinkTrailer*)(feData(8));

  return (CrcVlinkTrailer*)(data()+_numberOfWords-3);
}

unsigned CrcVlinkEventData::totalLength() const {
  // Prone to failure if data corrupted
  //const CrcVlinkTrailer* t(trailer());
  //if(t==0) return 0;
  //return *((unsigned*)(t+1));

  if(_numberOfWords==0) return 0;
  return *(data()+_numberOfWords-1);
}

void CrcVlinkEventData::totalLength(unsigned n) {
  // Prone to failure if data corrupted
  //const CrcVlinkTrailer* t(trailer());
  //if(t==0) return;
  //*((unsigned*)(t+1))=n;

  if(_numberOfWords==0) return;
  *(data()+_numberOfWords-1)=n;
}


unsigned CrcVlinkEventData::numberOfSamples(unsigned f) const {
  assert(f<8);
  const unsigned *h((const unsigned*)feHeader(f));
  if(h==0) return 0;
  UtlPack pack(h[2]);
  return (pack.halfWord(1)-4)/(4*7);
}

int CrcVlinkEventData::adc(unsigned f, unsigned chip, unsigned chan) const {
  if(chip>=numberOfSamples(f)) return 0;

  const unsigned *p((const unsigned *)feData(f));

  p+=2;

  p+=7*chan;
  const short int *d((const short int*)p);
  return *(d+chip);
}

/*
  bool CrcVlinkEventData::operator!=(const CrcVlinkEventData &c) {
  if(_control!=c._control) return true;

  const unsigned *a(data());
  const unsigned *b(c.data());
  for(unsigned i(0);i<numberOfWords();i++) {
  if(a[i]!=b[i]) return true;
  }

  return false;
  }

  bool CrcVlinkEventData::operator==(const CrcVlinkEventData &c) {
  return !operator!=(c);
  }
*/

bool CrcVlinkEventData::verify(bool p) const {
  if(_numberOfWords==0) return true;

  const CrcVlinkHeader *h(header());
  if(h==0) {
    if(p) std::cout << "CrcVlinkEventData::verify() "
		    << "header not present" << std::endl;
    return false;
  }
  
  if(!h->verify()) {
    if(p) std::cout << "CrcVlinkEventData::verify() "
		    << "header not verified" << std::endl;
    return false;
  }
  
  unsigned nw(38);
  
  for(unsigned f(0);f<8;f++) {
    const CrcVlinkFeHeader *fh(feHeader(f));
    if(fh==0)  {
      if(p) std::cout << "CrcVlinkEventData::verify() "
		      << "FE" << f << " header not present" << std::endl;
      return false;
    }
    
    if(!fh->verify())  {
      if(p) std::cout << "CrcVlinkEventData::verify() "
		      << "FE" << f << " header not verified" << std::endl;
      return false;
    }
    
    if(f>0 && fh->beStatus()!=0) {
      if(p) std::cout << "CrcVlinkEventData::verify() "
		      << "FE" << f << " BE status = " << fh->beStatus() << std::endl;
      return false;
    }
    
    // ADC data
    if(!fh->trgData()) {
      const CrcVlinkFeData *fd(feData(f));
      if(fd==0)  {
	if(fh->feLength()!=0)  {
	  if(p) std::cout << "CrcVlinkEventData::verify() "
			  << "FE" << f << " data not present but length = "
			  << fh->feLength() << std::endl;
	  return false;
	}
	
      } else {
	if((fh->feLength()%28)!=8) {
	  if(p) std::cout << "CrcVlinkEventData::verify() "
			  << "FE" << f << " length%28 = " << fh->feLength()%28 << std::endl;
	  return false;
	}
	
	nw+=fh->feLength()/4;
	nw+=((fh->feLength()/4)%2); // FE rounds up odd number of samples
	
	if(fd->fe()!=f)  {
	  if(p) std::cout << "CrcVlinkEventData::verify() "
			  << "FE" << f << " FE number = " << (unsigned)fd->fe() << std::endl;
	  return false;
	}
	
	if(fd->fifoStatus()!=0x10000)  {
	  if(p) std::cout << "CrcVlinkEventData::verify() "
			  << "FE" << f << " FIFO status = " << printHex(fd->fifoStatus()) << std::endl;
	  return false;
	}
	
	//if(fd->triggerCounter()!=bufferNumber()) return false;
	
	for(unsigned i(0);i<feNumberOfAdcSamples(f);i++) {
	  const CrcVlinkAdcSample *a(fd->adcSample(i));
	  if(a==0)  {
	    if(p) std::cout << "CrcVlinkEventData::verify() "
			    << "FE" << f << " ADC sample " << i << " not present" << std::endl;
	    return false;
	  }
	  
	  //if((a->flags()>>2)!=0)  {
	  if((a->flags()&0xfffefffc)!=0)  { // DON'T CHECK FOR BIT 16 AS IT IS FLAKEY - PDD 08/02/08
	    if(p) std::cout << "CrcVlinkEventData::verify() "
			    << "FE" << f << " ADC sample " << i << " flags = " << a->flags() << std::endl;
	    return false;
	  }
	}
      }

    // Trg history data
    } else {
      if(f!=0) {
	if(p) std::cout << "CrcVlinkEventData::verify() "
			<< "FE" << f << " has trigger data" << std::endl;
	return false;
      }

      const CrcVlinkTrgData *td(trgData());
      if(td==0) {
	if(fh->feLength()!=0)  {
	  if(p) std::cout << "CrcVlinkEventData::verify() "
			  << "FE" << f << " trg data not present but length = "
			  << fh->feLength() << std::endl;
	  return false;
	}
	
      } else {
	if(fh->feLength()!=1024) {
	  if(p) std::cout << "CrcVlinkEventData::verify() "
			  << "FE" << f << " trg length = " << fh->feLength() << std::endl;
	  return false;
	}
	
	nw+=fh->feLength()/4;
	nw+=((fh->feLength()/4)%2); // FE rounds up odd number of samples
      }
    }
  }
  
  const CrcVlinkTrailer *t(trailer());
  if(t==0) {
    if(p) std::cout << "CrcVlinkEventData::verify() "
		    << "trailer not present" << std::endl;
    return false;
  }
  
  if(!t->verify()) {
    if(p) std::cout << "CrcVlinkEventData::verify() "
		    << "trailer not verified" << std::endl;
    return false;
  }
  
  if(2*t->eventLength()+2!=_numberOfWords) {
    if(p) std::cout << "CrcVlinkEventData::verify() "
		    << "trailer length not verified" << std::endl;
    return false;
  }
  
  if(nw!=_numberOfWords) {
    if(p) std::cout << "CrcVlinkEventData::verify() "
		    << "Number of words counted = " << nw 
		    << ", reported = " << _numberOfWords << std::endl;
    return false;
  }
  
  return true;
}

std::ostream& CrcVlinkEventData::print(std::ostream &o, std::string s, bool raw) const {
  o << s << "CrcVlinkEventData::print()  Number of words = " 
    << numberOfWords() << std::endl;
  if(_numberOfWords==0) return o;

  if(raw) {
    const unsigned *d(data());
    for(unsigned i(0);i<_numberOfWords;i++) {
      o << s << " Data word " << std::setw(5) << i << " = " << printHex(d[i]) << std::endl;
    }
    return o;
  }

  o << s << " Buffer number = " << std::setw(10) << bufferNumber() << std::endl;

  const CrcVlinkHeader *h(header());
  if(h!=0) header()->print(o,s+" ");
  else o << s << " No header" << std::endl;

  for(unsigned f(0);f<8;f++) {
    const CrcVlinkFeHeader *fh(feHeader(f));
    if(fh==0) {
      o << s << " FE" << f << " No header" << std::endl;
    } else {
      o << s << " FE" << f << " Header" << std::endl;
      fh->print(o,s+" ");

      if(f==0 && fh->trgData()) {
	const CrcVlinkTrgData *fd(trgData());
	if(fd==0) {
	  o << s << " Trg No data" << std::endl;
	} else {
	  fd->print(o,s+" ");
	  o << s << " Number of trg data words = " << std::setw(3) << fh->feWords() << std::endl;
	  /*
	  if(fh->feWords()>0) o << s << "  Header          = " << printHex(fd->header()) << std::endl;
	  if(fh->feWords()>1) o << s << "  Trigger counter = " << std::setw(10) << fd->triggerCounter() << std::endl;
	  */
	  const unsigned *a(fd->data());
	  for(int i(0);i<fh->feWords()-2;i++) {
	    o << s << "  Word " << std::setw(3) << i
	      << " = " << printHex(a[i]) << std::endl;
	  }
	  if(fh->feWords()>0) o << s << "  Trailer         = " << printHex(fd->trailer(fh->feWords())) << std::endl;
	}

      } else {
	const CrcVlinkFeData *fd(feData(f));
	if(fd==0) {
	  o << s << " FE" << f << " No data" << std::endl;
	} else {
	  fd->print(o,s+" ");
	  o << s << " Number of ADC samples = " << std::setw(3) << feNumberOfAdcSamples(f) << std::endl;
	  for(unsigned i(0);i<feNumberOfAdcSamples(f);i++) {
	    o << s << "  Sample " << std::setw(3) << i << std::endl;
	    const CrcVlinkAdcSample *a(fd->adcSample(i));
	    if(a!=0) a->print(o,s+"  ");
	  }
	}
      }
    }
  }

  const CrcVlinkTrailer *t(trailer());
  if(t!=0) trailer()->print(o,s+" ");
  else o << s << " No trailer" << std::endl;

  o << s << " Total length = " << std::setw(10) << totalLength() << " words" << std::endl << std::endl;

  return o;
}

#endif
#endif
