#ifndef MpsSensorV10ConfigurationData_HH
#define MpsSensorV10ConfigurationData_HH

#include <iostream>
#include <fstream>

#include "UtlPack.hh"


class MpsSensorV10ConfigurationData {

public:
  enum {
    versionNumber=0
  };

  MpsSensorV10ConfigurationData();

  bool mask(unsigned x, unsigned y) const;
  void mask(unsigned x, unsigned y, bool b);
  void maskColumn(unsigned x, bool b);
  void maskRow(unsigned y, bool b);
  void maskRegion(unsigned r, bool b);
  void maskRegionRow(unsigned r, unsigned y, bool b);
  void maskQuadrant(unsigned q, bool b);
  void maskSensor(bool b);
  void maskInvert();

  unsigned trim(unsigned x, unsigned y) const;
  void     trim(unsigned x, unsigned y, unsigned t);
  void     trimRegion(unsigned r, unsigned t);
  void     trimQuadrant(unsigned q, unsigned t);
  void     trimSensor(unsigned t);

  unsigned stripCheckBits(unsigned s) const;
  void     stripCheckBits(unsigned s, unsigned b);
  void     stripCheckBitCounter();

  const unsigned char* data() const;
  void     stripCheckData(const unsigned char *p);
  void     maskTrimData(const unsigned char *p);

  unsigned numberOfStrips() const;
  unsigned numberOfBytes() const;
  unsigned numberOfMaskTrimBytes() const;

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

  bool readFile( const std::string &fileName);
  bool writeFile(const std::string &fileName) const;

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


private:
  // 168bits/8 = 21bytes + 3 bytes check = 24 bytes per strip
  // 5 bits/row * 168 rows + = 840 strips

  // In terms of x=0-167 and y=0-167 in the sensor
  // [6]x32bits is 191-x+24, [840] is 839-5*y
  // The 5 bits per pixel are: 0-3 threshold trim, 4 mask

  // A quadrant is 84x84 pixels
  // Quadrant 0 is x= 0- 83,y= 0- 83
  // Quadrant 1 is x= 0- 83,y=84-167
  // Quadrant 2 is x=84-167,y= 0- 83
  // Quadrant 3 is x=84-167,y=84-167

  UtlPack _data[840][6];
};


#ifdef CALICE_DAQ_ICC

#include <cstring>

#include "UtlPrintHex.hh"


MpsSensorV10ConfigurationData::MpsSensorV10ConfigurationData() {
  memset(this,0,sizeof(MpsSensorV10ConfigurationData));

  for(unsigned i(0);i<168;i++) {
    for(unsigned j(0);j<168;j++) {
      trim(i,j,7);
    }
  }

  maskQuadrant(0,false);
  maskQuadrant(1,false);
  maskQuadrant(2,false);
  maskQuadrant(3,false);

  for(unsigned i(0);i<840;i++) {
    stripCheckBits(i,0xaaaaaa);
  }

  for(unsigned i(0);i<840;i++) {
    for(unsigned j(0);j<6;j++) {
      _data[i][j]=8*i+j;
    }
  }
}

bool MpsSensorV10ConfigurationData::mask(unsigned x, unsigned y) const {
  
  // Check x and y are in the right range for the sensor
  assert(x<168 && y<168);

  // Return correct bit = 4
  unsigned strip(839-(5*y));
  unsigned bit(8*((191-x)/8)+7-((191-x)%8));

  //return _data[5*y+4][(x+24)/32].bit((x+24)%32);
  //return _data[839-(5*y+4)][(x+24)/32].bit(8*(((x+24)%32)/8)+7-(x+24)%8);
  return _data[strip][bit/32].bit(bit%32);
}

void MpsSensorV10ConfigurationData::mask(unsigned x, unsigned y, bool b) {
  
  // Check x and y are in the right range for the sensor
  assert(x<168);
  assert(y<168);
  
  // Set correct bit = 4
  unsigned strip(839-(5*y));
  unsigned bit(8*((191-x)/8)+7-((191-x)%8));

  _data[strip][bit/32].bit(bit%32,b);
  //_data[839-(5*y+4)][(x+24)/32].bit(8*(((x+24)%32)/8)+7-(x+24)%8,b);
}

void MpsSensorV10ConfigurationData::maskColumn(unsigned x, bool b) {

  // Check for correct x
  assert(x<168);

  // Loop over y
  for(unsigned y(0);y<168;y++) {
    mask(x,y,b);
  }
}

void MpsSensorV10ConfigurationData::maskRow(unsigned y, bool b) {

  // Check for correct y
  assert(y<168);

  // Loop over x
  for(unsigned x(0);x<168;x++) {
    mask(x,y,b);
  }
}

