#ifndef MpsAnalysisBase_HH
#define MpsAnalysisBase_HH

#include <cassert>

#include "TFile.h"
#include "TF1.h"
#include "TH1F.h"
#include "TH2F.h"
#include "TProfile.h"

#include "RcdUserRO.hh"
#include "SubAccessor.hh"


class MpsAnalysisBase : public RcdUserRO {

public:
  MpsAnalysisBase(const std::string &name) :
    _mpsAnalysisName(name), _validRunType(true), _rootFile(0) {

    for(unsigned x(0);x<168;x++) {
      for(unsigned y(0);y<168;y++) {
	std::ostringstream pLabel;
	pLabel << "X" << std::setw(3) << std::setfill('0') << x
	       << "Y" << std::setw(3) << std::setfill('0') << y;
	_pixelLabel[x][y]=pLabel.str();

	std::ostringstream pTitle;
	pTitle << "X " << x << ", Y " << y;
	_pixelTitle[x][y]=pTitle.str();
      }
    }
  }

  virtual ~MpsAnalysisBase() {

    // Close ROOT file when runEnd was not seen
    endRoot();
  }

  const std::string& mpsAnalysisName() const {
    return _mpsAnalysisName;
  }

  virtual bool mpsAnalysisValidRun(IlcRunType::Type t) const {
    return false;
  }

  virtual bool runStart(const RcdRecord &r) {
    return true;
  }

  virtual bool runEnd(const RcdRecord &r) {
    return true;
  }

  virtual bool configurationStart(const RcdRecord &r) {
    return true;
  }

  virtual bool configurationEnd(const RcdRecord &r) {
    return true;
  }

  virtual bool slowReadout(const RcdRecord &r) {
    return true;
  }

  virtual bool bunchTrain(const RcdRecord &r) {
    return true;
  }

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

    SubAccessor accessor(r);

