#include <string>
#include <iostream>
#include <iomanip>
#include <memory>

#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/JDetector.hh"
#include "JDetector/JDetectorToolkit.hh"
#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 "JReconstruction/JEvt.hh"
#include "JReconstruction/JMuonPrefit.hh"
#include "JReconstruction/JMuonPrefitParameters_t.hh"

#include "JLang/JPredicate.hh"
#include "JLang/JComparison.hh"

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


/**
 * \file
 *
 * Program to perform linear fits JFIT::JEstimator<JFIT::JLine1Z> covering the solid angle producing JFIT::JEvt data.
 *
 * \author mdejong, gmaggi, azegarelli
 */
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 JSingleFileScanner<JDAQEvent>                                                          JSingleFileScanner_t;
  typedef JTYPELIST<JCOMPASS::JOrientation,
		    JACOUSTICS::JEvt>::typelist                    calibration_types;
  typedef JMultipleFileScanner<calibration_types>                  JCalibration_t;

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

  try { 

    JParser<> zap("Program to perform pre-fit of muon trajectory to data.");
    
    zap['f'] = make_field(inputFile);
    zap['o'] = make_field(outputFile)          = "prefit.root";
    zap['a'] = make_field(detectorFile);
    zap['+'] = make_field(calibrationFile)     = JPARSER::initialised();
    zap['T'] = make_field(Tmax_s)              = 100.0;
    zap['n'] = make_field(numberOfEvents)      = JLimit::max();
    zap['@'] = make_field(parameters)          = JPARSER::initialised();
    zap['d'] = make_field(debug)               = 1;

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

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

    if (dynamics) {
      dynamics->update(*tev);
    }

    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);
      }
    }

    // apply default sorter

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

}