#ifndef MpsSensor1BunchTrainData_HH
#define MpsSensor1BunchTrainData_HH

#include <iostream>
#include <fstream>

#include "MpsSensor1Hit.hh"
#include "MpsSensor1BunchTrainDatum.hh"


class MpsSensor1BunchTrainData {

public:
  enum {
    versionNumber=0
  };

  MpsSensor1BunchTrainData();

  unsigned finalBits() const;
  void     finalBits(unsigned n);

  unsigned totalNumberOfHits() const;
  unsigned numberOfRegionHits(unsigned g) const;
  void     numberOfRegionHits(unsigned g, unsigned n);

  const MpsSensor1BunchTrainDatum* data() const;
  const MpsSensor1BunchTrainDatum* regionData(unsigned r) const;
  unsigned* daqData();

  std::vector<MpsSensor1Hit> hitVector() const;
  std::vector<MpsSensor1Hit> hitVector(unsigned x, unsigned y) const;
  void                       hitVector(const std::vector<MpsSensor1Hit> &v);

  std::vector<MpsSensor1Hit> fullVector() const;

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


private:
  UtlPack _finalBits;
  unsigned _totalNumberOfHits;
  UtlPack _numberOfRegionHits[2];
};


#ifdef CALICE_DAQ_ICC

#include <cstring>

#include "UtlPrintHex.hh"


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

unsigned MpsSensor1BunchTrainData::finalBits() const {
  return _finalBits.word();
}

void MpsSensor1BunchTrainData::finalBits(unsigned n) {
  _finalBits=n;
}

unsigned MpsSensor1BunchTrainData::totalNumberOfHits() const {
  return _totalNumberOfHits;
}

unsigned MpsSensor1BunchTrainData::numberOfRegionHits(unsigned g) const {
  assert(g<4);
  return _numberOfRegionHits[g/2].bits(0+16*(g%2),11+16*(g%2));
}

void MpsSensor1BunchTrainData::numberOfRegionHits(unsigned g, unsigned n) {
  assert(g<4);
  assert(n<4096);
  _numberOfRegionHits[g/2].bits(0+16*(g%2),11+16*(g%2),n);
}

std::vector<MpsSensor1Hit> MpsSensor1BunchTrainData::hitVector() const {
  std::vector<MpsSensor1Hit> v(6*totalNumberOfHits());
  unsigned nHit(0);

  const MpsSensor1BunchTrainDatum *p(data());
  for(unsigned region(0);region<4;region++) {
    for(unsigned i(0);i<numberOfRegionHits(region);i++) {
      if(p->row()<168 && p->group()<7) {

	for(unsigned c(0);c<6;c++) {
	  if(p->channel(c)) {
	    /*
	      MpsSensor1Hit hit;
	      hit.pixelX(c+6*p->group()+42*region);
	      hit.pixelY(p->row());
	      hit.timeStamp(p->timeStamp());
	      v.push_back(hit);
	    */
	    
	    v[nHit].pixelX(c+6*p->group()+42*region);
	    v[nHit].pixelY(p->row());
	    v[nHit].timeStamp(p->timeStamp());
	    nHit++;
	  }
	}
      }

      p++;
    }
  }

  v.resize(nHit);
  return v;
}

std::vector<MpsSensor1Hit> MpsSensor1BunchTrainData::hitVector(unsigned x, unsigned y) const {
  std::vector<MpsSensor1Hit> v(1000);
  unsigned nHit(0);

  const MpsSensor1BunchTrainDatum *p(data());
  for(unsigned region(0);region<4;region++) {
    if(region!=(x/42)) p+=numberOfRegionHits(region);
    else {
      for(unsigned i(0);i<numberOfRegionHits(region);i++) {
	unsigned c(x%6);
	if(p->channel(c) && p->group()==((x%42)/6) && p->row()==y) {
	  /*
	    MpsSensor1Hit hit;
	    hit.pixelX(c+6*p->group()+42*region);
	    hit.pixelY(p->row());
	    hit.timeStamp(p->timeStamp());
	    v.push_back(hit);
	  */
	  v[nHit].pixelX(c+6*p->group()+42*region);
	  v[nHit].pixelY(p->row());
	  v[nHit].timeStamp(p->timeStamp());
	  nHit++;
	}
	
	p++;
      }
    }
  }

  v.resize(nHit);
  return v;
}

