#ifndef IlcConfiguration_HH
#define IlcConfiguration_HH

#include <vector>
#include <fstream>
#include <iostream>

#include "RcdUserRW.hh"

#include "SubInserter.hh"
#include "SubAccessor.hh"

#include "IlcRunStart.hh"
#include "IlcRunEnd.hh"
#include "IlcConfigurationStart.hh"
#include "IlcConfigurationEnd.hh"
#include "IlcBunchTrain.hh"

//#include "DaqRunNumber.hh"


class IlcConfiguration : public RcdUserRW {

public:
  enum Counter {
    rInJ,
    cInR,
    srInR,srInC,
    bInR,bInC,
    endOfCounterEnum
  };

  IlcConfiguration() {
  }

  virtual ~IlcConfiguration() {
  }

  IlcRunStart runStart() const {
    return _runStartFromRunControl;
  }

  void runStart(IlcRunStart r) {
    _runStartFromRunControl=r;
    assert(_runStartFromRunControl.runType().knownType());
  }

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

    UtlPack tid;
    tid.halfWord(1,SubHeader::daq);
    tid.byte(2,0);

    DaqTwoTimer *t(inserter.insert<DaqTwoTimer>(true));
    t->timerId(tid.word());


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

    case RcdHeader::startUp: {
      _count[rInJ]=0;

      break;
    }
      
    case RcdHeader::runStart: {
      _count[cInR]=0; //c
      _count[srInR]=0; //sr
      _count[bInR]=0; //b

      SubInserter inserter(r);
      IlcRunStart *d(inserter.insert<IlcRunStart>(true));

      if(_runStartFromRunControl.runType().writeRun()) {
	_runNumber=daqReadRunNumber();
	daqWriteRunNumber(_runNumber+1);
      } else {
	_runNumber=r.recordTime().seconds();
      }

      d->runNumber(_runNumber);
      d->runType(_runStartFromRunControl.runType());
      setRun(*d);
      
      if(doPrint(r.recordType(),1)) d->print(std::cout," ") << std::endl;
      
      break;
    }
      
    case RcdHeader::configurationStart: {
      _count[srInC]=0; //a
      _count[bInC]=0; //b
          
      _daqConfigurationStart.reset();
      _daqConfigurationStart.configurationNumberInRun(_count[cInR]);
      setConfiguration(_daqConfigurationStart);
     
      SubInserter inserter(r);
      inserter.insert<IlcConfigurationStart>(_daqConfigurationStart);
    
      _count[cInR]++;
     
      if(doPrint(r.recordType(),1)) _daqConfigurationStart.print(std::cout," ") << std::endl;
      
      break;
    }

    case RcdHeader::slowReadout: {
      SubInserter inserter(r);
      IlcSlowReadout *d(inserter.insert<IlcSlowReadout>(true));

      d->slowReadoutNumberInRun(_count[srInR]);
      d->slowReadoutNumberInConfiguration(_count[srInC]);

      _count[srInR]++;
      _count[srInC]++;

      if(doPrint(r.recordType(),1)) d->print(std::cout," ") << std::endl;

      break;
    }

    case RcdHeader::bunchTrain: {
      SubInserter inserter(r);
      IlcBunchTrain *d(inserter.insert<IlcBunchTrain>(true));
      
      d->bunchTrainNumberInRun(_count[bInR]);
      d->bunchTrainNumberInConfiguration(_count[bInC]);
      
      _count[bInR]++;
      _count[bInC]++;

      if(doPrint(r.recordType(),1)) d->print(std::cout," ") << std::endl;
      
      break;
    }
      