void MpsSensorV10ConfigurationData::maskRegion(unsigned r, bool b) {

  // Check for only four regions
  assert(r<4);

  // Loop over correct x and y ranges
  for(unsigned x(42*r);x<42*(r+1);x++) {
    for(unsigned y(0);y<168;y++) {
      mask(x,y,b);
    }
  }
}

void MpsSensorV10ConfigurationData::maskRegionRow(unsigned r, unsigned y, bool b) {

  // Check for only four regions and for row
  assert(r<4);
  assert(y<168);

  // Loop over correct x range
  for(unsigned x(42*r);x<42*(r+1);x++) {
    mask(x,y,b);
  }
}

void MpsSensorV10ConfigurationData::maskQuadrant(unsigned q, bool b) {

  // Check for only four quadrants
  assert(q<4);

  // Loop over correct x and y ranges
  for(unsigned x(84*(q/2));x<84*((q/2)+1);x++) {
    for(unsigned y(84*(q%2));y<84*((q%2)+1);y++) {
      mask(x,y,b);
    }
  }
}

void MpsSensorV10ConfigurationData::maskSensor(bool b) {
  for(unsigned x(0);x<168;x++) {
    for(unsigned y(0);y<168;y++) {
      mask(x,y,b);
    }
  }
}

void MpsSensorV10ConfigurationData::maskInvert() {
  for(unsigned x(0);x<168;x++) {
    for(unsigned y(0);y<168;y++) {
      mask(x,y,!mask(x,y));
    }
  }
}

unsigned MpsSensorV10ConfigurationData::trim(unsigned x, unsigned y) const {
  
  // Check x and y are in the right range for the sensor
  assert(x<168 && y<168);
  
  unsigned bit(8*((191-x)/8)+7-((191-x)%8));

  UtlPack result;
  for(unsigned i(0);i<4;i++) {

    // Get each of the 0-3 bits in turn and pack into the result
    unsigned strip(839-(5*y+4-i));
    result.bit(i,_data[strip][bit/32].bit(bit%32));
  }

  // Send back the completed value
  return result.word();
}

void MpsSensorV10ConfigurationData::trim(unsigned x, unsigned y, unsigned t) {

  // Check x, y and t are in the right range for the sensor
  assert(x<168 && y<168 && t<16);
  
  unsigned bit(8*((191-x)/8)+7-((191-x)%8));

  UtlPack u(t);
  for(unsigned i(0);i<4;i++) {

    // Get each of the 4 bits in turn and pack into the correct location
    unsigned strip(839-(5*y+4-i));
    _data[strip][bit/32].bit(bit%32,u.bit(i));
  }
}

void MpsSensorV10ConfigurationData::trimRegion(unsigned r, unsigned t) {

  // Check for only four regions
  assert(r<4);

  // Loop over correct x and y ranges
  for(unsigned x(42*r);x<42*(r+1);x++) {
    for(unsigned y(0);y<168;y++) {
      trim(x,y,t);
    }
  }
}

void MpsSensorV10ConfigurationData::trimQuadrant(unsigned q, unsigned t) {

  // Check for only four quadrants
  assert(q<4);

  // Loop over correct x and y ranges
  for(unsigned x(84*(q/2));x<84*((q/2)+1);x++) {
    for(unsigned y(84*(q%2));y<84*((q%2)+1);y++) {
      trim(x,y,t);
    }
  }
}

void MpsSensorV10ConfigurationData::trimSensor(unsigned t) {
  for(unsigned x(0);x<168;x++) {
    for(unsigned y(0);y<168;y++) {
      trim(x,y,t);
    }
  }
}

unsigned MpsSensorV10ConfigurationData::stripCheckBits(unsigned s) const {
  
  // Check s is in the right range for the sensor
  assert(s<840);

  // Get each of the 0-2 bytes in turn and pack into the result
  UtlPack result;
  for(unsigned i(0);i<3;i++) {
    result.byte(i,_data[s][0].byte(i));
  }

  // Send back the completed value
  return result.word();
}

void MpsSensorV10ConfigurationData::stripCheckBits(unsigned s, unsigned b) {
  
  // Check s and b are in the right range for the sensor
  assert(s<840);
  assert(b<0x01000000);
  
  // Get each of the 0-2 bytes in turn and pack into the result
  UtlPack bits(b);
  for(unsigned i(0);i<3;i++) {
    _data[s][0].byte(i,bits.byte(i));
  }
}

void MpsSensorV10ConfigurationData::stripCheckBitCounter() {
  for(unsigned i(0);i<168;i++) {
    for(unsigned j(0);j<5;j++) {
      unsigned k(5*i+j);
      unsigned kb(~k);
      stripCheckBits(5*i+j,((kb&0xfff)<<12)+(k&0xfff));
    }
  }
}

void MpsSensorV10ConfigurationData::stripCheckData(const unsigned char *p) {
  assert(p!=0);

  //p+=21;

  //for(unsigned r(0);r<9;r++) {
  //  std::cout << "stripCheckData() p[ " << r << "] = " << printHex(p[r]) << std::endl;
  // }

  for(unsigned r(0);r<840;r++) {
    for(unsigned i(0);i<3;i++) {
      _data[r][0].byte(i,p[3*r+i]);
    }
  }
}

