#include <string>
#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 "JDetector/JModuleRouter.hh"
#include "JDetector/JDetectorToolkit.hh"

#include "JDynamics/JDynamics.hh"

#include "JSupport/JParallelFileScanner.hh"
#include "JSupport/JSupport.hh"
#include "JSupport/JFileRecorder.hh"
#include "JSupport/JMeta.hh"
#include "JSupport/JTreeScanner.hh"
#include "JSupport/JSummaryFileRouter.hh"

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

#include "JReconstruction/JEvt.hh"
#include "JReconstruction/JShowerPositionFitParameters_t.hh"
#include "JReconstruction/JShowerPositionFit.hh"

#include "JLang/JSharedPointer.hh"


/**
 * \file
 *
 * Program to perform EM Shower Second Position Fit for ORCA with I/O of JFIT::JEvt data.
 * The reconstruction is made at the HIT level.
 * \author adomi
 */

int main(int argc, char** argv){

  using namespace std;
  using namespace JPP;
  
  typedef JTYPELIST<JAllTypes_t, JFIT::JEvt>::typelist             typelist;
  typedef JParallelFileScanner< JTypeList<JDAQEvent, JFIT::JEvt> > JParallelFileScanner_t;
  typedef JParallelFileScanner_t::multi_pointer_type               multi_pointer_type;
  typedef JTYPELIST<JCOMPASS::JOrientation,
		    JACOUSTICS::JEvt>::typelist                    calibration_types;
  typedef JMultipleFileScanner<calibration_types>                  JCalibration_t;

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

   try { 

    JParser<> zap;
   
    zap['f'] = make_field(inputFile)           ;
    zap['o'] = make_field(outputFile)          = "JShowerPositionFit.root";
    zap['a'] = make_field(detectorFile)        ;
    zap['+'] = make_field(calibrationFile)     = JPARSER::initialised();
    zap['T'] = make_field(Tmax_s)              = 100.0;
    zap['F'] = make_field(pdfFile)             ;
    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);
  
  outputFile.open();
  outputFile.put(JMeta(argc, argv));

  JSummaryFileRouter  summary(inputFile, parameters.R_Hz);
  
  JShowerPositionFit fit(parameters, router, summary, pdfFile, debug);

  while (inputFile.hasNext()) {

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

    multi_pointer_type ps  = inputFile.next();

    JDAQEvent*         tev = ps;
    JFIT::JEvt*        in  = ps;
   
    summary.update(*tev);

    if (dynamics) { 
      dynamics->update(*tev);
    }
    
    // select start values
    JFIT::JEvt cp  = *in;

    cp.select(parameters.numberOfPrefits, qualitySorter);

    if (!cp.empty()) {
      cp.select(JHistory::is_event(cp.begin()->getHistory()));
    }
    
    // fit
    JFIT::JEvt out = fit(*tev, cp);

    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);
      }
    }
   
    // apply default sorter                                                                                 

    sort(out.begin(), out.end(), qualitySorter);

    // reduce output fits                                                                                  

    JFIT::JEvt::iterator __end = out.end();

    advance(__end = out.begin(), min(parameters.numberOfOutfits, (size_t) distance(out.begin(), out.end())));

    JFIT::JEvt reduced_out;

    copy(out.begin(), __end, back_inserter(reduced_out));

    copy(in->begin(), in->end(), back_inserter(reduced_out));

    outputFile.put(reduced_out);
  
  }
  STATUS(endl);
  
  JSingleFileScanner<JRemove<typelist, JFIT::JEvt>::typelist> io(inputFile);

  io >> outputFile;

  outputFile.close();
}