#include <map>
#include <memory>
#include <iostream>
#include <fstream>
#include <string>

#include "FWCore/Utilities/interface/Exception.h"
#include "FWCore/Framework/interface/ESProducer.h"
#include "FWCore/Framework/interface/EventSetupRecordIntervalFinder.h"
#include "FWCore/ParameterSet/interface/FileInPath.h"
#include "FWCore/ParameterSet/interface/ParameterSet.h"

#include "FWCore/Framework/interface/ValidityInterval.h"

#include "CalibCalorimetry/CastorCalib/interface/CastorDbASCIIIO.h"

#include "CondFormats/CastorObjects/interface/CastorPedestals.h"
#include "CondFormats/CastorObjects/interface/CastorPedestalWidths.h"
#include "CondFormats/CastorObjects/interface/CastorGains.h"
#include "CondFormats/CastorObjects/interface/CastorGainWidths.h"
#include "CondFormats/CastorObjects/interface/CastorElectronicsMap.h"
#include "CondFormats/CastorObjects/interface/CastorChannelQuality.h"
#include "CondFormats/CastorObjects/interface/CastorQIEData.h"

#include "CondFormats/DataRecord/interface/CastorPedestalsRcd.h"
#include "CondFormats/DataRecord/interface/CastorPedestalWidthsRcd.h"
#include "CondFormats/DataRecord/interface/CastorGainsRcd.h"
#include "CondFormats/DataRecord/interface/CastorGainWidthsRcd.h"
#include "CondFormats/DataRecord/interface/CastorElectronicsMapRcd.h"
#include "CondFormats/DataRecord/interface/CastorChannelQualityRcd.h"
#include "CondFormats/DataRecord/interface/CastorQIEDataRcd.h"
#include "CondFormats/DataRecord/interface/CastorRecoParamsRcd.h"
#include "CondFormats/DataRecord/interface/CastorSaturationCorrsRcd.h"

class CastorTextCalibrations : public edm::ESProducer, public edm::EventSetupRecordIntervalFinder {
public:
  CastorTextCalibrations(const edm::ParameterSet&);
  ~CastorTextCalibrations() override;

  void produce(){};

protected:
  void setIntervalFor(const edm::eventsetup::EventSetupRecordKey&,
                      const edm::IOVSyncValue&,
                      edm::ValidityInterval&) override;

  std::unique_ptr<CastorPedestals> producePedestals(const CastorPedestalsRcd& rcd);
  std::unique_ptr<CastorPedestalWidths> producePedestalWidths(const CastorPedestalWidthsRcd& rcd);
  std::unique_ptr<CastorGains> produceGains(const CastorGainsRcd& rcd);
  std::unique_ptr<CastorGainWidths> produceGainWidths(const CastorGainWidthsRcd& rcd);
  std::unique_ptr<CastorQIEData> produceQIEData(const CastorQIEDataRcd& rcd);
  std::unique_ptr<CastorChannelQuality> produceChannelQuality(const CastorChannelQualityRcd& rcd);
  std::unique_ptr<CastorElectronicsMap> produceElectronicsMap(const CastorElectronicsMapRcd& rcd);
  std::unique_ptr<CastorRecoParams> produceRecoParams(const CastorRecoParamsRcd& rcd);
  std::unique_ptr<CastorSaturationCorrs> produceSaturationCorrs(const CastorSaturationCorrsRcd& rcd);

private:
  std::map<std::string, std::string> mInputs;
};
//
// class decleration
//

using namespace cms;

CastorTextCalibrations::CastorTextCalibrations(const edm::ParameterSet& iConfig)

