#include <iostream>
#include <iomanip>
#include <string>
#include <set>

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

#include "JLang/JException.hh"

#include "JAAnet/JAAnetToolkit.hh"
#include "JAAnet/JEvtWeightFactorFunction.hh"
#include "JAAnet/JAtmosphericNeutrinoFlux.hh"
#include "JAAnet/JFlux.hh"

#include "JSupport/JLimit.hh"
#include "JSupport/JMultipleFileScanner.hh"
#include "JSupport/JEvtWeightFileScannerSet.hh"

#include "JOscProb/JOscChannel.hh"
#include "JOscProb/JOscParameters.hh"
#include "JOscProb/JOscProbInterpolator.hh"

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


/**
 * \file
 * Example program for printing the atmospheric neutrino fluxes in a given neutrino MC-file.
 *
 * \author bjung
 */
int main(int argc, char **argv)
{
  using namespace std;
  using namespace JPP;

  typedef JEvtWeightFactorFunction<JAtmosphericNeutrinoFlux, JFlux>   JFluxFunction_t;


  JMultipleFileScanner_t               inputFiles;
  string                               oscProbTable;
  JOscParameters<>                     parameters;
  int                                  debug;

  try {

    JParser<> zap;
    
    zap['f'] = make_field(inputFiles);
    zap['P'] = make_field(oscProbTable, "oscillation probability table file");
    zap['@'] = make_field(parameters,   "oscillation parameters") = JPARSER::initialised();
    zap['d'] = make_field(debug)                                  = 1;

    zap(argc, argv);
  }
  catch(const exception& error) {
    FATAL(error.what() << endl);
  }

  
  // Create atmospheric neutrino flux function
  
  const JFluxFunction_t fluxFunction = make_atmosphericNeutrinoFluxFunction(oscProbTable.c_str(), parameters);
  
  // Scan events

  JEvtWeightFileScannerSet<> scanners(inputFiles);  
  
  for (JEvtWeightFileScannerSet<>::iterator scanner = scanners.begin(); scanner != scanners.end(); ++scanner) {

    if (scanner->simul.size() > 0) {
      STATUS("Scanning " << scanner->simul[0].program << " files..." << endl);
    }
    
    // Assign flux function

    const JHead& header = scanner->getHeader();

    const JFluxMultiParticle multiFlux(header, fluxFunction);

    scanner->setFlux(multiFlux);
    

    // Print event-weights

    STATUS(LEFT (10) << "event ID"                       <<
	   RIGHT(15) << "E [GeV]"                        <<
	   RIGHT(15) << "costh"                          <<
	   RIGHT(10) << "flux "                          <<
	   LEFT (30) << "[GeV^-1 * m^-2 * sr^-1 * s^-1]" << endl);
    
    while (scanner->hasNext()) {
      
      const Evt* event    = scanner->next();
      const Trk& neutrino = get_neutrino(*event);
      
      const double E      = neutrino.E;
      const double costh  = -neutrino.dir.z / neutrino.dir.len();
      const double weight = scanner->getWeight(*event);
      
      STATUS(LEFT      (10)    << scanner->getCounter() << FILL(10,' ') << ' ' <<
	     FIXED     ( 5, 3) << right << E            << FILL(10,' ') << ' ' <<
	     FIXED     ( 5, 3) << right << costh        << FILL(10,' ') << ' ' <<
	     SCIENTIFIC(10, 3) << weight << '\r'); DEBUG(endl);
    }
  }

  return 0;
}