    default: {
      break;
    }
    };

    // Close off overall timer
    t->setEndTime();
    if(doPrint(r.recordType())) t->print(std::cout," ") << std::endl;

    return true;
  }


  virtual void setRun(IlcRunStart &d) const {
    //const unsigned char v(_runStartFromRunControl.runType().version());

    const UtlPack v(_runStartFromRunControl.runType().version());

    switch(_runStartFromRunControl.runType().type()) {

    case IlcRunType::slwMonitor: {
      break;
    }

    case IlcRunType::usbPmtThreshold: {
      break;
    }
    case IlcRunType::usbPmtThresholdScan: {
      d.maximumNumberOfConfigurationsInRun(v.word()+1);
      break;
    }

    case IlcRunType::mpsTest: {
      break;
    }
    case IlcRunType::mpsExpert: {
      break;
    }
    case IlcRunType::mpsNoise: {
      break;
    }
    case IlcRunType::mpsPcbConfigurationScan: {
      if(!v.bit(7)) {
	//d.maximumNumberOfConfigurationsInRun(32*(2*v.halfByte(0)+1)+2);
        d.maximumNumberOfConfigurationsInRun(100);
      }
      break;
    }
    case IlcRunType::mpsThreshold: {
      break;
    }
    case IlcRunType::mpsThresholdScan: {
      thresholdScanRun(v,d);
      //d.maximumNumberOfConfigurationsInRun(v.word()+1);
      //d.maximumNumberOfConfigurationsInRun((v.word()+1)*(v.word()+1));
      break;
    }
    case IlcRunType::mpsTrim: {
      break;
    }
    case IlcRunType::mpsTrimScan: {
      //d.maximumNumberOfConfigurationsInRun(16*16+2);
      d.maximumNumberOfConfigurationsInRun(16*42);
      break;
    }
    case IlcRunType::mpsHitOverride: {
      d.maximumNumberOfConfigurationsInRun(v.word()+1);
      break;
    }
    case IlcRunType::mpsBeamThresholdScan: {
      break;
    }
    case IlcRunType::mpsBeam: {
      break;
    }
    case IlcRunType::mpsCosmicsThresholdScan: {
      break;
    }
    case IlcRunType::mpsCosmics: {
      break;
    }
    case IlcRunType::mpsSource: {
      break;
    }
    case IlcRunType::mpsSourceThresholdScan: {
      break;
    }
    case IlcRunType::mpsLaser: {
      break;
    }
    case IlcRunType::mpsLaserPosition: {
      break;
    }
    case IlcRunType::mpsLaserPositionScan: {
      d.maximumNumberOfConfigurationsInRun(5*5);
      if(v.halfByte(0)==1) d.maximumNumberOfConfigurationsInRun(15*15);
      if(v.halfByte(0)==2) d.maximumNumberOfConfigurationsInRun(30*30);
      if(v.halfByte(0)==3) d.maximumNumberOfConfigurationsInRun(30);
      if(v.halfByte(0)==4) d.maximumNumberOfConfigurationsInRun(30);
      if(v.halfByte(0)==5) d.maximumNumberOfConfigurationsInRun(150);
      if(v.halfByte(0)==6) d.maximumNumberOfConfigurationsInRun(150);
      if(v.halfByte(0)==7) d.maximumNumberOfConfigurationsInRun(75*75);
      if(v.halfByte(0)==8) d.maximumNumberOfConfigurationsInRun(150*150);
      break;
    }
    case IlcRunType::mpsLaserThreshold: {
      break;
    }
    case IlcRunType::mpsLaserThresholdScan: {
      thresholdScanRun(v,d);
      /*
      d.maximumNumberOfConfigurationsInRun(400);
      if(v.halfByte(0)==1) d.maximumNumberOfConfigurationsInRun(  5);
      if(v.halfByte(0)==2) d.maximumNumberOfConfigurationsInRun( 10);
      if(v.halfByte(0)==3) d.maximumNumberOfConfigurationsInRun( 20);
      if(v.halfByte(0)==4) d.maximumNumberOfConfigurationsInRun( 50);
      if(v.halfByte(0)==5) d.maximumNumberOfConfigurationsInRun(100);
      if(v.halfByte(0)==6) d.maximumNumberOfConfigurationsInRun(200);
      */
      break;
    }
    case IlcRunType::mpsMonostableLengthScan: {
      //d.maximumNumberOfConfigurationsInRun(v.word()+1);
      break;
    }
    case IlcRunType::mpsConfigurationTest: {
      break;
    }
    case IlcRunType::mpsUsbDaqConfigurationScan: {
      if(!v.bit(7)) {
	d.maximumNumberOfConfigurationsInRun(256);

	// Reset durations
	if(v.halfByte(0)==0) d.maximumNumberOfConfigurationsInRun(100);
      }
      break;
    }
    case IlcRunType::mpsMaskThresholdScan: {
      thresholdScanRun(v,d);
      /*
      d.maximumNumberOfConfigurationsInRun(400);
      if(v.halfByte(0)==1) d.maximumNumberOfConfigurationsInRun(  5);
      if(v.halfByte(0)==2) d.maximumNumberOfConfigurationsInRun( 10);
      if(v.halfByte(0)==3) d.maximumNumberOfConfigurationsInRun( 20);
      if(v.halfByte(0)==4) d.maximumNumberOfConfigurationsInRun( 50);
      if(v.halfByte(0)==5) d.maximumNumberOfConfigurationsInRun(100);
      if(v.halfByte(0)==6) d.maximumNumberOfConfigurationsInRun(200);
      mpsMaskThresholdScanRun(v,d);
      */
      break;
    }
    case IlcRunType::mpsLaserCoordinates: {
      d.maximumNumberOfConfigurationsInRun((v.halfByte(0)+1)*(v.halfByte(0)+1));
      break;
    }
    case IlcRunType::mpsLaserTime: {
      break;
    }
    case IlcRunType::mpsLaserTimeScan: {
      d.maximumNumberOfConfigurationsInRun(40);
      break;
    }
    case IlcRunType::mpsLaserTrim: {
      break;
    }
    case IlcRunType::mpsLaserTrimScan: {
      d.maximumNumberOfConfigurationsInRun(16);
      break;
    }

    default: {
      // We missed a run type
      assert(false);
      break;
    }
    };

    // Reset limits if endless run from run control
    if(_runStartFromRunControl.runType().endlessRun()) {
      d.maximumNumberOfConfigurationsInRun(0xffffffff);
      d.maximumNumberOfBunchTrainsInRun(0xffffffff);
      d.maximumTimeOfRun(UtlTimeDifference(0x7fffffff,999999));
    }

    // Reset limits if smaller from run control
    if(d.maximumNumberOfConfigurationsInRun()>
       _runStartFromRunControl.maximumNumberOfConfigurationsInRun())
      d.maximumNumberOfConfigurationsInRun(_runStartFromRunControl.maximumNumberOfConfigurationsInRun());

    if(d.maximumNumberOfBunchTrainsInRun()>
       _runStartFromRunControl.maximumNumberOfBunchTrainsInRun())
      d.maximumNumberOfBunchTrainsInRun(_runStartFromRunControl.maximumNumberOfBunchTrainsInRun());

    if(d.maximumTimeOfRun()>
       _runStartFromRunControl.maximumTimeOfRun())
      d.maximumTimeOfRun(_runStartFromRunControl.maximumTimeOfRun());
  }

  virtual void setConfiguration(IlcConfigurationStart &d) const {
    //const unsigned iCfg(d.configurationNumberInRun());

    const UtlPack v(_runStartFromRunControl.runType().version());

    // Overall defaults
    d.maximumTimeOfConfiguration(UtlTimeDifference(60*60)); // 1hour
    
    // Select on run type
    switch(_runStartFromRunControl.runType().type()) {

    case IlcRunType::slwMonitor: {
      break;
    }

    case IlcRunType::usbPmtThreshold: {
      break;
    }
    case IlcRunType::usbPmtThresholdScan: {
      d.maximumNumberOfBunchTrainsInConfiguration(1000);
      break;
    }

    case IlcRunType::mpsTest: {
      break;
    }
    case IlcRunType::mpsExpert: {
      break;
    }
    case IlcRunType::mpsNoise: {
      break;
    }
    case IlcRunType::mpsPcbConfigurationScan: {
      if(v.bits(5,6)==0) d.maximumNumberOfBunchTrainsInConfiguration(1);
      if(v.bits(5,6)==1) d.maximumNumberOfBunchTrainsInConfiguration(10);
      if(v.bits(5,6)==2) d.maximumNumberOfBunchTrainsInConfiguration(100);
      if(v.bits(5,6)==3) d.maximumNumberOfBunchTrainsInConfiguration(1000);
      break;
    }
    case IlcRunType::mpsThreshold: {
      break;
    }
    case IlcRunType::mpsThresholdScan: {
      //d.maximumNumberOfBunchTrainsInConfiguration(50000);
      d.maximumNumberOfBunchTrainsInConfiguration(2000);
      break;
    }
    case IlcRunType::mpsTrim: {
      break;
    }
    case IlcRunType::mpsTrimScan: {
      d.maximumNumberOfBunchTrainsInConfiguration(1000);
      break;
    }
    case IlcRunType::mpsHitOverride: {
      d.maximumNumberOfBunchTrainsInConfiguration(10000);
      break;
    }
    case IlcRunType::mpsBeamThresholdScan: {
      d.maximumNumberOfBunchTrainsInConfiguration(1000);
      if(v.word()==122) d.maximumNumberOfBunchTrainsInConfiguration(1);
      break;
    }
    case IlcRunType::mpsBeam: {
      break;
    }
    case IlcRunType::mpsCosmicsThresholdScan: {
      d.maximumNumberOfBunchTrainsInConfiguration(10000);
      if(v.word()==122) d.maximumNumberOfBunchTrainsInConfiguration(1);
      break;
    }
    case IlcRunType::mpsCosmics: {
      break;
    }
    case IlcRunType::mpsSource: {
      break;
    }
    case IlcRunType::mpsSourceThresholdScan: {
      d.maximumNumberOfBunchTrainsInConfiguration(1000);
      break;
    }
    case IlcRunType::mpsLaser: {
      break;
    }
    case IlcRunType::mpsLaserPosition: {
      break;
    }
    case IlcRunType::mpsLaserPositionScan: {
      d.maximumNumberOfBunchTrainsInConfiguration(1000);
      if(v.bit(4)) d.maximumNumberOfBunchTrainsInConfiguration(100);
      break;
    }
    case IlcRunType::mpsLaserThreshold: {
      break;
    }
    case IlcRunType::mpsLaserThresholdScan: {
      d.maximumNumberOfBunchTrainsInConfiguration(1000);
      if(v.bit(4)) d.maximumNumberOfBunchTrainsInConfiguration(100);
      break;
    }
    case IlcRunType::mpsMonostableLengthScan: {
      d.maximumNumberOfBunchTrainsInConfiguration(100);
      break;
    }
    case IlcRunType::mpsConfigurationTest: {
      d.maximumNumberOfBunchTrainsInConfiguration(0);
      break;
    }
    case IlcRunType::mpsUsbDaqConfigurationScan: {
      if(v.bits(4,6)==0) d.maximumNumberOfBunchTrainsInConfiguration(1);
      if(v.bits(4,6)==1) d.maximumNumberOfBunchTrainsInConfiguration(10);
      if(v.bits(4,6)==2) d.maximumNumberOfBunchTrainsInConfiguration(100);
      if(v.bits(4,6)==3) d.maximumNumberOfBunchTrainsInConfiguration(1000);
      if(v.bits(4,6)==4) d.maximumNumberOfBunchTrainsInConfiguration(10000);
      if(v.bits(4,6)==5) d.maximumNumberOfBunchTrainsInConfiguration(100000);
      if(v.bits(4,6)==6) d.maximumNumberOfBunchTrainsInConfiguration(1000000);
      if(v.bits(4,6)==7) d.maximumNumberOfBunchTrainsInConfiguration(10000000);
      break;
    }
    case IlcRunType::mpsMaskThresholdScan: {
      thresholdScanCfg(v,d);
      /*
      d.maximumNumberOfBunchTrainsInConfiguration(1000);
      if(v.bit(4)) d.maximumNumberOfBunchTrainsInConfiguration(100);
      mpsMaskThresholdScanCfg(v,d);
      */
      break;
    }
    case IlcRunType::mpsLaserCoordinates: {
      d.maximumNumberOfBunchTrainsInConfiguration(1000);
      if(v.bit(4)) d.maximumNumberOfBunchTrainsInConfiguration(100);
      break;
    }
    case IlcRunType::mpsLaserTime: {
      break;
    }
    case IlcRunType::mpsLaserTimeScan: {
      d.maximumNumberOfBunchTrainsInConfiguration(1000);
      if(v.bit(4)) d.maximumNumberOfBunchTrainsInConfiguration(100);
      break;
    }
    case IlcRunType::mpsLaserTrim: {
      break;
    }
    case IlcRunType::mpsLaserTrimScan: {
      d.maximumNumberOfBunchTrainsInConfiguration(1000);
      break;
    }

    default: {
      // We missed a run type
      assert(false);
      break;
    }
    };
  }

  void thresholdScanRun(UtlPack v, IlcRunStart &d) const {
    d.maximumNumberOfConfigurationsInRun(400);
    if(v.halfByte(0)==0) d.maximumNumberOfConfigurationsInRun(  8);
    if(v.halfByte(0)==1) d.maximumNumberOfConfigurationsInRun( 10);
    if(v.halfByte(0)==2) d.maximumNumberOfConfigurationsInRun( 20);
    if(v.halfByte(0)==3) d.maximumNumberOfConfigurationsInRun( 40);
    if(v.halfByte(0)==4) d.maximumNumberOfConfigurationsInRun( 80);
    if(v.halfByte(0)==5) d.maximumNumberOfConfigurationsInRun(100);
    if(v.halfByte(0)==6) d.maximumNumberOfConfigurationsInRun(200);
  }

  void thresholdScanCfg(UtlPack v, IlcConfigurationStart &d) const {
    d.maximumNumberOfBunchTrainsInConfiguration(1000);
    if(v.bit(4)) d.maximumNumberOfBunchTrainsInConfiguration(100);
  }

  //void mpsMaskThresholdScanRun(UtlPack v, IlcRunStart &d) const;
  //void mpsMaskThresholdScanCfg(UtlPack v, IlcConfigurationStart &d) const;

protected:
  IlcRunStart _runStartFromRunControl;

private:
  unsigned _runNumber;
  IlcConfigurationStart _daqConfigurationStart;
  unsigned _count[endOfCounterEnum];
};

//#include "IlcConfigurationMpsMaskThresholdScan.icc"

#endif
