#ifndef IlcReadout_HH
#define IlcReadout_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 "RunControl.hh"


class IlcReadout : public RcdUserRW {

public:
  enum Counter {
    cfgInRun,
    slwInRun,slwInCfg,
    bntInRun,bntInCfg,
    endOfCounterEnum
  };

  IlcReadout() :
    _shmRunControl(RunControl::shmKey), _pRc(_shmRunControl.payload()) {
    assert(_pRc!=0);

    _inRun=false;
    _inConfiguration=false;
  }

  virtual ~IlcReadout() {
  }

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

    SubInserter inserter(r);

    UtlPack tid;
    tid.halfWord(1,SubHeader::daq);
    tid.byte(2,1);
    DaqTwoTimer *t(inserter.insert<DaqTwoTimer>(true));
    t->timerId(tid.word());


    // Get time of record and check durations
    _headerTime=r.recordTime();

    if(_inRun           && _headerTime-_time[0]>_runStart.maximumTimeOfRun()) {
      if(_pRc->flag()>RunControl::runEnd) {
	_pRc->flag(RunControl::runEnd);
	if(doPrint(r.recordType(),1)) {
	  std::cout << " Run timeout " << _headerTime-_time[0]
		    << " > " << _runStart.maximumTimeOfRun() << std::endl;
	}
      }
    }
    
    if(_inConfiguration && _headerTime-_time[1]>_configurationStart.maximumTimeOfConfiguration()) {
      if(_pRc->flag()>RunControl::configurationEnd) {
	_pRc->flag(RunControl::configurationEnd);
	if(doPrint(r.recordType(),1)) {
	  std::cout << " Configuration timeout " << _headerTime-_time[1]
		    << " > " << _configurationStart.maximumTimeOfConfiguration() << std::endl;
	}
      }
    }

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

    case RcdHeader::runStart: {
      _count[cfgInRun]=0; //c
      _count[slwInRun]=0; //sr
      _count[bntInRun]=0; //b

      _inRun=true;
      _time[0]=_headerTime;

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

      if(doPrint(r.recordType(),1)) v[0]->print(std::cout," ");
      
      // Save start of run information
      _runStart=*(v[0]);

      // Check for end of run conditions
      if(_count[cfgInRun]>=_runStart.maximumNumberOfConfigurationsInRun()) {
	if(_pRc->flag()>RunControl::runEnd) _pRc->flag(RunControl::runEnd);
      }

      if(_count[bntInRun]>=_runStart.maximumNumberOfBunchTrainsInRun()) {
        if(_pRc->flag()>RunControl::runEnd) _pRc->flag(RunControl::runEnd);
      }
      
      break;
    }
      
    case RcdHeader::runEnd: {
      _inRun=false;

      SubInserter inserter(r);
      IlcRunEnd *d(inserter.insert<IlcRunEnd>(true));
      
      d->runNumber(_runStart.runNumber());
      d->runType(_runStart.runType());
      d->actualNumberOfConfigurationsInRun(_count[cfgInRun]);
      d->actualNumberOfSlowReadoutsInRun(_count[slwInRun]);
      d->actualNumberOfBunchTrainsInRun(_count[bntInRun]);
      d->actualTimeOfRun(_headerTime-_time[0]);

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

      break;
    }

    case RcdHeader::configurationStart: {
      _count[slwInCfg]=0; //sr
      _count[bntInCfg]=0; //b
 
      _inConfiguration=true;
      _time[1]=_headerTime;

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

      if(doPrint(r.recordType(),1)) v[0]->print(std::cout," ");
      
      // Save start of configuration information
      _configurationStart=*(v[0]);

      // Check for end of configuration conditions
      if(_count[bntInCfg]>=_configurationStart.maximumNumberOfBunchTrainsInConfiguration()) {
        if(_pRc->flag()>RunControl::configurationEnd) _pRc->flag(RunControl::configurationEnd);
      }
      
      // Check for end of run conditions
      if(_count[bntInRun]>=_runStart.maximumNumberOfBunchTrainsInRun()) {
        if(_pRc->flag()>RunControl::runEnd) _pRc->flag(RunControl::runEnd);
      }
      
      break;
    }

    case RcdHeader::configurationEnd: {
      _inConfiguration=false;

      SubInserter inserter(r);
      IlcConfigurationEnd *d(inserter.insert<IlcConfigurationEnd>(true));
      
      d->configurationNumberInRun(_count[cfgInRun]);
      d->actualNumberOfSlowReadoutsInConfiguration(_count[slwInCfg]);
      d->actualNumberOfBunchTrainsInConfiguration(_count[bntInCfg]);
      d->actualTimeOfConfiguration(_headerTime-_time[1]);

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

      // Increment counts
      _count[cfgInRun]++;

      // Check for end of run conditions
      if(_count[cfgInRun]>=_runStart.maximumNumberOfConfigurationsInRun()) {
	if(_pRc->flag()>RunControl::runEnd) _pRc->flag(RunControl::runEnd);
      }

      break;
    }

    case RcdHeader::slowReadout: {
      _count[slwInRun]++;
      _count[slwInCfg]++;
 
      break;
    }

    case RcdHeader::bunchTrain: {
      _count[bntInRun]++;
      _count[bntInCfg]++;

      // Check for end of configuration conditions
      if(_count[bntInCfg]>=_configurationStart.maximumNumberOfBunchTrainsInConfiguration()) {
        if(_pRc->flag()>RunControl::configurationEnd) _pRc->flag(RunControl::configurationEnd);
      }
      
      // Check for end of run conditions
      if(_count[bntInRun]>=_runStart.maximumNumberOfBunchTrainsInRun()) {
        if(_pRc->flag()>RunControl::runEnd) _pRc->flag(RunControl::runEnd);
      }
      
      break;
    }

    default: {
      break;
    }
    };

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

    return true;
  }

private:
  ShmObject<RunControl> _shmRunControl;
  RunControl *_pRc;

  bool _inRun,_inConfiguration;
  IlcRunStart _runStart;
  IlcConfigurationStart _configurationStart;

  unsigned _count[endOfCounterEnum];

  UtlTime _headerTime;
  UtlTime _time[5];
};

#endif
