#include "runnerDefine.icc"

#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>

#include <iostream>
#include <sstream>
#include <vector>
#include <cstdio>

// records/inc/utl
#include "UtlTime.hh"
#include "UtlArguments.hh"

// records/inc/rcd
#include "RcdArena.hh"
#include "RunWriterSlow.hh"
#include "RunWriterData.hh"
#include "RunWriterDataMultiFile.hh"
#include "RcdMultiUserRW.hh"
#include "RcdMultiUserRO.hh"

// records/inc/sub
#include "SubAccessor.hh"

// records/inc/daq
#include "DaqRunStart.hh"
#include "DaqConfigurationStart.hh"
#include "DaqAcquisitionStart.hh"
#include "DaqSoftware.hh"

#include "RunRunner.hh"

// records/inc/crc
#include "CrcLocationData.hh"
#include "CrcReadoutConfigurationData.hh"

// daquser/inc/hst
#include "HstGeneric.hh"
#include "HstCrcNoiseShm.hh"
#include "HstCaen767Shm.hh"
#include "HstBeTrgHistoryShm.hh"

// online/inc/daq
#include "DaqConfiguration.hh"
#include "DaqReadout.hh"

#include "TrgConfiguration.hh"
#include "EmcConfiguration.hh"
#include "AhcConfiguration.hh"
#include "DaqConfiguration.hh"
#include "BmlLc1176Configuration.hh"
#include "BmlCaen767Configuration.hh"

#ifdef EMC_PCI
#include "EmcReadout.hh"
#endif

#ifdef AHC_PCI
#include "AhcReadout.hh"
#endif

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

#ifdef BML_LC1176_PCI
#include "BmlLc1176Readout.hh"
#endif

#ifdef BML_CAEN767_PCI
#include "BmlCaen767Readout.hh"
#endif

#include "EmcStageReadout.hh"
#include "AhcSlowReadout.hh"

#include "RunLock.hh"

#include "RcdSktUserWO.hh"
#include "RcdSktUserRW.hh"
#include "RcdSkt2UserRW.hh"
#include "RunControl.hh"
#include "RunMonitor.hh"
#include "ShmObject.hh"


using namespace std;


bool continueLoop;
RunRunner theRunner;

void signalHandler(int signal) {
  time_t t(time(0));
  pid_t p(getpid());
  //std::cerr << "Process " << p << " received signal "
  //    << signal << " at " << ctime(&t);
  std::cout << "Process " << p << " received signal "
	    << signal << " at " << ctime(&t);

  if(signal==SIGTERM) {
    continueLoop=false;
    theRunner.continuationFlag(0);
  }

  if(signal==SIGINT ) theRunner.continuationFlag(0);
  if(signal==SIGUSR1) theRunner.continuationFlag(1);
  if(signal==SIGUSR2) theRunner.continuationFlag(2);
}


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);
  cout << "Command = " << argh.command() << endl << endl;

  const unsigned printLevel(argh.optionArgument('p',9,"Print level"));
  if(argh.help()) return 0;

  /*
  const bool useWriteDmy(argh.option('w',"Dummy output file"));
  const bool useWriteAsc(argh.option('a',"Ascii output file"));
  const bool doHistograms(argh.option('s',"Display histograms"));
  
  const unsigned version(argh.optionArgument('v',0,"Run type version"));
  
  if(argh.help()) return 0;

  cout << "Command = " << argh.command() << endl << endl;
  
  if(doHistograms)  cout << "Histograms display selected" << endl;
  else              cout << "Histograms display not selected" << endl;
  if(useWriteDmy)   cout << "Dummy output selected" << endl;
  else {
    if(useWriteAsc) cout << "Ascii output selected" << endl;
    else            cout << "Binary output selected" << endl;
  }
  
  cout << "Print level set to " << printLevel << endl;
  cout << "Run type version set to " << version << endl;
  cout << endl;
  */

  // Create runner.lock file to prevent multiple runners being executed
  RunLock locker(argv[0]);

  // Set this immediately so any signals will cause clean exit
  continueLoop=true;

  // Catch signals
  signal(SIGTERM,signalHandler);
  signal(SIGINT ,signalHandler);
  signal(SIGUSR1,signalHandler);
  signal(SIGUSR2,signalHandler);
  
  // Connect to run control shared memory
  ShmObject<RunControl> shmRunControl(RunControl::shmKey);
  RunControl *pRc(shmRunControl.payload());
  assert(pRc!=0);
  pRc->runRegister();
  pRc->reset();

  // Define lists of user modules
  RcdMultiUserRW vRrw;
  RcdMultiUserRO vrub;
  
  // Add DAQ counter module
  DaqConfiguration dc;
  vRrw.addUser(dc);
  theRunner.daqConfiguration(&dc);

  // Add trigger configuration module  
#ifdef TRG_CRATE
#ifdef TRG_SLOT
  TrgConfiguration trgc(TRG_CRATE,TRG_SLOT);
  vRrw.addUser(trgc);
#endif
#endif

  // Add ECAL and AHCAL configuration modules
  EmcConfiguration ec;
#ifdef TRG_CRATE
#ifdef TRG_SLOT
  if(TRG_CRATE==0xec && TRG_SLOT!=0) ec.trgSlot(TRG_SLOT);
#endif
#endif
  vRrw.addUser(ec);

  AhcConfiguration ac;
#ifdef TRG_CRATE
#ifdef TRG_SLOT
  if(TRG_CRATE==0xac && TRG_SLOT!=0) ac.trgSlot(TRG_SLOT);