void MpsSensor1BunchTrainData::hitVector(const std::vector<MpsSensor1Hit> &v) {
  unsigned short nh[3];
  nh[0]=0;
  nh[1]=0;
  nh[2]=0;
  nh[3]=0;

  MpsSensor1BunchTrainDatum *p((MpsSensor1BunchTrainDatum*)(this+1));
  for(unsigned i(0);i<v.size();i++) {
    unsigned region(v[i].pixelX()/42);
    nh[region]++;

    p[i].group(((v[i].pixelX())%42)/6);
    p[i].channel(((v[i].pixelX())%42)%6,true);
    p[i].row(v[i].pixelY());
    p[i].timeStamp(v[i].timeStamp());
  }

  numberOfRegionHits(0,nh[0]);
  numberOfRegionHits(1,nh[1]);
  numberOfRegionHits(2,nh[2]);
  numberOfRegionHits(3,nh[3]);
  _totalNumberOfHits=nh[0]+nh[1]+nh[2]+nh[3];

  assert(v.size()==totalNumberOfHits());
}

std::vector<MpsSensor1Hit> MpsSensor1BunchTrainData::fullVector() const {
  std::vector<MpsSensor1Hit> v;

  const MpsSensor1BunchTrainDatum *p(data());
  for(unsigned region(0);region<4;region++) {
    unsigned groupCount[168][7];
    memset(groupCount[0],0,168*7*sizeof(unsigned));

    for(unsigned i(0);i<numberOfRegionHits(region);i++) {
      groupCount[p->row()][p->group()]++;
      if(groupCount[p->row()][p->group()]>=19) {
	for(unsigned c(0);c<6;c++) {
	  MpsSensor1Hit hit;
	  hit.pixelX(c+6*p->group()+42*region);
	  hit.pixelY(p->row());
	  hit.timeStamp(p->timeStamp()+1);
	  v.push_back(hit);
	}
      }

      p++;
    }
  }
  return v;
}

const MpsSensor1BunchTrainDatum* MpsSensor1BunchTrainData::data() const {
  return (const MpsSensor1BunchTrainDatum*)(this+1);
}

const MpsSensor1BunchTrainDatum* MpsSensor1BunchTrainData::regionData(unsigned r) const {
  assert(r<4);

  const MpsSensor1BunchTrainDatum *p(data());
  for(unsigned i(0);i<r;i++) p+=numberOfRegionHits(i);
  return p;
}

unsigned* MpsSensor1BunchTrainData::daqData() {
  return &_totalNumberOfHits;
}

std::ostream& MpsSensor1BunchTrainData::print(std::ostream &o, std::string s) const {
  o << s << "MpsSensor1BunchTrainData::print()" << std::endl;
  o << s << " Final bit status = " << printHex(_finalBits) << std::endl;
  o << s << " Total number of hits = " << totalNumberOfHits() << std::endl;
  o << s << " Region 01 hit header = " << printHex(_numberOfRegionHits[0]) << std::endl;
  o << s << " Region 23 hit header = " << printHex(_numberOfRegionHits[1]) << std::endl;

  for(unsigned i(0);i<4;i++) {
    o << s << " Number of hits in region " << i << " = "
      << numberOfRegionHits(i) << std::endl;
  }

  /*  
  if(numberOfRegionHits()>0) {
    for(unsigned i(0);i<4;i++) {
      o << s << " Column " << i << " , number of hits = "
	<< numberOfColumnHits(i) << std::endl;
      
      const unsigned *p((unsigned*)columnData(i));
      for(unsigned j(0);j<numberOfColumnHits(i);j++) {
      o << s << "  Hit " << std::setw(3) << " = "
	<< printHex(p[i]) << std::endl;
      }
    }
  }
  */

  /*
  const MpsSensor1BunchTrainDatum *p(data());
  for(unsigned i(0);i<19 && i<totalNumberOfHits();i++) {
    o << s << " Hit " << std::setw(4) << i << " datum = "
      << printHex(_numberOfRegionHits[i+2]) << std::endl;
    p[i].print(o,s+"  ");
  }
  */

  /*
  for(unsigned region(0);region<4;region++) {
    const MpsSensor1BunchTrainDatum *p(regionData(region));
    for(unsigned i(0);i<19 && i<numberOfRegionHits(region);i++) {
      o << s << " Region " << region << ", hit " << std::setw(4) << i << " datum = "
	<< printHex(*((unsigned*)(p+i))) << std::endl;
      p[i].print(o,s+"  ");
    }
  }
*/

  for(unsigned region(0);region<1;region++) {
    const MpsSensor1BunchTrainDatum *p(regionData(region));
    for(unsigned i(0);i<numberOfRegionHits(region);i++) {
      if(p[i].row()==0) {
	o << s << " Region " << region << ", hit " << std::setw(4) << i << " datum = "
	  << printHex(*((unsigned*)(p+i))) << std::endl;
	p[i].print(o,s+"  ");
      }
    }
  }


  /*
  const MpsSensor1Hit *p((const MpsSensor1Hit*)(this+1));
  for(unsigned i(0);i<16 && i<totalNumberOfHits();i++) {
    p[i].print(o,s+"  ");
  }
  */
  return o;
}

#endif
#endif
