
#include "TBDataFormats/HcalTBObjects/interface/HcalTBTriggerData.h"
#include "TBDataFormats/HcalTBObjects/interface/HcalTBRunData.h"
#include "TBDataFormats/HcalTBObjects/interface/HcalTBEventPosition.h"
#include "TBDataFormats/HcalTBObjects/interface/HcalTBTiming.h"
#include "TBDataFormats/HcalTBObjects/interface/HcalTBBeamCounters.h"
#include "RecoTBCalo/HcalTBObjectUnpacker/plugins/HcalTBObjectUnpacker.h"
#include "DataFormats/Common/interface/EDCollection.h"
#include "DataFormats/Common/interface/Handle.h"
#include <iostream>
#include <fstream>
using namespace std;

HcalTBObjectUnpacker::HcalTBObjectUnpacker(edm::ParameterSet const& conf)
    : triggerFed_(conf.getUntrackedParameter<int>("HcalTriggerFED", -1)),
      sdFed_(conf.getUntrackedParameter<int>("HcalSlowDataFED", -1)),
      spdFed_(conf.getUntrackedParameter<int>("HcalSourcePositionFED", -1)),
      tdcFed_(conf.getUntrackedParameter<int>("HcalTDCFED", -1)),
      qadcFed_(conf.getUntrackedParameter<int>("HcalQADCFED", -1)),
      calibFile_(conf.getUntrackedParameter<string>("ConfigurationFile", "")),
      tdcUnpacker_(conf.getUntrackedParameter<bool>("IncludeUnmatchedHits", false)),
      doRunData_(false),
      doTriggerData_(false),
      doEventPosition_(false),
      doTiming_(false),
      doSourcePos_(false),
      doBeamADC_(false) {
  tok_raw_ = consumes<FEDRawDataCollection>(conf.getParameter<edm::InputTag>("fedRawDataCollectionTag"));

  if (triggerFed_ >= 0) {
    std::cout << "HcalTBObjectUnpacker will unpack Trigger FED ";
    std::cout << triggerFed_ << endl;
    doTriggerData_ = true;
  }

  if (sdFed_ >= 0) {
    std::cout << "HcalTBObjectUnpacker will unpack SlowData FED ";
    std::cout << sdFed_ << endl;
    doRunData_ = true;
    doEventPosition_ = true;  // at least the table
  }

  if (tdcFed_ >= 0) {
    std::cout << "HcalTBObjectUnpacker will unpack TDC FED ";
    std::cout << tdcFed_ << endl;
    doTiming_ = true;
    doEventPosition_ = true;  // at least the WC
  }

  if (qadcFed_ >= 0) {
    std::cout << "HcalTBObjectUnpacker will unpack QADC FED ";
    std::cout << qadcFed_ << endl;
    doBeamADC_ = true;
  }

  if (spdFed_ >= 0) {
    std::cout << "HcalTBObjectUnpacker will unpack Source Position Data FED ";
    std::cout << spdFed_ << endl;
    doSourcePos_ = true;
  }

  if (tdcFed_ >= 0 || qadcFed_ >= 0) {
    calibLines_.clear();
    if (!calibFile_.empty()) {
      parseCalib();
      //  printf("I got %d lines!\n",calibLines_.size());
      if (calibLines_.empty())
        throw cms::Exception("Incomplete configuration")
            << "HcalTBObjectUnpacker: TDC/QADC/WC configuration file not found or is empty: " << calibFile_ << endl;
    } else {
      throw cms::Exception("Incomplete configuration")
          << "HcalTBObjectUnpacker: TDC/QADC/WC configuration file not found: " << calibFile_ << endl;
    }
  }

  if (doTriggerData_)
    produces<HcalTBTriggerData>();
  if (doRunData_)
    produces<HcalTBRunData>();
  if (doEventPosition_)
    produces<HcalTBEventPosition>();
  if (doTiming_)
    produces<HcalTBTiming>();
  //    if (doBeamADC_) produces<HcalTBBeamCounters>();
  if (doBeamADC_) {
    produces<HcalTBBeamCounters>();
    qadcUnpacker_.setCalib(calibLines_);
  }
  if (doSourcePos_)
    produces<HcalSourcePositionData>();
  if (doTiming_ || doEventPosition_)
    tdcUnpacker_.setCalib(calibLines_);
}