    switch (r.recordType()) {
  
    case RcdHeader::runStart: {

      // Find runStart object
      std::vector<const IlcRunStart*>
        vs(accessor.access<IlcRunStart>());
      assert(vs.size()==1);
      
      // Get local copy
      _runStart=*(vs[0]);
      
      // Check if this run is to be analysed
      _validRunType=mpsAnalysisValidRun(_runStart.runType().type());
      
      if(_validRunType) {
	if(doPrint(r.recordType(),1)) {
	  _runStart.print(std::cout," ") << std::endl;
	}
	
	// Open root file
        std::ostringstream sFile;
        sFile << _mpsAnalysisName << std::setfill('0') << std::setw(6)
              << _runStart.runNumber() << ".root";
	_rootFileName=sFile.str();
	
	std::cout << _mpsAnalysisName << "::record()  Creating ROOT file "
		  << _rootFileName << std::endl << std::endl;
        _rootFile = new TFile(sFile.str().c_str(),"RECREATE");
	
	// Make useful run labels and titles for histograms, etc.
	std::ostringstream rLabel;
	rLabel << "Run" << std::setfill('0') << std::setw(6)
	       << _runStart.runNumber();
	_runLabel=rLabel.str();
	
	std::ostringstream rTitle;
	rTitle << "Run " << _runStart.runNumber();
	_runTitle=rTitle.str();
	
	// Get list of USB_DAQs
	std::vector<const MpsLocationData<MpsUsbDaqRunData>* >
	  v(accessor.access< MpsLocationData<MpsUsbDaqRunData> >());
	
	// Go through list and store location of each
	_vLocation.clear();
	for(unsigned i(0);i<v.size();i++) {
	  if(doPrint(r.recordType(),2)) {
	    v[i]->print(std::cout," ") << std::endl;
	  }
	  _vLocation.push_back(v[i]->location());
	  
	  // Make useful labels and titles for histograms, etc.
	  std::ostringstream uLabel;
	  uLabel << "UsbDaq" << std::setfill('0') << std::setw(2)
		 << (unsigned)_vLocation[i].usbDaqAddress();
	  _usbDaqLabel.push_back(uLabel.str());
	  
	  std::ostringstream uTitle;
	  uTitle << "USB_DAQ " << (unsigned)_vLocation[i].usbDaqAddress();
	  _usbDaqTitle.push_back(uTitle.str());
	  
	  std::ostringstream sLabel;
	  sLabel << "Sensor" << std::setfill('0') << std::setw(2)
		 << (unsigned)_vLocation[i].sensorId();
	  _sensorLabel.push_back(sLabel.str());
	  
	  std::ostringstream sTitle;
	  sTitle << "Sensor " << (unsigned)_vLocation[i].sensorId();
	  _sensorTitle.push_back(sTitle.str());
	}
	
	// Print out all the labels and titles
	if(doPrint(r.recordType(),1)) {
	  std::cout << " Run label = " << _runLabel
		    << " and title = " << _runTitle << std::endl;
	  
	  if(doPrint(r.recordType(),2)) {
	    std::cout << " Number of USB_DAQ labels and titles = "
		      << _vLocation.size() << std::endl;
	    
	    for(unsigned i(0);i<_vLocation.size();i++) {
	      std::cout << "  USB_DAQ " << std::setw(2) << i
			<< ", USB_DAQ label = " << _usbDaqLabel[i]
			<< " and title = " << _usbDaqTitle[i] << std::endl
 			<< "              Sensor  label = " << _sensorLabel[i]
			<< " and title = " << _sensorTitle[i]
			<< std::endl;
	    }
	    std::cout << std::endl;
	  }
	}

	// Clear all configuration data and set vector lengths
	//_vUsbDaqConfigurationData.clear();
	//_vPcbConfigurationData.clear();
	//_vSensorConfigurationData.clear();
	_vUsbDaqConfigurationData.resize(_vLocation.size());
	_vPcbConfigurationData.resize(_vLocation.size());
	_vSensorConfigurationData.resize(_vLocation.size());

	for(unsigned i(0);i<_vLocation.size();i++) {
	  _validUsbDaqConfigurationData.push_back(false);
	  _validPcbConfigurationData.push_back(false);
	  _validSensorConfigurationData.push_back(false);
	}
	
	// Call analysis method
	_rootFile->cd();
	runStart(r);
	
	
      } else {
	
	// Not a run to be analysed by this class
	  if(doPrint(r.recordType(),1)) {
	    std::cout << _mpsAnalysisName << "::record()  Run ignored"
		      << std::endl << std::endl;
	  }
      }

      break;
    }
      
    case RcdHeader::runEnd: {
      if(_validRunType) {

	// Print out runEnd information
	std::vector<const IlcRunEnd*>
	  ve(accessor.access<IlcRunEnd>());
	assert(ve.size()==1);
	if(doPrint(r.recordType(),1)) ve[0]->print(std::cout) << std::endl;

	// Call analysis method
	_rootFile->cd();
	runEnd(r);

	// Close root file
	endRoot();
      }

      // Reset run analysis switch
      _validRunType=true;
      break;
    }
  
    case RcdHeader::configurationStart: {
      if(_validRunType) {

	// Find configurationStart object
	std::vector<const IlcConfigurationStart*>
	  vs(accessor.access<IlcConfigurationStart>());
	assert(vs.size()==1);
	
	// Get local copy
	_configurationStart=*(vs[0]);

	if(doPrint(r.recordType(),1)) {
	  _configurationStart.print(std::cout," ") << std::endl;
	}

	// Make useful labels and titles
	std::ostringstream cLabel;
	cLabel << "Cfg" << std::setfill('0') << std::setw(6)
	       << _configurationStart.configurationNumberInRun();
	_cfgLabel=cLabel.str();
	
	std::ostringstream cTitle;
	cTitle << "Cfg " << _configurationStart.configurationNumberInRun();
	_cfgTitle=cTitle.str();

	if(doPrint(r.recordType(),1)) {
	  std::cout << " Configuration label = " << _cfgLabel
		    << " and title = " << _cfgTitle
		    << std::endl << std::endl;
	}

	// Get list of USB_DAQ configuration data
	std::vector<const MpsLocationData<MpsUsbDaqConfigurationData>* >
	  u(accessor.access< MpsLocationData<MpsUsbDaqConfigurationData> >());
	
	for(unsigned i(0);i<u.size();i++) {
	  if(doPrint(r.recordType(),2)) {
	    u[i]->print(std::cout,"  ") << std::endl;
	  }

	  // Only copy data read back
	  if(!u[i]->write()) {

	    // Find array index of this USB_DAQ
	    for(unsigned j(0);j<_vLocation.size();j++) {
	      if(_vLocation[j].usbDaqAddress()==u[i]->usbDaqAddress()) {

		// Copy data to right array location
		_validUsbDaqConfigurationData[j]=true;
		_vUsbDaqConfigurationData[j]=*(u[i]->data());	      
	      }
	    }
	  }
	}
	
	// Get list of sensor PCB configuration data
	std::vector<const MpsLocationData<MpsPcb1ConfigurationData>* >
	  v(accessor.access< MpsLocationData<MpsPcb1ConfigurationData> >());
	
	for(unsigned i(0);i<v.size();i++) {
	  if(doPrint(r.recordType(),2)) {
	    v[i]->print(std::cout,"  ") << std::endl;
	  }

	  // Only copy data read back
	  if(!v[i]->write()) {

	    // Find array index of this USB_DAQ
	    for(unsigned j(0);j<_vLocation.size();j++) {
	      if(_vLocation[j].usbDaqAddress()==v[i]->usbDaqAddress()) {

		// Copy data to right array location
		_validPcbConfigurationData[j]=true;
		_vPcbConfigurationData[j]=*(v[i]->data());
	      }
	    }
	  }
	}

	// Get list of sensor configuration dataa
	std::vector<const MpsLocationData<MpsSensor1ConfigurationData>* >
	  w(accessor.access< MpsLocationData<MpsSensor1ConfigurationData> >());

	// First do broadcasts
	for(unsigned i(0);i<w.size();i++) {
	  if(doPrint(r.recordType(),2)) w[i]->print() << std::endl;
	  
	  // Only copy data written; read back is previous stored data
	  if(w[i]->write() && w[i]->sensorBroadcast()) {
	    
	    // Write to all sensors
	    for(unsigned j(0);j<_vLocation.size();j++) {
	      _validSensorConfigurationData[j]=true;
	      _vSensorConfigurationData[j]=*(w[i]->data());
	    }
	  }
	}

	// Now do individual configurations
	for(unsigned i(0);i<w.size();i++) {
	  
	  // Only copy data written; read back is previous stored data
	  if(w[i]->write() && !w[i]->sensorBroadcast()) {
	    
	    // Find array index of this USB_DAQ
	    for(unsigned j(0);j<_vLocation.size();j++) {
	      if(_vLocation[j].usbDaqAddress()==w[i]->usbDaqAddress()) {
		_validSensorConfigurationData[j]=true;
		_vSensorConfigurationData[j]=*(w[i]->data());
	      }
	    }
	  }
	}

	// Call analysis method
	_rootFile->cd();
	configurationStart(r);
      }

      break;
    }
      
    case RcdHeader::configurationEnd: {
      if(_validRunType) {

	// Print out configurationEnd information
	std::vector<const IlcConfigurationEnd*>
	  ve(accessor.access<IlcConfigurationEnd>());
	assert(ve.size()==1);
	if(doPrint(r.recordType(),1)) {
	  ve[0]->print(std::cout," ") << std::endl;
	}

	// Call analysis method
	_rootFile->cd();
	configurationEnd(r);
      }

      break;
    }
  
    case RcdHeader::slowReadout: {
      if(_validRunType) {

	// Get local copy of slow readout object
	std::vector<const IlcSlowReadout*>
	  vs(accessor.access<IlcSlowReadout>());
	assert(vs.size()==1);

	_slowReadout=*(vs[0]);
	if(doPrint(r.recordType())) {
	  _slowReadout.print(std::cout," ") << std::endl;
	}

	// Call analysis method
	_rootFile->cd();
	slowReadout(r);
      }

      break;
    }
      
    case RcdHeader::bunchTrain: {
      if(_validRunType) {

	// Get local copy of bunch train object
	std::vector<const IlcBunchTrain*>
	  vb(accessor.access<IlcBunchTrain>());
	assert(vb.size()==1);

	_bunchTrain=*(vb[0]);
	if(doPrint(r.recordType())) {
	  _bunchTrain.print(std::cout," ") << std::endl;
	}

	// Make useful labels and titles
	std::ostringstream bLabel;
	bLabel << "Bnt" << std::setfill('0') << std::setw(6)
	       << _bunchTrain.bunchTrainNumberInRun();
	_bntLabel=bLabel.str();
	
	std::ostringstream bTitle;
	bTitle << "Bnt " << _bunchTrain.bunchTrainNumberInRun();
	_bntTitle=bTitle.str();
	
	if(doPrint(r.recordType(),1)) {
	  std::cout << " Bunch train label = " << _bntLabel
		    << " and title = " << _bntTitle
		    << std::endl << std::endl;
	}

	// Call analysis method
	_rootFile->cd();
	bunchTrain(r);
      }
      break;
    }
      
      
    default: {
      break;
    }
    };
    