#endif
#endif
  vRrw.addUser(ac);

  // Add DESY tracker TDC configuration
#ifdef BML_LC1176_CRATE
  BmlLc1176Configuration bl;
  vRrw.addUser(bl);
#endif

  // Add CERN tracker TDC configuration
#ifdef BML_CAEN767_CRATE
  BmlCaen767Configuration bc(BML_CAEN767_CRATE);
  vRrw.addUser(bc);
#endif

  // Add trigger readout module
#ifdef TRG_PCI
  TrgReadout   trgr(TRG_PCI,TRG_CRATE,TRG_SLOT);
  vRrw.addUser(trgr);
#else
#ifdef TRG_SKT
  RcdSktUserRW trgr(TRG_SKT,1126,100);
  //RcdSktUserRW trgr("192.168.111.11",1126,100); // icalice01
  //RcdSktUserRW trgr(TRG_REMOTE,1126,100);
  vRrw.addUser(trgr);
#endif
#endif

  // Add ECAL and AHCAL readout modules
#ifdef EMC_PCI
  EmcReadout er(EMC_PCI);
  //CrcReadout er(1,0xec);
  vRrw.addUser(er);
#else
#ifdef EMC_SKT
#ifndef AHC_SKT
  RcdSktUserRW er(EMC_SKT,1124,100);
  //RcdSktUserRW er("192.168.111.11",1124,100); // icalice01
  //RcdSktUserRW er("131.169.184.163",1124,100); // flchcaldaq03
  vRrw.addUser(er);
#endif
#endif
#endif

#ifdef AHC_PCI
  AhcReadout ar(AHC_PCI);
  //CrcReadout ar(0,0xac);
  vRrw.addUser(ar); 
#else
#ifdef AHC_SKT
#ifndef EMC_SKT
  RcdSktUserRW ar(AHC_SKT,1124,100);
  vRrw.addUser(ar);
#endif
#endif
#endif

#ifdef EMC_SKT
#ifdef AHC_SKT
  //RcdSkt2UserRW cr(EMC_SKT,1124,AHC_SKT,1125,100);
  RcdSkt2UserRW cr(EMC_SKT,1124,AHC_SKT,1124,100);

  //RcdSkt2UserRW cr("localhost",1124,"localhost",1125,100);
  //RcdSkt2UserRW cr("192.168.111.11",1124,"192.168.111.11",1125,100); // icalice01
  cr.serial(false);
  vRrw.addUser(cr);
#endif
#endif
  
  // Add DESY tracker TDC readout
#ifdef BML_LC1176_PCI
  BmlLc1176Readout rl(BML_LC1176_PCI,BML_LC1176_ADDRESS);
  vRrw.addUser(rl); 
#endif

  // Add DESY tracker TDC readout
#ifdef BML_CAEN767_PCI
  BmlCaen767Readout rc(BML_CAEN767_PCI,BML_CAEN767_CRATE,
		       BML_CAEN767_ADDRESS0,BML_CAEN767_ADDRESS1);
  vRrw.addUser(rc); 
#endif

#ifdef EMC_STAGE_SKT
  EmcStageReadout esr(EMC_STAGE_SKT); 
  vRrw.addUser(esr); 
#endif

#ifdef AHC_STAGE_SKT
  AhcSlowReadout asr(AHC_STAGE_SKT); 
  vRrw.addUser(asr); 
#endif

  // DAQ module to stop run
  DaqReadout dr;
  vRrw.addUser(dr);

  // Add writer modules
  RunWriterSlow sw;
  vrub.addUser(sw);
  //RunWriterData dw;
  RunWriterDataMultiFile dw;
  vrub.addUser(dw);

  // Add histogram module
#ifdef HST_SKT
  /*
  HstCrcNoiseShm hcn;
  vrub.addUser(hcn);
  HstCaen767Shm hct;
  vrub.addUser(hct);
  HstBeTrgHistoryShm hcb;
  vrub.addUser(hcb);
  */

  RcdSktUserWO hgn(HST_SKT,1127,100);
  vrub.addUser(hgn);
#else
  HstGeneric hgn;
  vrub.addUser(hgn);
#endif

  // Add display modules
  RunMonitor rm;
  vrub.addUser(rm);

  // Set initial print level for startUp
  vRrw.printLevel(printLevel);
  vrub.printLevel(printLevel);

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

  // Initialise startup record
  arena.initialise(RcdHeader::startUp);

  // Put in software information
  DaqSoftware *dsw(inserter.insert<DaqSoftware>(true));
  dsw->message(argh.command());
  dsw->setVersions();
  dsw->print(std::cout);

  // Send startUp to modules on lists
  vRrw.record(arena);
  vrub.record(arena);

  /*
  // Loop over groups of runs
  while(continueLoop) {

    // Get run information from shared memory
    pRc->print(std::cout,"NEW RUN ==>> ");
    unsigned nr(pRc->numberOfRuns());
    //unsigned pl(pRc->printLevel());
    DaqRunStart rs(pRc->runStart());

    // Reset to default (slow monitoring) after copying contents
    pRc->reset();
    
    // Set run type for DAQ configuration module
    dc.runStart(rs);

    // Do the runs
    theRunner.run(nr,rs.runType(),vRrw,vrub);
  }
  */

  // Do the runs
  theRunner.run(vRrw,vrub);

  // Set final print level for shutDown
  vRrw.printLevel(printLevel);
  vrub.printLevel(printLevel);
  
  // Send shutDown record
  arena.initialise(RcdHeader::shutDown);
  vRrw.record(arena);
  vrub.record(arena);

  return 0;
}
