//
// $Id: DhcConfiguration.hh,v 1.1 2008/06/27 10:34:01 meyern Exp $
//

#ifndef DhcConfiguration_HH
#define DhcConfiguration_HH

#include <iostream>
#include <fstream>

// dual/inc/daq
#include "DaqRunStart.hh"
#include "DaqConfigurationStart.hh"
#include "DaqSpillStart.hh"
#include "DaqRunType.hh"

// dual/inc/rcd
#include "RcdUserRW.hh"

// dual/inc/sub
#include "SubInserter.hh"
#include "SubAccessor.hh"

#include "DhcLocation.hh"
#include "DhcBeConfigurationData.hh"
#include "DhcFeConfigurationData.hh"
#include "DhcReadoutConfigurationData.hh"

#include "TtmLocation.hh"
#include "TtmLocationData.hh"
#include "TtmConfigurationData.hh"

#include "DhcConfigReader.hh"


class DhcConfiguration : public RcdUserRW {
public:
  DhcConfiguration(unsigned char c) :
    _location(c,0,0,1), _trgLocation(c,0),
    _runType(), _configurationNumber(0) {
  }

  virtual ~DhcConfiguration() {
  }

  void trgSlot(unsigned s) {
    assert(s>0 && s<=21);
    _trgLocation.slotNumber(s);

    std::cout << "DhcConfiguration::trgSlot()" << std::endl;
    _trgLocation.print(std::cout," ") << std::endl;
  }

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

    // Check record type
    switch (r.recordType()) {

      // Run start 
    case RcdHeader::runStart: {

      // Access the DaqRunStart
      SubAccessor accessor(r);
      std::vector<const DaqRunStart*>
	v(accessor.extract<DaqRunStart>());
      assert(v.size()==1);

      _runType=v[0]->runType();

      break;
    }

    // Configuration start is used to set up system
    case RcdHeader::configurationStart: {

      // Access the DaqConfigurationStart
      SubAccessor accessor(r);
      std::vector<const DaqConfigurationStart*>
	v(accessor.extract<DaqConfigurationStart>());
      assert(v.size()==1);
      if(doPrint(r.recordType(),1)) v[0]->print(std::cout," ") << std::endl;

      _configurationNumber=v[0]->configurationNumberInRun();

      switch(_runType.type()) {

      case DaqRunType::dhcNoise: {
	if (!_configReader.load("data/cfg/dhcal_noise.xml"))
	  return false;
	break;
      }

      case DaqRunType::dhcQinj:
      case DaqRunType::dhcQinjScan: {
	if (!_configReader.load("data/cfg/dhcal_qinj.xml"))
	  return false;
	break;
      }
	
      case DaqRunType::dhcCosmics: {
	if (!_configReader.load("data/cfg/dhcal_cosmic.xml"))
	  return false;
	break;
      }

      default: {
      // use common config for now
	if (!_configReader.load("data/cfg/dhcal.xml"))
	  return false;
	break;
      }
      };


      // Do Trg
      trgConfiguration(r);

      // Do BE 
      beConfiguration(r);

      // Do FE
      feConfiguration(r);

      // Do readout
      readoutConfiguration(r);

      break;
    }

      // Run end
    case RcdHeader::runEnd: {
      _configurationNumber=0;
      break;
    }

    default: {
      break;
    }
    };