// Functions that gets called by framework every event
void HcalTBObjectUnpacker::produce(edm::Event& e, const edm::EventSetup&) {
  // Step A: Get Inputs
  edm::Handle<FEDRawDataCollection> rawraw;
  e.getByToken(tok_raw_, rawraw);

  // Step B: Create empty output
  auto trigd = std::make_unique<HcalTBTriggerData>();

  auto rund = std::make_unique<HcalTBRunData>();

  auto epd = std::make_unique<HcalTBEventPosition>();

  auto tmgd = std::make_unique<HcalTBTiming>();

  auto bcntd = std::make_unique<HcalTBBeamCounters>();

  auto spd = std::make_unique<HcalSourcePositionData>();

  if (triggerFed_ >= 0) {
    // Step C: unpack all requested FEDs
    const FEDRawData& fed = rawraw->FEDData(triggerFed_);
    tdUnpacker_.unpack(fed, *trigd);
  }

  if (sdFed_ >= 0) {
    // Step C: unpack all requested FEDs
    const FEDRawData& fed = rawraw->FEDData(sdFed_);
    sdUnpacker_.unpack(fed, *rund, *epd);
  }

  if (tdcFed_ >= 0) {
    // Step C: unpack all requested FEDs
    const FEDRawData& fed = rawraw->FEDData(tdcFed_);
    tdcUnpacker_.unpack(fed, *epd, *tmgd);
  }

  if (qadcFed_ >= 0) {
    // Step C: unpack all requested FEDs
    const FEDRawData& fed = rawraw->FEDData(qadcFed_);
    bool is04 = true;
    if (qadcFed_ == 8)
      is04 = false;
    qadcUnpacker_.unpack(fed, *bcntd, is04);
  }

  if (spdFed_ >= 0) {
    // Step C: unpack all requested FEDs
    const FEDRawData& fed = rawraw->FEDData(spdFed_);
    spdUnpacker_.unpack(fed, *spd);
  }

  // Step D: Put outputs into event
  if (doTriggerData_)
    e.put(std::move(trigd));
  if (doRunData_)
    e.put(std::move(rund));
  if (doEventPosition_)
    e.put(std::move(epd));
  if (doTiming_)
    e.put(std::move(tmgd));
  if (doBeamADC_)
    e.put(std::move(bcntd));
  if (doSourcePos_)
    e.put(std::move(spd));
}

void HcalTBObjectUnpacker::parseCalib() {
  if (calibFile_.empty()) {
    printf("HcalTBObjectUnpacker cowardly refuses to parse a NULL file...\n");
    return;
  }

  edm::FileInPath fip(calibFile_);

  ifstream infile(fip.fullPath().c_str());

  char buffer[1024];
  string tmpStr;

  while (infile.getline(buffer, 1024)) {
    if (buffer[0] == '#')
      continue;  //ignore comment
    if (buffer[0] == '/' && buffer[1] == '/')
      continue;  //ignore comment
    tmpStr = string(buffer);
    vector<string> lineVect;

    int start = 0;
    bool empty = true;
    for (unsigned i = 0; i <= tmpStr.size(); i++) {
      if (tmpStr[i] == ' ' || i == tmpStr.size()) {
        if (!empty) {
          std::string item(tmpStr, start, i - start);
          lineVect.push_back(item);
          empty = true;
          //	  printf("Got: %s\n",item.c_str());
        }
        start = i + 1;
      } else {
        if (empty)
          empty = false;
      }
    }

    if (!lineVect.empty())
      calibLines_.push_back(lineVect);
  }

  infile.close();
  return;
}

#include "FWCore/PluginManager/interface/ModuleDef.h"
#include "FWCore/Framework/interface/MakerMacros.h"

DEFINE_FWK_MODULE(HcalTBObjectUnpacker);
