#ifndef HstFlags_HH
#define HstFlags_HH

#include <iostream>
#include <vector>

//#include "DaqRunStart.hh"
//#include "DaqConfigurationStart.hh"
//#include "DaqAcquisitionStart.hh"
//#include "DaqEvent.hh"
#include "RcdRecord.hh"
#include "SubAccessor.hh"
#include "HstArray.hh"


class HstFlags {

public:
  enum Level {
    job,
    run,
    configuration,
    acquisition,
    event,
    bunchTrain
  };

  HstFlags() {
    _validRun=false;
    _validConfiguration=false;
    _validAcquisition=false;
    _validEvent=false;
    _validBunchTrain=false;

    _inJob=true;
    _inRun=false;
    _inConfiguration=false;
    _inAcquisition=false;
    _inBunchTrain=false;

    _resetLevel=configuration;
  }

  std::string label(Level l=event) const {
    if(l==bunchTrain)    return "Bnt";
    if(l==event)         return "Evt";
    if(l==acquisition)   return "Acq";
    if(l==configuration) return "Cfg";
    if(l==run)           return "Run";
    if(l==job)           return "Job";
    return "???";
  }

  std::string title() const {
    std::ostringstream sout;
    if(!_validRun) return sout.str();

    if(_ilcTiming) {
      sout << "Run " << _irs.runNumber() << ", ";
      if(!_validConfiguration) return sout.str();
      
      sout << "Cfg " << _ics.configurationNumberInRun() << ", ";
      if(!_validBunchTrain) return sout.str();
      
      sout << "Bnt " << _ibt.bunchTrainNumberInRun()
	   << "/"    << _ibt.bunchTrainNumberInConfiguration() << ", ";

    } else {
      sout << "Run " << _drs.runNumber() << ", ";
      if(!_validConfiguration) return sout.str();
      
      sout << "Cfg " << _dcs.configurationNumberInRun() << ", ";
      if(!_validAcquisition) return sout.str();
      
      sout << "Acq " << _das.acquisitionNumberInRun()
	   << "/"    << _das.acquisitionNumberInConfiguration() << ", ";
      if(!_validEvent) return sout.str();
      
      sout << "Evt " << _dev.eventNumberInRun()
	   << "/"    << _dev.eventNumberInConfiguration()
	   << "/"    << _dev.eventNumberInAcquisition() << ", ";
    }

    return sout.str(); 
  }

  bool ready(Level l) {
    if(l==event)         return                                             _validEvent;
    if(l==acquisition)   return !_inAcquisition   && _validAcquisition   && _validEvent;
    if(l==configuration) return !_inConfiguration && _validConfiguration && _validEvent;
    if(l==run)           return !_inRun           && _validRun           && _validEvent;
    return false;
  }

  void fill(const RcdRecord &r) {

    switch (r.recordType()) {

    case RcdHeader::shutDown: {
      _inJob=false;
      break;
    }

    case RcdHeader::runStart: {
      SubAccessor accessor(r);

      std::vector<const DaqRunStart*> 
	dv(accessor.access<DaqRunStart>());
      if(dv.size()>0) {
	_ilcTiming=false;
	_drs=*(dv[0]);
      }

      std::vector<const IlcRunStart*> 
	iv(accessor.access<IlcRunStart>());
      if(iv.size()>0) {
	_ilcTiming=true;
	_irs=*(iv[0]);
      }
      
      _validRun=true;
      _validConfiguration=false;
      _validAcquisition=false;
      _validEvent=false;
      _validBunchTrain=false;

      _inRun=true;
      break;
    }

    case RcdHeader::runEnd: {
      _inRun=false;
      break;
    }

    case RcdHeader::configurationStart: {
      SubAccessor accessor(r);

      if(_ilcTiming) {
	std::vector<const IlcConfigurationStart*> 
	  v(accessor.access<IlcConfigurationStart>());
	if(v.size()>0) _ics=*(v[0]);
      } else {
	std::vector<const DaqConfigurationStart*> 
	  v(accessor.access<DaqConfigurationStart>());
	if(v.size()>0) _dcs=*(v[0]);
      }

      _validConfiguration=true;
      _validAcquisition=false;
      _validEvent=false;
      _validBunchTrain=false;

      _inConfiguration=true;
      break;
    }

    case RcdHeader::configurationEnd: {
      _inConfiguration=false;
      break;
    }

    case RcdHeader::acquisitionStart: {
      SubAccessor accessor(r);
      std::vector<const DaqAcquisitionStart*> 
	v(accessor.access<DaqAcquisitionStart>());
      if(v.size()>0) _das=*(v[0]);
      //_das.print(std::cout);
      _validAcquisition=true;
      _validEvent=false;
      _inAcquisition=true;
      break;
    }

    case RcdHeader::acquisitionEnd: {
      _inAcquisition=false;
      break;
    }

    case RcdHeader::event: {
      SubAccessor accessor(r);
      std::vector<const DaqEvent*> 
	v(accessor.access<DaqEvent>());
      if(v.size()>0) _dev=*(v[0]);
      //_dev.print(std::cout);

      // This must be reset when event has been processed completely
      _validEvent=false;
      break;
    }

    case RcdHeader::bunchTrain: {
      SubAccessor accessor(r);
      std::vector<const IlcBunchTrain*> 
	v(accessor.access<IlcBunchTrain>());
      if(v.size()>0) _ibt=*(v[0]);
      //_ibt.print(std::cout);

      // This must be reset when bunch train has been processed completely
      _validBunchTrain=false;
      break;
    }

    default: {
      break;
    }
    };
  }

  std::ostream& print(std::ostream &o, std::string s) const {
    o << s << "HstFlags::print()" << std::endl;
    if(_ilcTiming) {
      _irs.print(o,s+" ") << std::endl;
      _ics.print(o,s+" ") << std::endl;
      _ibt.print(o,s+" ") << std::endl;
    } else {
      _drs.print(o,s+" ") << std::endl;
      _dcs.print(o,s+" ") << std::endl;
      _das.print(o,s+" ") << std::endl;
      _dev.print(o,s+" ") << std::endl;
    }
    return o;
  }

  // Keep all data public for access speed
  bool _inJob,_inRun,_inConfiguration,_inAcquisition,_inBunchTrain;
  bool _validRun,_validConfiguration,_validAcquisition,
			   _validEvent,_validBunchTrain;

  Level _resetLevel;
  bool _resetNow;

  bool _ilcTiming;

  DaqRunStart           _drs;
  DaqConfigurationStart _dcs;
  DaqAcquisitionStart   _das;
  DaqEvent              _dev;

  IlcRunStart           _irs;
  IlcConfigurationStart _ics;
  IlcBunchTrain         _ibt;

  HstArray<10000> _timers[RcdHeader::endOfRecordTypeEnum];
};

#endif
