#ifndef RunSequencer_HH
#define RunSequencer_HH

#include <sstream>
#include <fstream>
#include <iostream>

#include "DaqRunType.hh"
#include "DaqRunStart.hh"
#include "DaqConfigurationStart.hh"
#include "DaqAcquisitionStart.hh"

#include "DaqConfiguration.hh"
#include "DaqReadout.hh"
#include "RcdMultiUserRW.hh"
#include "RcdMultiUserRO.hh"

#include "SubAccessor.hh"

#include "DaqRunStart.hh"


class RunSequencer {

public:
  RunSequencer(DaqConfiguration &dc, DaqReadout &dr) :
    _continuationFlag(999), _daqConfiguration(dc), _daqReadout(dr),
    _shmRunControl(RunControl::shmKey), _runControl(0) {

    // Connect to run control shared memory
    _runControl=shmRunControl.payload();
    assert(_runControl!=0);
    _runControl->runRegister();
    _runControl->reset();
  }
  
  virtual ~RunSequencer() {
  }

  unsigned continuationFlag() const {
    return _continuationFlag;
  }

  void continuationFlag(unsigned c) {
    _continuationFlag=c;
  }

  bool run(RcdMultiUserRW rw, RcdMultiUserRO ro) {
    _continuationFlag=999;

    // Define record memory
    RcdArena arena;
    SubAccessor accessor(arena);

    // Loop over sequences; there is no maximum
    for(unsigned iSeq(0);_continuationFlag>0;iSeq++) {

      // Get run information from shared memory
      _runControl->print(std::cout,"NEXT SEQUENCE ==>> ");
      unsigned nRun(_runControl->numberOfRuns());

      DaqSequenceStart dss(_runControl->sequenceStart());

      // TEMP!!! SHOULD BE DONE IN EACH MODULE
      rw.printLevel(dss.sequenceType.printLevel());
      ro.printLevel(dss.sequenceType.printLevel());
      
      // Reset to default (slow monitoring) after copying contents
      _runControl->reset();
    
      // Set up the sequenceStart record
      arena.initialise(RcdHeader::sequenceStart);
      rw.record(arena);
      ro.record(arena);

      // Get the sequencer ready
      RunSequence rs(dss.sequenceType());

      unsigned lastSlowRecord(0);
    
      // Loop over runs
      for(unsigned iRun(0);(iRun<nRun || nRun==0) && _continuationFlag>1;iRun++) {
	
	// Set run type for DAQ configuration module
	_daqConfiguration.runStart(rs.nextRunType());
	
	// Set up the runStart
	arena.initialise(RcdHeader::runStart);
	rw.record(arena);
	ro.record(arena);
	
	/*
      // Access the DaqRunStart
      std::vector<const DaqRunStart*> v(accessor.extract<DaqRunStart>());
      assert(v.size()==1);
    
      // Get information on configurations and others
      const unsigned nCfg(v[0]->maximumNumberOfConfigurationsInRun());

      const unsigned nAcqR(v[0]->maximumNumberOfAcquisitionsInRun());
      unsigned iAcqR(0);

      const unsigned nEvtR(v[0]->maximumNumberOfEventsInRun());
      unsigned iEvtR(0);

      // If last configuration was aborted, allow next
      if(_continuationFlag==1) _continuationFlag=999;
      //while(_continuationFlag==1) sleep(1); //???

      // Loop over configurations
      for(unsigned iCfg(0);iCfg<nCfg && _continuationFlag>1;iCfg++) {
	arena.initialise(RcdHeader::configurationStart);
	rw.record(arena);
	ro.record(arena);
      
	// Access the DaqConfigurationStart
	std::vector<const DaqConfigurationStart*> v(accessor.extract<DaqConfigurationStart>());
	assert(v.size()==1);

	// Get information on acquisitions
	const unsigned nAcq(v[0]->maximumNumberOfAcquisitionsInConfiguration());
	const double slwDt(v[0]->minimumTimeBeforeSlowReadout().deltaTime());
	
	// If last acquisition was aborted, allow next
	if(_continuationFlag==2) _continuationFlag=999;
	//while(_continuationFlag==2) sleep(1); //???
      
	// If slow run, then no events
	if(runType.majorType()==DaqRunType::slow) {

	  // Loop over slow readouts
	  for(unsigned iSlw(0);_continuationFlag>2;iSlw++) {
	    arena.initialise(RcdHeader::slowReadout);
	    rw.record(arena);
	    ro.record(arena);
	    
	    // Sleep between readouts
	    sleep((int)slwDt);
	  }

        // Standard run
	} else {

	  // Loop over acquisitions
	  for(unsigned iAcq(0);iAcq<nAcq && iAcqR<nAcqR && _continuationFlag>2;iAcq++) {
	    iAcqR++;

	    UtlTime start;
	    start.update();
	    if(runType.printLevel()>12) std::cout << "About to do acquisition "
				       << std::setw(6) << iAcq
				       << " with mode = " << printHex(runType.switches()) << std::endl << std::endl;
	    
	    arena.initialise(RcdHeader::acquisitionStart);
	    rw.record(arena);
	    ro.record(arena);
	    
	    // Access the DaqAcquisitionStart
	    std::vector<const DaqAcquisitionStart*> v(accessor.extract<DaqAcquisitionStart>());
	    assert(v.size()==1);

	    // Get information on events
	    const unsigned nEvt(v[0]->maximumNumberOfEventsInAcquisition());
	    unsigned iEvt(0);

	    // If last event/spill was aborted, allow next
	    if(_continuationFlag==3) _continuationFlag=999;
	    //while(_continuationFlag==3) sleep(1); //???
        
	    // Switch depending on spill state machine mode; buffer mode
	    if(runType.bufferRun()) {

	      // Switch if spill mode
	      if(runType.spillRun()) {
		arena.initialise(RcdHeader::spill);
		rw.record(arena);
		ro.record(arena);
		
		// Access the DaqSpillEnd
		std::vector<const DaqSpillEnd*> v(accessor.extract<DaqSpillEnd>());
		assert(v.size()==1);

		// Get information on events
		iEvt=v[0]->actualNumberOfEventsInSpill();
		iEvtR+=iEvt;

	      // PreEvent mode
	      } else {
		arena.initialise(RcdHeader::spillStart);
		rw.record(arena);
		ro.record(arena);
		
		// Loop over events
		for(iEvt=0;iEvt<nEvt && iEvtR<nEvtR && _continuationFlag>3;iEvt++) {
		  iEvtR++;

		  arena.initialise(RcdHeader::preEvent);
		  rw.record(arena);
		  ro.record(arena);
		}

		arena.initialise(RcdHeader::spillEnd);
		rw.record(arena);
		ro.record(arena);
	      }
          
	      // Switch if transfer mode
	      if(runType.transferRun()) {
		arena.initialise(RcdHeader::transfer);
		rw.record(arena);
		ro.record(arena);
		
	      // PostEvent run
	      } else {
		arena.initialise(RcdHeader::transferStart);
		rw.record(arena);
		ro.record(arena);
            
		for(unsigned jEvt(0);jEvt<iEvt;jEvt++) {
		  arena.initialise(RcdHeader::postEvent);
		  rw.record(arena);
		  ro.record(arena);
		}
            
		arena.initialise(RcdHeader::transferEnd);
		rw.record(arena);
		ro.record(arena);
	      }

	    // Single event mode
	    } else {

	      // Loop over events
	      for(iEvt=0;iEvt<nEvt && iEvtR<nEvtR && _continuationFlag>3;iEvt++) {
		iEvtR++;

		arena.initialise(RcdHeader::trigger);
		rw.record(arena);
		ro.record(arena);

		arena.initialise(RcdHeader::event);
		rw.record(arena);
		ro.record(arena);
	      }      
	    }

	    // Do acquisition end
	    arena.initialise(RcdHeader::acquisitionEnd);
	    rw.record(arena);
	    ro.record(arena);

	    UtlTime finish;
	    finish.update();
	    if(runType.printLevel()>12) std::cout << " Done " << iEvt << " events in "
				       << (finish-start).deltaTime() << " sec, "
				       << iEvt/(finish-start).deltaTime()
				       << " events/sec, "
				       << (finish-start).deltaTime()/iEvt 
				       << " secs/event" << std::endl << std::endl;
	    
	    // Do a slow readout if enough time since last
	    if((time(0)-lastSlowRecord)>=slwDt) {
	      arena.initialise(RcdHeader::slowReadout);
	      rw.record(arena);
	      ro.record(arena);
	      
	      lastSlowRecord=arena.recordTime().seconds();
	    }
	  }
	}

	arena.initialise(RcdHeader::configurationEnd);
	rw.record(arena);
	ro.record(arena);
      }
	*/

	arena.initialise(RcdHeader::runEnd);
	rw.record(arena);
	ro.record(arena);
	
	// Ensure if run aborted that slow files have different timestamps
	sleep(1);
      }
      
      // Set up the sequenceEnd record
      arena.initialise(RcdHeader::sequenceEnd);
      rw.record(arena);
      ro.record(arena);
    }
    
    return true;
  }


private:
  volatile unsigned _continuationFlag;
  DaqConfiguration &_daqConfiguration;
  DaqReadout &_daqReadout;
  ShmObject<RunControl> _shmRunControl;
  const RunControl* const _runControl;
};

#endif
