#include "runnerDefine.icc"

#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include <iostream>

#include "UtlArguments.hh"
#include "RcdArena.hh"
#include "RcdIoSkt.hh"

#include "RcdMultiUserRW.hh"
#include "RunLock.hh"

#ifdef TRG_PCI
#include "TrgReadout.hh"
#endif

using namespace std;


int main(int argc, const char **argv) {
  
  unsigned eTime(CALICE_DAQ_TIME);
  cout << argv[0] << " compiled at " << ctime((const time_t*)&eTime);

#ifndef TRG_PCI
  std::cout << argv[0] << " not compiled for use; exiting" << std::endl;

#else
  
  UtlArguments argh(argc,argv);
  
  const unsigned pciCard(argh.optionArgument('n',0,"PCI card"));
  
  if(argh.help()) return 0;
  
  RunLock lock(argv[0]);

  cout << "PCI card set to " << pciCard << endl;
  
  RcdIoSkt s;
  //assert(s.open(1124+pciCard));
  assert(s.open(1126));

  RcdMultiUserRW vRrw;

  // Add trigger readout module
  TrgReadout trgr(TRG_PCI,TRG_CRATE,TRG_SLOT);
  vRrw.addUser(trgr);

  RcdArena r;
  r.initialise(RcdHeader::startUp);
  vRrw.printLevel(9);
  
  RcdArena ahead;
  ahead.initialise(RcdHeader::event);

#ifdef SKT_READ_AHEAD
  bool inSpill(false),inTransfer(false);
  bool eventAhead(false);
#endif
  
  UtlPack tid;
  tid.halfWord(1,SubHeader::trg);
  tid.byte(2,0xff);

  while(r.recordType()!=RcdHeader::shutDown) {
    
    // Read next record from socket while timing the wait
    DaqTwoTimer tt;
    tt.timerId(tid.word());

    assert(s.read(r));

    tt.setEndTime();
    SubInserter inserter(r);
    inserter.insert<DaqTwoTimer>(tt);
    
    // Check for startUp record
    if(r.recordType()==RcdHeader::startUp) {
      
      // Put in software information
      SubInserter inserter(r);
      DaqSoftware *dsw(inserter.insert<DaqSoftware>(true));
      dsw->message(argh.command());
      dsw->setVersions();
      dsw->print(std::cout);
    }
    
    // Check for shutDown record
    if(r.recordType()==RcdHeader::shutDown) {
      vRrw.printLevel(9);
    }

    // Check for runStart record
    if(r.recordType()==RcdHeader::runStart) {

      // Access the DaqRunStart to get print level
      SubAccessor accessor(r);
      std::vector<const DaqRunStart*> v(accessor.extract<DaqRunStart>());
      if(v.size()>0)  vRrw.printLevel(v[0]->runType().printLevel());
    }

    // Check for spillStart/End records
#ifdef SKT_READ_AHEAD
    if(r.recordType()==RcdHeader::spillStart) inSpill=true;
    if(r.recordType()==RcdHeader::spillEnd)   inSpill=false;

    // Check for transferStart/End records
    if(r.recordType()==RcdHeader::transferStart) inTransfer=true;
    if(r.recordType()==RcdHeader::transferEnd)   inTransfer=false;

    // Check if have an event already read
    if(eventAhead) {
      if(r.recordType()!=RcdHeader::event) {
	if(r.recordType()!=RcdHeader::transferEnd) {
	  std::cerr << "Event read ahead but next record is not event" << std::endl;
	  r.RcdHeader::print(std::cerr," AHEAD ") << std::endl;
	}
      } else {
	r.extend(ahead.numberOfWords(),ahead.data());	
      }
    }
#endif

    // Process record and send back
    assert(vRrw.record(r));
    assert(s.write(r));

    // Check for possibility of readahead, i.e. predict next is event
#ifdef SKT_READ_AHEAD
    eventAhead=false;
    if((!inSpill && r.recordType()==RcdHeader::trigger) || inTransfer) {
      eventAhead=true;
      ahead.initialise(RcdHeader::event);
      assert(vRrw.record(ahead));
    }
#endif
  }

  assert(s.close());

#endif
  return 0;
}
