#ifndef HstCheckConfiguration_HH
#define HstCheckConfiguration_HH

#include <cassert>

//#include "TROOT.h"
#include "TMapFile.h"
#include "TH1F.h"
#include "TH2F.h"
#include "TProfile.h"
#include "TRandom.h"

#include "DaqRunStart.hh"

#include "RcdUserRO.hh"
#include "RcdWriterDmy.hh"
#include "RcdWriterAsc.hh"
#include "RcdWriterBin.hh"

#include "UtlAverage.hh"
#include "SubAccessor.hh"


class HstCheckConfiguration : public RcdUserRO {

public:
  HstCheckConfiguration() {
    for(unsigned c(0);c<2;c++) {
      for(unsigned i(0);i<=21;i++) {
	_configuration[c][i][0]=0;
	_configuration[c][i][1]=0;
      }
    }
  }

  virtual ~HstCheckConfiguration() {
  }

  bool record(const RcdRecord &r) {
    if(doPrint(r.recordType())) {
      std::cout << "HstCheckConfiguration::record()" << std::endl;
      r.RcdHeader::print(std::cout," ") << std::endl;
    }
 
    if(r.recordType()==RcdHeader::runStart) {

      SubAccessor accessor(r);
      std::vector<const CrcLocationData<CrcVmeRunData>* >
	v(accessor.access< CrcLocationData<CrcVmeRunData> >());
 
      for(unsigned i(0);i<v.size();i++) {

	// Get crate number
	unsigned crate(2);
	if(v[i]->crateNumber()==0xec) crate=0;
	if(v[i]->crateNumber()==0xce) crate=0;
	if(v[i]->crateNumber()==0xac) crate=1;
	if(crate<2) {

	  // Get slot number
	  if(v[i]->slotNumber()<22) {
	    unsigned slot(v[i]->slotNumber());

	    if(_configuration[crate][slot][0]==0 &&
	       _configuration[crate][slot][1]==0) {

	      std::ostringstream label;
	      label << "HstCheckConfigurationCrate" << crate << "Slot";
	      if(slot<10) label << "0";
	      label << slot;
	      
	      std::ostringstream title;
	      if(crate==0) title << "ECAL Crate, ";
	      if(crate==1) title << "AHCAL Crate, ";
	      title << " Slot " << std::setw(2) << slot;

	      std::cout << title.str() << std::endl;
	      
	      _configuration[crate][slot][0]=new 
		TH1F((label.str()+"Total").c_str(),
		     (title.str()+", Total number of configuration checks").c_str(),
		     36,0,36);
	      
	      _configuration[crate][slot][1]=new 
		TH1F((label.str()+"Error").c_str(),
		     (title.str()+", Number of configuration check errors").c_str(),
		     36,0,36);
	    }
	  }
	}
      }

      return true;
    }


    if(r.recordType()==RcdHeader::configurationStart) {
      
      SubAccessor accessor(r);

      {std::vector<const CrcLocationData<CrcBeTrgConfigurationData>* >
	v(accessor.access< CrcLocationData<CrcBeTrgConfigurationData> >());

      for(unsigned i(0);i<v.size();i++) {
	assert(v[i]->crcComponent()==CrcLocation::beTrg);

	// Check for write data
	if(v[i]->label()==1) {
	
	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xce) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    
	    // Get slot broadcast
	    if(v[i]->slotBroadcast()) {
	      for(unsigned slot(0);slot<=21;slot++) {
		_beTrgConfigurationData[crate][slot]=*(v[i]->data());
	      }
	    }
	  }
	}
      }

      for(unsigned i(0);i<v.size();i++) {
	assert(v[i]->crcComponent()==CrcLocation::beTrg);

	// Check for write data
	if(v[i]->label()==1) {

	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xce) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    

	    // Get slot number
	    if(v[i]->slotNumber()<22) {
	      unsigned slot(v[i]->slotNumber());
	      _beTrgConfigurationData[crate][slot]=*(v[i]->data());
	    }
	  }
	}
      }}

      {std::vector<const CrcLocationData<CrcBeConfigurationData>* >
	v(accessor.access< CrcLocationData<CrcBeConfigurationData> >());

      for(unsigned i(0);i<v.size();i++) {
	assert(v[i]->crcComponent()==CrcLocation::be);

	// Check for write data
	if(v[i]->label()==1) {
	
	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xce) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    
	    // Get slot broadcast
	    if(v[i]->slotBroadcast()) {
	      for(unsigned slot(0);slot<=21;slot++) {
		_beConfigurationData[crate][slot]=*(v[i]->data());
	      }
	    }
	  }
	}
      }

      for(unsigned i(0);i<v.size();i++) {
	assert(v[i]->crcComponent()==CrcLocation::be);

	// Check for write data
	if(v[i]->label()==1) {

	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xce) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    

	    // Get slot number
	    if(v[i]->slotNumber()<22) {
	      unsigned slot(v[i]->slotNumber());
	      _beConfigurationData[crate][slot]=*(v[i]->data());
	    }
	  }
	}
      }}

      {std::vector<const CrcLocationData<CrcFeConfigurationData>* >
	v(accessor.access< CrcLocationData<CrcFeConfigurationData> >());

      for(unsigned i(0);i<v.size();i++) {
	assert(v[i]->crcComponent()<=CrcLocation::fe7 || v[i]->crcComponent()<=CrcLocation::feBroadcast);

	// Check for write data
	if(v[i]->label()==1) {
	
	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xce) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    
	    // Get slot and FE broadcast
	    if(v[i]->slotBroadcast() && v[i]->crcComponent()==CrcLocation::feBroadcast) {
	      for(unsigned slot(0);slot<=21;slot++) {
		for(unsigned fe(0);fe<8;fe++) {
		  _feConfigurationData[crate][slot][fe]=*(v[i]->data());
		}
	      }
	    }
	  }
	}
      }

      for(unsigned i(0);i<v.size();i++) {
	assert(v[i]->crcComponent()<=CrcLocation::fe7 || v[i]->crcComponent()<=CrcLocation::feBroadcast);

	// Check for write data
	if(v[i]->label()==1) {
	
	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xce) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    
	    // Get slot broadcast and separate FEs
	    if(v[i]->slotBroadcast() && v[i]->crcComponent()!=CrcLocation::feBroadcast) {
	      for(unsigned slot(0);slot<=21;slot++) {
		_feConfigurationData[crate][slot][v[i]->crcComponent()]=*(v[i]->data());
	      }
	    }
	  }
	}
      }

      for(unsigned i(0);i<v.size();i++) {
	assert(v[i]->crcComponent()<=CrcLocation::fe7 || v[i]->crcComponent()<=CrcLocation::feBroadcast);

	// Check for write data
	if(v[i]->label()==1) {
	
	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xce) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    
	    // Get separate slot and FE broadcast
	    if(v[i]->slotNumber()<22) {
	      unsigned slot(v[i]->slotNumber());
	      if(v[i]->crcComponent()==CrcLocation::feBroadcast) {
		for(unsigned fe(0);fe<8;fe++) {
		  _feConfigurationData[crate][slot][fe]=*(v[i]->data());
		}
	      }
	    }
	  }
	}
      }

      for(unsigned i(0);i<v.size();i++) {
	assert(v[i]->crcComponent()<=CrcLocation::fe7 || v[i]->crcComponent()<=CrcLocation::feBroadcast);

	// Check for write data
	if(v[i]->label()==1) {

	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xce) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    

	    // Get slot number
	    if(v[i]->slotNumber()<22) {
	      unsigned slot(v[i]->slotNumber());
	      _feConfigurationData[crate][slot][v[i]->crcComponent()]=*(v[i]->data());
	    }
	  }
	}
      }}
    }

    if(r.recordType()==RcdHeader::configurationStart ||
       r.recordType()==RcdHeader::configurationEnd ||
       r.recordType()==RcdHeader::runEnd) {

      unsigned offset(0);
      if(r.recordType()==RcdHeader::configurationEnd) offset=18;

      SubAccessor accessor(r);

      {std::vector<const CrcLocationData<CrcBeTrgConfigurationData>* >
	 v(accessor.access< CrcLocationData<CrcBeTrgConfigurationData> >());
      
      for(unsigned i(0);i<v.size();i++) {
	assert(v[i]->crcComponent()==CrcLocation::beTrg);
	
	// Check for read data
	if(v[i]->label()==0) {

	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xce) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    
	    // Get slot number
	    if(v[i]->slotNumber()<22) {
	      unsigned slot(v[i]->slotNumber());
	      
	      // Compare data
	      if(_configuration[crate][slot][0]!=0) {
		_configuration[crate][slot][0]->Fill(0+offset);
	      }

	      if(_configuration[crate][slot][1]!=0) {
		if(_beTrgConfigurationData[crate][slot]!=*(v[i]->data())) {
		  if(doPrint(r.recordType(),1)) {
		    v[i]->print(std::cout," Read ") << std::endl;
		    _beTrgConfigurationData[crate][slot].print(std::cout," Write ") << std::endl;
		  }
		  _configuration[crate][slot][1]->Fill(0+offset);
		}
	      }
	    }
	  }
	}
      }}

      {std::vector<const CrcLocationData<CrcBeConfigurationData>* >
	v(accessor.access< CrcLocationData<CrcBeConfigurationData> >());

      for(unsigned i(0);i<v.size();i++) {
	assert(v[i]->crcComponent()==CrcLocation::be);

	// Check for read data
	if(v[i]->label()==0) {

	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xce) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    
	    // Get slot number
	    if(v[i]->slotNumber()<22) {
	      unsigned slot(v[i]->slotNumber());
	      
	      // Compare data
	      if(_configuration[crate][slot][0]!=0) {
		_configuration[crate][slot][0]->Fill(1+offset);
	      }
	      if(_configuration[crate][slot][1]!=0) {
		if(_beConfigurationData[crate][slot]!=*(v[i]->data())) {
		  if(doPrint(r.recordType(),1)) {
		    v[i]->print(std::cout," Read  ") << std::endl;
		    _beConfigurationData[crate][slot].print(std::cout," Write ") << std::endl;
		  }
		  _configuration[crate][slot][1]->Fill(1+offset);
		}
	      }
	    }
	  }
	}
      }}

      {std::vector<const CrcLocationData<CrcFeConfigurationData>* >
	v(accessor.access< CrcLocationData<CrcFeConfigurationData> >());

      for(unsigned i(0);i<v.size();i++) {
	assert(v[i]->crcComponent()<=CrcLocation::fe7 || v[i]->crcComponent()<=CrcLocation::feBroadcast);

	// Check for read data
	if(v[i]->label()==0) {

	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xce) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    
	    // Get slot number
	    if(v[i]->slotNumber()<22) {
	      unsigned slot(v[i]->slotNumber());
	      
	      // Get FE number
	      unsigned fe(v[i]->crcComponent());

	      // Compare data
	      if(_configuration[crate][slot][0]!=0) {
		_configuration[crate][slot][0]->Fill(2+fe+offset);
	      }
	      if(_configuration[crate][slot][1]!=0) {
		if(_feConfigurationData[crate][slot][fe]!=*(v[i]->data())) {
		  if(doPrint(r.recordType(),2)) {
		    v[i]->print(std::cout," Read  ") << std::endl;
		    _feConfigurationData[crate][slot][fe].print(std::cout," Write ") << std::endl;
		  }
		  _configuration[crate][slot][1]->Fill(2+fe+offset);
		}
	      }
	    }
	  }
	}
      }}

      {std::vector<const CrcLocationData<AhcVfeConfigurationData>* >
	v(accessor.access< CrcLocationData<AhcVfeConfigurationData> >());

      for(unsigned i(0);i<v.size();i++) {
	assert(v[i]->crcComponent()<=CrcLocation::fe7 || v[i]->crcComponent()<=CrcLocation::feBroadcast);

	// Check for read data
	if(v[i]->label()==2) {

	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xce) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    
	    // Get slot number
	    if(v[i]->slotNumber()<22) {
	      unsigned slot(v[i]->slotNumber());
	      
	      // Get FE number
	      unsigned fe(v[i]->crcComponent());

	      // Compare data
	      if(_configuration[crate][slot][0]!=0) {
		_configuration[crate][slot][0]->Fill(10+fe+offset);
	      }
	      if(_configuration[crate][slot][1]!=0) {
		if(_vfeConfigurationData[crate][slot][fe]!=*(v[i]->data())) {
		  if(doPrint(r.recordType(),2)) {
		    v[i]->print(std::cout," Read  ") << std::endl;
		    _vfeConfigurationData[crate][slot][fe].print(std::cout," Write ") << std::endl;
		  }
		  _configuration[crate][slot][1]->Fill(10+fe+offset);
		}
	      }
	    }
	  }
	}
      }}
    }

    // Must save values for AHC VFE after check, not before
    if(r.recordType()==RcdHeader::configurationStart ||
       r.recordType()==RcdHeader::runEnd) {
      
      SubAccessor accessor(r);

      {std::vector<const CrcLocationData<AhcVfeConfigurationData>* >
	v(accessor.access< CrcLocationData<AhcVfeConfigurationData> >());

      for(unsigned i(0);i<v.size();i++) {
	assert(v[i]->crcComponent()<=CrcLocation::fe7 || v[i]->crcComponent()<=CrcLocation::feBroadcast);

	// Check for write data
	if(v[i]->label()==1) {
	
	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xce) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    
	    // Get slot and FE broadcast
	    if(v[i]->slotBroadcast() && v[i]->crcComponent()==CrcLocation::feBroadcast) {
	      for(unsigned slot(0);slot<=21;slot++) {
		for(unsigned fe(0);fe<8;fe++) {
		  _vfeConfigurationData[crate][slot][fe]=*(v[i]->data());
		}
	      }
	    }
	  }
	}
      }

      for(unsigned i(0);i<v.size();i++) {
	assert(v[i]->crcComponent()<=CrcLocation::fe7 || v[i]->crcComponent()<=CrcLocation::feBroadcast);

	// Check for write data
	if(v[i]->label()==1) {
	
	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xce) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    
	    // Get slot broadcast and separate FEs
	    if(v[i]->slotBroadcast() && v[i]->crcComponent()!=CrcLocation::feBroadcast) {
	      for(unsigned slot(0);slot<=21;slot++) {
		_vfeConfigurationData[crate][slot][v[i]->crcComponent()]=*(v[i]->data());
	      }
	    }
	  }
	}
      }

      for(unsigned i(0);i<v.size();i++) {
	assert(v[i]->crcComponent()<=CrcLocation::fe7 || v[i]->crcComponent()<=CrcLocation::feBroadcast);

	// Check for write data
	if(v[i]->label()==1) {
	
	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xce) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    
	    // Get separate slot and FE broadcast
	    if(v[i]->slotNumber()<22) {
	      unsigned slot(v[i]->slotNumber());
	      if(v[i]->crcComponent()==CrcLocation::feBroadcast) {
		for(unsigned fe(0);fe<8;fe++) {
		  _vfeConfigurationData[crate][slot][fe]=*(v[i]->data());
		}
	      }
	    }
	  }
	}
      }

      for(unsigned i(0);i<v.size();i++) {
	assert(v[i]->crcComponent()<=CrcLocation::fe7 || v[i]->crcComponent()<=CrcLocation::feBroadcast);

	// Check for write data
	if(v[i]->label()==1) {

	  // Get crate number
	  unsigned crate(2);
	  if(v[i]->crateNumber()==0xec) crate=0;
	  if(v[i]->crateNumber()==0xce) crate=0;
	  if(v[i]->crateNumber()==0xac) crate=1;
	  if(crate<2) {
	    

	    // Get slot number
	    if(v[i]->slotNumber()<22) {
	      unsigned slot(v[i]->slotNumber());
	      _vfeConfigurationData[crate][slot][v[i]->crcComponent()]=*(v[i]->data());
	    }
	  }
	}
      }}
    }

    /*
    if(r.recordType()==RcdHeader::event) {
      //r.RcdHeader::print(std::cout);

      SubAccessor accessor(r);
      std::vector<const CrcLocationData<CrcVlinkEventData>* >
	v(accessor.access< CrcLocationData<CrcVlinkEventData> >());

      for(unsigned i(0);i<v.size();i++) {

	// Get crate number
	unsigned crate(2);
	if(v[i]->crateNumber()==0xec) crate=0;
	if(v[i]->crateNumber()==0xce) crate=0;
	if(v[i]->crateNumber()==0xac) crate=1;
	if(crate<2) {

	  // Get slot number
	  if(v[i]->slotNumber()<22) {
	    unsigned slot(v[i]->slotNumber());
	    if(_number[crate][slot]!=0 &&
	       _pedestal[crate][slot]!=0 &&
	       _noise[crate][slot]!=0) {

	      // Loop over FE number
	      for(unsigned fe(0);fe<8;fe++) {
		const CrcVlinkFeData *fd(v[i]->data()->feData(fe));
		if(fd!=0) {

		  // Loop over mplex channel number
		  for(unsigned chan(0);chan<v[i]->data()->feNumberOfAdcSamples(fe);chan++) {
		    const CrcVlinkAdcSample *as(fd->adcSample(chan));
		    if(as!=0) {

		      // Loop over chips=ADCs
		      for(unsigned chip(0);chip<12;chip++) {
			_average[crate][slot][fe][chip]+=as->adc(chip);
		      }
		    }
		  }
		}
	      }
	    }
	  }
	}
      }
    }
    */

    return true;
  }

private:
  TH1F *_configuration[2][22][2];
  CrcBeTrgConfigurationData _beTrgConfigurationData[2][22];
  CrcBeConfigurationData _beConfigurationData[2][22];
  CrcFeConfigurationData _feConfigurationData[2][22][8];
  AhcVfeConfigurationData _vfeConfigurationData[2][22][8];
};

#endif
