#include "CalibTracker/SiStripLorentzAngle/plugins/EnsembleCalibrationLA.h"
#include "CalibTracker/SiStripCommon/interface/Book.h"
#include "FWCore/ParameterSet/interface/ParameterSet.h"
#include <TChain.h>
#include <TFile.h>
#include <fstream>
#include <regex>

namespace sistrip {

  EnsembleCalibrationLA::EnsembleCalibrationLA(const edm::ParameterSet& conf)
      : inputFiles(conf.getParameter<std::vector<std::string> >("InputFiles")),
        inFileLocation(conf.getParameter<std::string>("InFileLocation")),
        Prefix(conf.getUntrackedParameter<std::string>("Prefix", "")),
        maxEvents(conf.getUntrackedParameter<unsigned>("MaxEvents", 0)),
        samples(conf.getParameter<unsigned>("Samples")),
        nbins(conf.getParameter<unsigned>("NBins")),
        lowBin(conf.getParameter<double>("LowBin")),
        highBin(conf.getParameter<double>("HighBin")),
        vMethods(conf.getParameter<std::vector<int> >("Methods")),
        tTopoToken_(esConsumes<edm::Transition::EndRun>()) {}

  EnsembleCalibrationLA::~EnsembleCalibrationLA(){};

  void EnsembleCalibrationLA::endJob() {
    Book book("la_ensemble");
    TChain* const chain = new TChain("la_ensemble");
    for (auto const& file : inputFiles)
      chain->Add((file + inFileLocation).c_str());

    int methods = 0;
    for (unsigned int method : vMethods)
      methods |= method;

    LA_Filler_Fitter laff(methods, samples, nbins, lowBin, highBin, maxEvents, tTopo_);
    laff.fill(chain, book);
    laff.fit(book);
    laff.summarize_ensembles(book);

    write_ensembles_text(book);
    write_ensembles_plots(book);
    write_samples_plots(book);
    write_calibrations();
  }

  void EnsembleCalibrationLA::beginRun(const edm::Run&, const edm::EventSetup& eSetup) {}

  void EnsembleCalibrationLA::endRun(const edm::Run&, const edm::EventSetup& eSetup) {
    tTopo_ = &eSetup.getData(tTopoToken_);
  }

  void EnsembleCalibrationLA::write_ensembles_text(const Book& book) {
    for (auto const& ensemble : LA_Filler_Fitter::ensemble_summary(book)) {
      std::fstream file((Prefix + ensemble.first + ".dat").c_str(), std::ios::out);
      for (auto const& summary : ensemble.second)
        file << summary << std::endl;

      const std::pair<std::pair<float, float>, std::pair<float, float> > line =
          LA_Filler_Fitter::offset_slope(ensemble.second);
      const float pull = LA_Filler_Fitter::pull(ensemble.second);

      unsigned index = 15;
      std::string label;
      {
        std::cout << ensemble.first << std::endl;
        std::regex format(".*(T[IO]B)_layer(\\d)([as])_(.*)");
        if (std::regex_match(ensemble.first, format)) {
          const bool TIB = "TIB" == std::regex_replace(ensemble.first, format, "\\1");
          const bool stereo = "s" == std::regex_replace(ensemble.first, format, "\\3");
          const unsigned layer = std::stoul(std::regex_replace(ensemble.first, format, "\\2"));
          label = std::regex_replace(ensemble.first, format, "\\4");
          index = LA_Filler_Fitter::layer_index(TIB, stereo, layer);

          calibrations[label].slopes[index] = line.second.first;
          calibrations[label].offsets[index] = line.first.first;
          calibrations[label].pulls[index] = pull;
        }
      }

      file << std::endl
           << std::endl
           << "# Best Fit Line: " << line.first.first << "(" << line.first.second << ")   +   x* " << line.second.first
           << "(" << line.second.second << ")" << std::endl
           << "# Pull (average sigma of (x_measure-x_truth)/e_measure): " << pull << std::endl
           << "LA_Calibration( METHOD_XXXXX , xxx, " << line.second.first << ", " << line.first.first << ", " << pull
           << ")," << std::endl;
      file.close();
    }
  }

  void EnsembleCalibrationLA::write_ensembles_plots(const Book& book) const {
    TFile file((Prefix + "sampleFits.root").c_str(), "RECREATE");
    for (Book::const_iterator hist = book.begin(".*(profile|ratio|reconstruction|symm|symmchi2|_w\\d)");
         hist != book.end();
         ++hist)
      hist->second->Write();
    file.Close();
  }

  void EnsembleCalibrationLA::write_samples_plots(const Book& book) const {
    TFile file((Prefix + "ensembleFits.root").c_str(), "RECREATE");
    for (Book::const_iterator hist = book.begin(".*(measure|merr|ensembleReco|pull)"); hist != book.end(); ++hist)
      hist->second->Write();
    file.Close();
  }

  void EnsembleCalibrationLA::write_calibrations() const {
    std::fstream file((Prefix + "calibrations.dat").c_str(), std::ios::out);
    for (auto const& cal : calibrations) {
      file << cal.first << std::endl << "\t slopes(";
      for (float i : cal.second.slopes)
        file << i << ",";
      file << ")" << std::endl << "\t offsets(";
      for (float i : cal.second.offsets)
        file << i << ",";
      file << ")" << std::endl << "\t pulls(";
      for (float i : cal.second.pulls)
        file << i << ",";
      file << ")" << std::endl;
    }
    file.close();
  }

}  // namespace sistrip