{
  //parsing parameters
  std::vector<edm::ParameterSet> data = iConfig.getParameter<std::vector<edm::ParameterSet> >("input");
  std::vector<edm::ParameterSet>::iterator request = data.begin();
  for (; request != data.end(); ++request) {
    std::string objectName = request->getParameter<std::string>("object");
    edm::FileInPath fp = request->getParameter<edm::FileInPath>("file");
    mInputs[objectName] = fp.fullPath();
    if (objectName == "Pedestals") {
      setWhatProduced(this, &CastorTextCalibrations::producePedestals);
      findingRecord<CastorPedestalsRcd>();
    } else if (objectName == "PedestalWidths") {
      setWhatProduced(this, &CastorTextCalibrations::producePedestalWidths);
      findingRecord<CastorPedestalWidthsRcd>();
    } else if (objectName == "Gains") {
      setWhatProduced(this, &CastorTextCalibrations::produceGains);
      findingRecord<CastorGainsRcd>();
    } else if (objectName == "GainWidths") {
      setWhatProduced(this, &CastorTextCalibrations::produceGainWidths);
      findingRecord<CastorGainWidthsRcd>();
    } else if (objectName == "QIEData") {
      setWhatProduced(this, &CastorTextCalibrations::produceQIEData);
      findingRecord<CastorQIEDataRcd>();
    } else if (objectName == "ChannelQuality") {
      setWhatProduced(this, &CastorTextCalibrations::produceChannelQuality);
      findingRecord<CastorChannelQualityRcd>();
    } else if (objectName == "ElectronicsMap") {
      setWhatProduced(this, &CastorTextCalibrations::produceElectronicsMap);
      findingRecord<CastorElectronicsMapRcd>();
    } else if (objectName == "RecoParams") {
      setWhatProduced(this, &CastorTextCalibrations::produceRecoParams);
      findingRecord<CastorRecoParamsRcd>();
    } else if (objectName == "SaturationCorrs") {
      setWhatProduced(this, &CastorTextCalibrations::produceSaturationCorrs);
      findingRecord<CastorSaturationCorrsRcd>();
    } else {
      std::cerr << "CastorTextCalibrations-> Unknown object name '" << objectName << "', known names are: "
                << "Pedestals PedestalWidths Gains GainWidths QIEData ChannelQuality ElectronicsMap RecoParams "
                   "SaturationCorrs"
                << std::endl;
    }
  }
  //  setWhatProduced(this);
}

CastorTextCalibrations::~CastorTextCalibrations() {}

//
// member functions
//
void CastorTextCalibrations::setIntervalFor(const edm::eventsetup::EventSetupRecordKey& iKey,
                                            const edm::IOVSyncValue& iTime,
                                            edm::ValidityInterval& oInterval) {
  std::string record = iKey.name();
  oInterval = edm::ValidityInterval(edm::IOVSyncValue::beginOfTime(), edm::IOVSyncValue::endOfTime());  //infinite
}

template <class T>
std::unique_ptr<T> produce_impl(const std::string& fFile) {
  std::unique_ptr<T> result(new T());
  std::ifstream inStream(fFile.c_str());
  if (!inStream.good()) {
    std::cerr << "CastorTextCalibrations-> Unable to open file '" << fFile << "'" << std::endl;
    throw cms::Exception("FileNotFound") << "Unable to open '" << fFile << "'" << std::endl;
  }
  if (!CastorDbASCIIIO::getObject(inStream, *result)) {
    std::cerr << "CastorTextCalibrations-> Can not read object from file '" << fFile << "'" << std::endl;
    throw cms::Exception("ReadError") << "Can not read object from file '" << fFile << "'" << std::endl;
  }
  return result;
}

std::unique_ptr<CastorPedestals> CastorTextCalibrations::producePedestals(const CastorPedestalsRcd&) {
  return produce_impl<CastorPedestals>(mInputs["Pedestals"]);
}

std::unique_ptr<CastorPedestalWidths> CastorTextCalibrations::producePedestalWidths(const CastorPedestalWidthsRcd&) {
  return produce_impl<CastorPedestalWidths>(mInputs["PedestalWidths"]);
}

std::unique_ptr<CastorGains> CastorTextCalibrations::produceGains(const CastorGainsRcd&) {
  return produce_impl<CastorGains>(mInputs["Gains"]);
}

std::unique_ptr<CastorGainWidths> CastorTextCalibrations::produceGainWidths(const CastorGainWidthsRcd&) {
  return produce_impl<CastorGainWidths>(mInputs["GainWidths"]);
}

std::unique_ptr<CastorQIEData> CastorTextCalibrations::produceQIEData(const CastorQIEDataRcd& rcd) {
  return produce_impl<CastorQIEData>(mInputs["QIEData"]);
}

std::unique_ptr<CastorChannelQuality> CastorTextCalibrations::produceChannelQuality(const CastorChannelQualityRcd& rcd) {
  return produce_impl<CastorChannelQuality>(mInputs["ChannelQuality"]);
}

std::unique_ptr<CastorElectronicsMap> CastorTextCalibrations::produceElectronicsMap(const CastorElectronicsMapRcd& rcd) {
  return produce_impl<CastorElectronicsMap>(mInputs["ElectronicsMap"]);
}

std::unique_ptr<CastorRecoParams> CastorTextCalibrations::produceRecoParams(const CastorRecoParamsRcd& rcd) {
  return produce_impl<CastorRecoParams>(mInputs["RecoParams"]);
}

std::unique_ptr<CastorSaturationCorrs> CastorTextCalibrations::produceSaturationCorrs(
    const CastorSaturationCorrsRcd& rcd) {
  return produce_impl<CastorSaturationCorrs>(mInputs["SaturationCorrs"]);
}

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

DEFINE_FWK_EVENTSETUP_SOURCE(CastorTextCalibrations);
