#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 "EmcReadout.hh"
#include "AhcReadout.hh"
#include "BmlLc1176Readout.hh"
#include "BmlCaen767Readout.hh"

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

#define SKT_READ_AHEAD

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);
  
  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(1124));

  // Set up list of modules
  RcdMultiUserRW vRrw;

  // Add ECAL and AHCAL readout modules
#ifdef EMC_PCI
  EmcReadout er(EMC_PCI);
  vRrw.addUser(er);
#endif

#ifdef AHC_PCI
  AhcReadout ar(AHC_PCI);
  vRrw.addUser(ar);
#endif

  // Add TDC modules
#ifdef BML_LC1176_PCI
  BmlLc1176Readout bl(BML_LC1176_PCI,BML_LC1176_ADDRESS);
  vRrw.addUser(bl);
#endif

#ifdef BML_CAEN767_PCI
  BmlCaen767Readout cl(BML_CAEN767_PCI,BML_CAEN767_CRATE,
		       BML_CAEN767_ADDRESS0,BML_CAEN767_ADDRESS1);
  vRrw.addUser(cl);
#endif

  // Define memory for records
  RcdArena r;
  r.initialise(RcdHeader::startUp);
  
  // Set initial print level;
  vRrw.printLevel(9);

#ifdef SKT_READ_AHEAD

  RcdArena ahead;
  ahead.initialise(RcdHeader::event);
  bool inSpill(false),eventAhead(false);
  unsigned nTrg(0),nEvt(0);

#endif

  // Define the id label for the timer
  UtlPack tid;
  tid.halfWord(1,SubHeader::crc);
#ifdef EMC_PCI
  tid.halfWord(1,SubHeader::emc);
#endif
#ifdef AHC_PCI
  tid.halfWord(1,SubHeader::ahc);
#endif
  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) {

      // Reset print level
      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());
    }

#ifndef SKT_READ_AHEAD

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

#else

    // Zero trigger and event counters in acquisition start
    if(r.recordType()==RcdHeader::acquisitionStart) {
      nTrg=0;
      nEvt=0;
    }

    // Check for mismatch of counters
    if(r.recordType()==RcdHeader::acquisitionEnd) {
      if(nTrg!=nEvt) {
	std::cout << "sktReadout counter mismatch, nTrg = "
		  << nTrg << ", nEvt = " << nEvt << std::endl;
	r.RcdHeader::print(std::cerr," SKT ") << std::endl;
      }
    }

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

    // Increment counter
    if(r.recordType()==RcdHeader::trigger) nTrg++;

    // Process record and send back
    if(r.recordType()==RcdHeader::event && eventAhead) {
      eventAhead=false;
      r.extend(ahead.numberOfWords(),ahead.data());
    } else {
      if(r.recordType()==RcdHeader::event) nEvt++;
      assert(vRrw.record(r));
    }
    assert(s.write(r));

    // Check for possibility of readahead
    if(!eventAhead && !inSpill && nTrg>nEvt) {
      eventAhead=true;
      nEvt++;
      ahead.initialise(RcdHeader::event);
      assert(vRrw.record(ahead));
    }

#endif

  }

  assert(s.close());

  return 0;
}
