#include <limits>

#include "km3net-dataformat/offline/Head.hh"
#include "km3net-dataformat/offline/MultiHead.hh"
#include "km3net-dataformat/offline/Evt.hh"

#include "JDAQ/JDAQEventIO.hh"
#include "JDAQ/JDAQTimesliceIO.hh"
#include "JDAQ/JDAQSummarysliceIO.hh"
#include "JDAQ/JDAQEvaluator.hh"

#include "JTrigger/JTriggerParameters.hh"

#include "JDetector/JModuleRouter.hh"
#include "JDetector/JDetectorToolkit.hh"// <- to enable: load 
#include "JDetector/JModuleRouter.hh"

#include "JDynamics/JDynamics.hh"

#include "JSupport/JSupport.hh"
#include "JSupport/JSingleFileScanner.hh"
#include "JSupport/JTreeScanner.hh"
#include "JSupport/JFileRecorder.hh" 
#include "JSupport/JMeta.hh"

#include "Jeep/JParser.hh" 
#include "Jeep/JMessage.hh"

#include "JReconstruction/JEvt.hh"
#include "JReconstruction/JShowerPrefit.hh"
#include "JReconstruction/JShowerPrefitParameters_t.hh"

#include "JLang/JSharedPointer.hh"

/**
 * \author adomi
 */

int main(int argc, char **argv)
{
  using namespace std;
  using namespace JPP;
  using namespace KM3NETDAQ;
  
  typedef JTYPELIST<JRemove<JAllTypes_t, JDAQTimesliceTypes_t>::typelist, JFIT::JEvt>::typelist  typelist;
  typedef JTYPELIST<JCOMPASS::JOrientation,
		    JACOUSTICS::JEvt>::typelist                    calibration_types;
  typedef JMultipleFileScanner<calibration_types>                  JCalibration_t;

  JSingleFileScanner<JDAQEvent>  inputFile;
  JFileRecorder<typelist>        outputFile;
  JLimit_t&                      numberOfEvents = inputFile.getLimit();
  string                         detectorFile;
  JCalibration_t                 calibrationFile;
  double                         Tmax_s;
  JShowerPrefitParameters_t      parameters;
  int                            debug;

  try { 

    JParser<> zap("Program to perform position pre-fit of shower to data.");
    
    zap['f'] = make_field(inputFile);
    zap['o'] = make_field(outputFile)          = "ShowerPrefit.root";
    zap['a'] = make_field(detectorFile);
    zap['+'] = make_field(calibrationFile)     = JPARSER::initialised();
    zap['T'] = make_field(Tmax_s)              = 100.0;
    zap['@'] = make_field(parameters)          = JPARSER::initialised();
    zap['n'] = make_field(numberOfEvents)      = JLimit::max();
    zap['d'] = make_field(debug)               = 2;
    
    zap(argc, argv);
  }
  catch(const exception& error) {
    FATAL(error.what() << endl);
  }
  
  
  JDetector detector;

  try {
    load(detectorFile, detector);
  }
  catch(const JException& error) {
    FATAL(error);
  }

  unique_ptr<JDynamics> dynamics;
 
  try {
    
    dynamics.reset(new JDynamics(detector, Tmax_s));
    
    dynamics->load(calibrationFile);
  }
  catch(const exception& error) {
    if (!calibrationFile.empty()) {
      FATAL(error.what());
    }
  }
  
  const JModuleRouter router(dynamics ? dynamics->getDetector() : detector);

  JShowerPrefit fit(parameters, router, debug);
  
  outputFile.open();
  outputFile.put(JMeta(argc, argv));

  JTreeScanner<JDAQEvent, JDAQEvaluator> in (inputFile, inputFile.getLimit());
  
  while (in.hasNext()) {

    STATUS("event: " << setw(10) << in.getCounter() << '\r'); DEBUG(endl);

    const JDAQEvent* tev   = in.next();
    
    JFIT::JEvt out = fit(*tev);

    if (dynamics) { 
 
      const JDynamics::coverage_type coverage = dynamics->getCoverage();
 
      for (JFIT::JEvt::iterator i = out.begin(); i != out.end(); ++i) {
	i->setW(JPP_COVERAGE_ORIENTATION, coverage.orientation);
	i->setW(JPP_COVERAGE_POSITION,    coverage.position);
      }
    }

    if (parameters.numberOfPrefits > 0) {
 
      // apply default sorter
      
      JFIT::JEvt::iterator __end = out.end();

      if (parameters.numberOfPrefits < out.size()) {

	advance(__end = out.begin(), parameters.numberOfPrefits);

	partial_sort(out.begin(), __end, out.end(), qualitySorter);

	out.erase(__end, out.end());
      
      } else {
	
	sort(out.begin(), __end, qualitySorter);
      }

    } else {
      
      sort(out.begin(), out.end(), qualitySorter);
    }
    
    outputFile.put(*tev);
    outputFile.put(out);    

  }

  STATUS(endl);
  
  JSingleFileScanner<JRemove<typelist, JTYPELIST<JFIT::JEvt, JDAQEvent>::typelist>::typelist> io(inputFile);

  io >> outputFile;

  outputFile.close();
}