#include <fstream>
#include <sstream>

#include "CondFormats/BTauObjects/interface/BTagCalibration.h"
#include "FWCore/Utilities/interface/Exception.h"

BTagCalibration::BTagCalibration(const std::string &taggr) : tagger_(taggr) {}

BTagCalibration::BTagCalibration(const std::string &taggr, const std::string &filename, bool validate)
    : tagger_(taggr) {
  std::ifstream ifs(filename);
  if (!ifs.good()) {
    throw cms::Exception("BTagCalibration") << "input file not available: " << filename;
  }
  readCSV(ifs, validate);
  ifs.close();
}

void BTagCalibration::addEntry(const BTagEntry &entry) { data_[token(entry.params)].push_back(entry); }

const std::vector<BTagEntry> &BTagCalibration::getEntries(const BTagEntry::Parameters &par) const {
  std::string tok = token(par);
  if (!data_.count(tok)) {
    throw cms::Exception("BTagCalibration") << "(OperatingPoint, measurementType, sysType) not available: " << tok;
  }
  return data_.at(tok);
}

void BTagCalibration::readCSV(const std::string &s, bool validate) {
  std::stringstream buff(s);
  readCSV(buff, validate);
}

void BTagCalibration::readCSV(std::istream &s, bool validate) {
  std::string line;

  // firstline might be the header
  getline(s, line);
  if (line.find("OperatingPoint") == std::string::npos) {
    addEntry(BTagEntry(line, validate));
  }

  while (getline(s, line)) {
    line = BTagEntry::trimStr(line);
    if (line.empty()) {  // skip empty lines
      continue;
    }
    addEntry(BTagEntry(line, validate));
  }
}

void BTagCalibration::makeCSV(std::ostream &s) const {
  s << tagger_ << ";" << BTagEntry::makeCSVHeader();
  for (std::map<std::string, std::vector<BTagEntry> >::const_iterator i = data_.cbegin(); i != data_.cend(); ++i) {
    const std::vector<BTagEntry> &vec = i->second;
    for (std::vector<BTagEntry>::const_iterator j = vec.cbegin(); j != vec.cend(); ++j) {
      s << j->makeCSVLine();
    }
  }
}

std::string BTagCalibration::makeCSV() const {
  std::stringstream buff;
  makeCSV(buff);
  return buff.str();
}

std::string BTagCalibration::token(const BTagEntry::Parameters &par) {
  std::stringstream buff;
  buff << par.operatingPoint << ", " << par.measurementType << ", " << par.sysType;
  return buff.str();
}
