#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"

#include "RunLock.hh"

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

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

#include "RunRunner.hh"

#ifndef DAQ_ILC_TIMING

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

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

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

#include "TrgConfiguration.hh"
#include "BmlLc1176Configuration.hh"
#include "BmlCaen767Configuration.hh"

#ifdef EMC_CFG
#include "EmcConfiguration.hh"
#endif

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

#ifdef SCE_CFG
#include "SceConfiguration.hh"
#endif

#ifdef SCE_PCI
#include "AhcReadout.hh" // Identical to AHCAL
#endif

#ifdef AHC_CFG
#include "AhcConfiguration.hh"
#endif

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

#ifdef DHC_CFG
#include "DhcConfiguration.hh"
#endif

#ifdef DHC_PCI
#include "TtmReadout.hh"
#include "DhcReadout.hh"
#endif

#ifdef DHE_CFG
#include "DheConfiguration.hh"
#endif

#ifdef DHE_PCI
#include "DheReadout.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

#ifdef BML_LALHODO_SKT
#include "BmlLalHodoscopeConfiguration.hh"
#include "BmlLalHodoscopeReadout.hh"
#endif

#ifdef EMC_STAGE_SKT
#include "EmcStageReadout.hh"
#endif
#include "AhcSlowReadout.hh"

#ifdef ELOG_MODULE
#include "DaqElogger.hh"
#endif




#else 

#include "IlcConfiguration.hh"
#include "IlcReadout.hh"
#include "DaqGeneric.hh"

#ifdef MPS_LOCATION
#include "MpsConfiguration.hh"
#include "MpsReadout.hh"
#endif
#ifdef MPS_LASER_SKT
#include "MpsLaserReadout.hh"
#endif


#endif

// daquser/inc/hst
#include "HstGeneric.hh"
//#include "HstCrcNoiseShm.hh"
//#include "HstCaen767Shm.hh"
//#include "HstBeTrgHistoryShm.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   CALICE_DAQ_TIME = " << ctime((const time_t*)&eTime);
  cout << argv[0] << " compiled with CALICE_DAQ_SIZE = " << CALICE_DAQ_SIZE <<std::endl;

  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;

#ifndef DAQ_ILC_TIMING
  // 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, SCECAL and HCAL configuration modules
#ifdef EMC_CFG
  EmcConfiguration ec;
#ifdef TRG_CRATE
#ifdef TRG_SLOT
  if(TRG_CRATE==0xec && TRG_SLOT!=0) ec.trgSlot(TRG_SLOT);
#endif
#endif
  vRrw.addUser(ec);
#endif

#ifdef SCE_CFG
  SceConfiguration ce;
#ifdef TRG_CRATE
#ifdef TRG_SLOT
  if(TRG_CRATE==0xce && TRG_SLOT!=0) ce.trgSlot(TRG_SLOT);
#endif
#endif
  vRrw.addUser(ce);
#endif

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

#ifdef DHC_CFG
  DhcConfiguration dh(0xdc);
  vRrw.addUser(dh);
#ifdef TTM_SLOT
  if (TTM_SLOT!=0) dh.trgSlot(TTM_SLOT);
#endif
#endif

#ifdef DHE_CFG
  DheConfiguration de;
#ifdef TRG_CRATE
#ifdef TRG_SLOT
  if(TRG_CRATE==0xde && TRG_SLOT!=0) de.trgSlot(TRG_SLOT);
#endif
#endif
  vRrw.addUser(de);
#endif

  // 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

#ifdef BML_LALHODO_SKT
  BmlLalHodoscopeConfiguration lhc;
  vRrw.addUser(lhc);
#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, SCECAL and HCAL 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 SCE_PCI
  AhcReadout cr(SCE_PCI,0xce);
  vRrw.addUser(cr);
#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 DHC_PCI
  DhcReadout hr(DHC_PCI,0xdc);
  vRrw.addUser(hr);
#ifdef TTM_SLOT
  TtmReadout tr(DHC_PCI,0xdc,TTM_SLOT);
  vRrw.addUser(tr);
#endif
#endif

#ifdef DHE_PCI
  DheReadout dher(DHE_PCI,0xde);
  vRrw.addUser(dher);
#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 CERN 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 BML_LALHODO_SKT
  BmlLalHodoscopeReadout lhr(BML_LALHODO_SKT);
  vRrw.addUser(lhr); 
#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 elog module
#ifdef ELOG_MODULE
  DaqElogger el;
  vrub.addUser(el);
#endif


  //////////////////////////// ILC_TIMING ////////////////////////



#else
  // Add DAQ counter module
  IlcConfiguration dc;
  vRrw.addUser(dc);
  theRunner.daqConfiguration(&dc);
 
#ifdef MPS_LOCATION
  // Add MAPS configuration module
  MpsConfiguration trgc(MPS_LOCATION);
  vRrw.addUser(trgc);
 
  // Add MAPS readout module
  MpsReadout   trgr(MPS_LOCATION);
  vRrw.addUser(trgr);
#endif
 
#ifdef MPS_LASER_SKT
  MpsLaserReadout mlrd(MPS_LASER_SKT);
  vRrw.addUser(mlrd);
#endif

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

  // Add online analysis module
  DaqGeneric dg;
  vrub.addUser(dg);
#endif

  // 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
  UtlPack hstBits(HstGeneric::defaultHstGenericBits);
#ifdef BML_LC1176_PCI
  hstBits.bit(11,true);
  hstBits.bit(12,false);
#endif
#ifdef BML_CAEN767_PCI
  hstBits.bit(11,false);
  hstBits.bit(12,true);
#endif
#ifdef DAQ_ILC_TIMING
  hstBits.bit(11,false);
  hstBits.bit(12,false);
  hstBits.bit(31,true);
#endif
  HstGeneric hgn(hstBits);
  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;
}