    return true;
  }

  bool trgConfiguration(RcdRecord &r) {

    // Define vector for configuration data
    std::vector< TtmLocationData<TtmConfigurationData> > vTcd;

    vTcd.push_back(TtmLocationData<TtmConfigurationData>(_trgLocation));

    // Load configuration into record
    SubInserter inserter(r);
    
    if(doPrint(r.recordType(),1)) std::cout 
      << " Number of TtmConfigurationData subrecords inserted = "
      << vTcd.size() << std::endl << std::endl;
    
    for(unsigned i(0);i<vTcd.size();i++) {

      switch(_runType.type()) {

      case DaqRunType::dhcNoise: {
	vTcd[i].data()->tcsRamSignal(1);
	vTcd[i].data()->tcsInternalReset(0);
	vTcd[i].data()->tcsInternalTcal(0);
	vTcd[i].data()->tcsInternalTrigger(1);
	break;
      }

      case DaqRunType::dhcQinj:
      case DaqRunType::dhcQinjScan: {
	vTcd[i].data()->tcsRamSignal(0);
	vTcd[i].data()->tcsInternalReset(0);
	vTcd[i].data()->tcsInternalTcal(1);
	vTcd[i].data()->tcsInternalTrigger(1);
	vTcd[i].data()->ramDepth(8);
	vTcd[i].data()->ramLoad(4);
	break;
      }

      case DaqRunType::dhcCosmics: {
	vTcd[i].data()->tcsRamSignal(0);
	vTcd[i].data()->tcsInternalReset(0);
	vTcd[i].data()->tcsInternalTcal(0);
	vTcd[i].data()->tcsInternalTrigger(0);
	break;
      }

      default: {
	break;
      }
      }; // switch(type)

      inserter.insert< TtmLocationData<TtmConfigurationData> >(vTcd[i]);
      if(doPrint(r.recordType(),1)) vTcd[i].print(std::cout) << std::endl;
    }

    return true;
  }

  bool beConfiguration(RcdRecord &r) {

    // Define vector for configuration data
    std::vector< DhcLocationData<DhcBeConfigurationData>* > vBcd(_configReader.vBcd());

    // Load configuration into record
    SubInserter inserter(r);
    
    if(doPrint(r.recordType(),1)) std::cout 
      << " Number of DhcBeConfigurationData subrecords inserted = "
      << vBcd.size() << std::endl << std::endl;
    
    for(unsigned i(0);i<vBcd.size();i++) {

      inserter.insert< DhcLocationData<DhcBeConfigurationData> >(*vBcd[i]);
      if(doPrint(r.recordType(),1)) vBcd[i]->print(std::cout) << std::endl;
    }

    return true;
  }

  bool feConfiguration(RcdRecord &r) {

    // Define vector for configuration data
    std::vector< DhcLocationData<DhcFeConfigurationData>* > vFcd(_configReader.vFcd());

    // Load configuration into record
    SubInserter inserter(r);
    
    if(doPrint(r.recordType(),1)) std::cout 
      << " Number of DhcFeConfigurationData subrecords inserted = "
      << vFcd.size() << std::endl << std::endl;
    
    const unsigned char v(_runType.version());
    const UtlPack u(_runType.version());

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

      switch(_runType.type()) {

      case DaqRunType::dhcNoise: {
	vFcd[i]->data()->vtpd(30);
	vFcd[i]->data()->dcrExternalTrigger(0);
	break;
      }

      case DaqRunType::dhcQinj: {
	vFcd[i]->data()->vtpd(v);
	vFcd[i]->data()->dcrExternalTrigger(1);

	unsigned char inj[8];
	for (unsigned j(0); j<sizeof(inj); j++)
	  inj[j] = 0x11<<_configurationNumber;
	vFcd[i]->data()->inj(inj);

	break;
      }

      case DaqRunType::dhcQinjScan: {
	vFcd[i]->data()->dcrExternalTrigger(1);

	unsigned char inj[8];
	for (unsigned j(0); j<sizeof(inj); j++)
	  inj[j] = 0x11<<(_configurationNumber%4);
	vFcd[i]->data()->inj(inj);

	vFcd[i]->data()->vtpd(*vFcd[i]->data()->vtpd() +
			     u.bits(0,3)*(_configurationNumber/4));
	break;
      }

      case DaqRunType::dhcCosmics: {
	vFcd[i]->data()->dcrExternalTrigger(1);

	break;
      }

      default: {
	break;
      }
      }; // switch(type)
      
      inserter.insert< DhcLocationData<DhcFeConfigurationData> >(*vFcd[i]);
      if(doPrint(r.recordType(),1)) vFcd[i]->print(std::cout) << std::endl;
    }

    return true;
  }

  bool readoutConfiguration(RcdRecord &r) {

    std::vector< DhcReadoutConfigurationData* > vRcd(_configReader.vRcd());

    SubInserter inserter(r);
    
    if(doPrint(r.recordType(),1)) std::cout 
      << " Number of DhcReadoutConfigurationData subrecords inserted = "
      << vRcd.size() << std::endl << std::endl;
    
    for(unsigned i(0);i<vRcd.size();i++) {

      switch(_runType.type()) {

      case DaqRunType::dhcNoise: {
	vRcd[i]->triggerByWaiting(true);
	break;
      }

      case DaqRunType::dhcQinj:
      case DaqRunType::dhcQinjScan: {
	vRcd[i]->triggerInternally(true);
	break;
      }

      case DaqRunType::dhcCosmics: {
	vRcd[i]->triggerExternally(true);
	break;
      }

      default: {
	break;
      }
      }; // switch(type)

      inserter.insert< DhcReadoutConfigurationData >(*vRcd[i]);
      if(doPrint(r.recordType(),1)) vRcd[i]->print(std::cout) << std::endl;
    }

    return true;
  }

protected:
  DhcLocation _location;
  TtmLocation _trgLocation;

  DaqRunType _runType;
  unsigned _configurationNumber;

  DhcConfigReader _configReader;
};


#endif // DhcConfiguration_HH