    return true;
  }

  std::vector<const MpsLocationData<MpsSensor1BunchTrainData>* > sensor1BunchTrainData(const RcdRecord &r) const {
    std::vector<const MpsLocationData<MpsSensor1BunchTrainData>* > v(_vLocation.size());
    for(unsigned j(0);j<_vLocation.size();j++) v[j]=0;

    SubAccessor accessor(r);

    std::vector<const MpsLocationData<MpsSensor1BunchTrainData>* >
      w(accessor.access< MpsLocationData<MpsSensor1BunchTrainData> >());

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

      // Find array index of this USB_DAQ
      for(unsigned j(0);j<_vLocation.size();j++) {
	if(_vLocation[j].usbDaqAddress()==w[i]->usbDaqAddress()) {
	  v[j]=w[i];
	}
      }
    }

    return v;
  }

  virtual void endRoot() {
    if(_rootFile!=0) {
      std::cout << _mpsAnalysisName << "::endRoot()  "
		<< "Closing ROOT file " << _rootFileName << std::endl;

      _rootFile->cd();
      _rootFile->Write();
      _rootFile->Close();

      delete _rootFile;
      _rootFile=0;
    }
  }


private:
  std::string _mpsAnalysisName;
  bool _validRunType;

protected:
  TFile* _rootFile;
  std::string _rootFileName;

  // Filled in ctor
  std::string _pixelLabel[168][168];
  std::string _pixelTitle[168][168];

  // Filled at runStart
  IlcRunStart _runStart;
  std::string _runLabel;
  std::string _runTitle;

  std::vector<MpsLocation> _vLocation;
  std::vector<std::string> _usbDaqLabel;
  std::vector<std::string> _usbDaqTitle;
  std::vector<std::string> _sensorLabel;
  std::vector<std::string> _sensorTitle;

  // Filled at configurationStart
  IlcConfigurationStart _configurationStart;
  std::string _cfgLabel;
  std::string _cfgTitle;

  std::vector<std::string> _cfgUsbDaqLabel;
  std::vector<std::string> _cfgUsbDaqTitle;
  std::vector<std::string> _cfgSensorLabel;
  std::vector<std::string> _cfgSensorTitle;

  std::vector<bool> _validUsbDaqConfigurationData;
  std::vector<MpsUsbDaqConfigurationData>  _vUsbDaqConfigurationData;
  std::vector<bool> _validPcbConfigurationData;
  std::vector<MpsPcb1ConfigurationData>    _vPcbConfigurationData;
  std::vector<bool> _validSensorConfigurationData;
  std::vector<MpsSensor1ConfigurationData> _vSensorConfigurationData;

  // Filled at slowReadout
  IlcSlowReadout _slowReadout;

  // Filled at bunchTrain
  IlcBunchTrain _bunchTrain;
  std::string _bntLabel;
  std::string _bntTitle;
};

#endif