void MpsSensorV10ConfigurationData::maskTrimData(const unsigned char *p) {
  assert(p!=0);

  //p+=21;

  //for(unsigned r(0);r<21*3;r++) {
  //  std::cout << "maskTrimData()   p[" << std::setw(2) << r << "] = " << printHex(p[r]) << std::endl;
  // }

  for(unsigned r(0);r<840;r++) {
    for(unsigned i(0);i<21;i++) {
      _data[r][(i+3)/4].byte((i+3)%4,p[21*r+i]);
    }
  }
}

const unsigned char* MpsSensorV10ConfigurationData::data() const {

  // Return pointer to beginning of all information
  return (const unsigned char*)(&_data[0][0]);
}

unsigned MpsSensorV10ConfigurationData::numberOfStrips() const {
  return 840;
}

unsigned MpsSensorV10ConfigurationData::numberOfBytes() const {
  return 24;
}

unsigned MpsSensorV10ConfigurationData::numberOfMaskTrimBytes() const {
  return 21;
}

bool MpsSensorV10ConfigurationData::operator!=(const MpsSensorV10ConfigurationData &d) const {
  for(unsigned i(0);i<840;i++) {
    for(unsigned j(0);j<6;j++) {
      if(_data[i][j]!=d._data[i][j]) return true;
    }  
  }
  return false;
}

bool MpsSensorV10ConfigurationData::operator==(const MpsSensorV10ConfigurationData &d) const {
  return !(this->operator!=(d));
}

bool MpsSensorV10ConfigurationData::writeFile(const std::string &fileName) const {
  std::ofstream fout(fileName.c_str());
  if(!fout) return false;

  for(unsigned row(0);row<168;row++) {
    for(unsigned bit(0);bit<5;bit++) {
      fout << std::setw(3) << row << " " << bit << std::hex << std::setfill('0');
      for(unsigned word(0);word<6;word++) {
	fout << " " << std::setw(8) << _data[5*row+bit][5-word].word();
      }
      fout << std::dec << std::setfill(' ') << std::endl;
    }
  }

  fout.close();
  return true;
}

bool MpsSensorV10ConfigurationData::readFile(const std::string &fileName) {
  std::ifstream fin(fileName.c_str());
  if(!fin) return false;

  unsigned r,b,w;
  for(unsigned row(0);row<168;row++) {
    for(unsigned bit(0);bit<5;bit++) {
      fin >> r >> b >> std::hex;
      if(!fin || r!=row || b!=bit) return false;

      for(unsigned word(0);word<6;word++) {
	fin >> w;
	_data[5*row+bit][5-word].word(w);
      }
      fin >> std::dec;
    }
  }

  fin.close();
  return true;
}

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

  for(unsigned q(0);q<4;q++) {
    o << s << " Quadrant = " << q 
      << ", pixels x = "
      << std::setw(2) << 84*(q/2) << "-"
      << std::setw(3) << 84*((q/2)+1)-1
      << ", y = "
      << std::setw(2) << 84*(q%2) << "-"
      << std::setw(3) << 84*((q%2)+1)-1
      << ", threshold trim/mask settings"  << std::endl << std::hex;

    for(unsigned y(84*(q%2));y<84*((q%2)+1);y++) {
      o << s << "  ";
      for(unsigned x(84*(q/2));x<84*((q/2)+1);x++) {
	if(mask(x,y)) o << ".";
	else          o << trim(x,y);
      }
      o << std::endl;
    }
    o << std::dec;
  }

  /*
  for(unsigned i(0);i<3;i++) {
    for(unsigned j(0);j<6;j++) {
      o << s << " Data[" << std::setw(3) << i << "][" << std::setw(1) << j
	<< "] = " << printHex(_data[i][j]) << std::endl;
    }
  }
  */

  o << std::endl << s << "Strip check data";
  for(unsigned i(0);i<840;i++) {
    if((i%8)==0) o << std::endl << s << "Rows " << std::dec
		   << std::setw(3) << i << "-" << std::setw(3) << i+7;
    o << " 0x" << std::hex << std::setw(6) << std::setfill('0') << stripCheckBits(i);
  }
  o << std::setfill(' ') << std::dec << std::endl;

  return o;
}

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

  unsigned n(0);
  for(unsigned x(0);x<168;x++) {
    for(unsigned y(0);y<168;y++) {
      if(mask(x,y)) {
	o << s << "Pixel " << std::setw(3) << x
	  << ", " << std::setw(3) << y << " masked" << std::endl;
	n++;
      }
    }
  }

  o << s << "Total number of masked pixels = " << n << std::endl;

  return o;
}

#endif
#endif
