// C++
#include<map>
#include<vector>
#include<string>
#include<utility>

//Root
#include "TObject.h"
#include "Rtypes.h"
#include "TH1.h"

// Local
#include "FitBeam.h"
#include "FitData.h"
#include "MCReweight/Zbeam.h"
#include "MCReweight/Zfluk.h"

class TH2F;
class TH1D;
class TH2D;

class TTree;
class Zbeam;
class Zfluk;
class MCReweight;
class Registry;

class FitPTRW : public TObject
{
 public:
  FitPTRW();
  ~FitPTRW();

  void FillMCVectors(const std::string &filename, const std::string &panname, FitBeam::FitBeam_t beam_type, const bool rwinuk);
  void FillDataHist(const std::string &filename, const std::string &histname, const FitBeam::FitBeam_t beam_type);
  void FillRWHist();
  double InukeParam(double reco_eshw, double true_eshw, bool doit);
  bool CutEvent();
  void UseNeugen(const bool &useit) {fUseNeugen=useit;}

  void UseSmooth(const bool &useit) {fUseSmooth=useit;}
  void SetSmoothWidth(const double &val) {fSmoothWidth=val;}

  void SetDataPOTS(const FitBeam::FitBeam_t beam_type, double nPOT)
  {const std::pair<const FitBeam::FitBeam_t, double> p(beam_type, nPOT);
    fDataPots.insert(p);}
  void SetMCPOTS(const FitBeam::FitBeam_t beam_type, double nPOT)
  {const std::pair<const FitBeam::FitBeam_t, double> p(beam_type, nPOT);
    fMCPots.insert(p);}
  void SetZflukPar(const std::vector<double>& par) {zflParVec=par; fZfluk.SetParameters(zflParVec);}
  void SetZbeamPar(const std::vector<double>& par) {zbmParVec=par;}
  void SetDetPar(const std::vector<double>& par)   {detParVec=par;}
  void SetNueMRCCRatio(const TH1* rat) 
  {
    for(int i = 0; i < rat->GetNbinsX()+1; i++)
      {
	double low = double(rat->GetBinLowEdge(i));
	double high = double(low + rat->GetBinWidth(i));
	double val= double(rat->GetBinContent(i));
	
	fMRCCStart.push_back(low);
	fMRCCEnd.push_back(high);
	fMRCCWeight.push_back(val);
      }
    fUseNueMRCC=true;
  }
  const double GetMCPOTS(const FitBeam::FitBeam_t beam_type) {return fMCPots[beam_type];}
  const double GetDataPOTS(const FitBeam::FitBeam_t beam_type) {return fDataPots[beam_type];}
  const std::vector<double> GetZflukPar() {return zflParVec;}
  const std::vector<double> GetZbeamPar() {return zbmParVec;}
  const std::vector<double> GetDetPar()   {return detParVec;}
  const double GetNuEMiscalPar(int nutype) {
    if      (nutype==14  || nutype==56) return detParVec[0];
    else if (nutype==-14 || nutype==55) return detParVec[0];
    else if (nutype==12  || nutype==53) return detParVec[1];
    else if (nutype==-12 || nutype==52) return detParVec[1];
    return 0.;
  }
  const double GetShwOffsetPar() {return detParVec[2];}
  const double GetNCPar(const FitBeam::FitBeam_t beam_type)
  {
    if      (beam_type>=5600&&beam_type<5700) return detParVec[3];
    else if (beam_type>=5500&&beam_type<5600) return detParVec[4];
    else if (beam_type>=5300&&beam_type<5400) return detParVec[5];
    return 0.;
  }
  const double GetNumubarXsecPar(int par=0) {return detParVec[6+par];}
  const double GetEshwPar() {return detParVec[8];}
  const double GetEmuCrvPar() {return detParVec[10];}
  const double GetEmuRangePar() {return detParVec[9];}
  const double GetXSPar(int process) {
    if(process==1001) return detParVec[11];
    if(process==1002) return detParVec[12];
    if(process==1003) return detParVec[13];
    return 0.0;
  }

  double GetArea(const double &center, 
		 const double &low_edge,const double &high_edge,
		 const double &width);
  void FillSmooth(TH1D* &hist,const double &center,const double &width,
		  const double &weight=1);


  std::map<FitBeam::FitBeam_t, double> fDataPots;
  std::map<FitBeam::FitBeam_t, double> fMCPots;
  // events for each beam type
  std::map<FitBeam::FitBeam_t, std::vector<FitData> > fMCData;

  std::map<FitBeam::FitBeam_t, TH1D *> fDataHist;
  std::map<FitBeam::FitBeam_t, TH1D *> fReWeightHist;
  std::map<FitBeam::FitBeam_t, TH1D *> fMCHist;
  
  std::map<FitBeam::FitBeam_t, double> initchi2;
  std::map<FitBeam::FitBeam_t, int> initndf;
  
  std::map<FitBeam::FitBeam_t, double> bestchi2;
  std::map<FitBeam::FitBeam_t, int> bestndf;

  int iterations;

  double inittotchi2;
  double besttotchi2;

  Zbeam fZbeam;
  Zfluk fZfluk;

  Registry *rwtconfig;
 private:
  std::vector<double> zflParVec;
  std::vector<double> zbmParVec;
  std::vector<double> detParVec;
  
  std::vector<double> fMRCCStart;
  std::vector<double> fMRCCEnd;
  std::vector<double> fMRCCWeight;
  bool fUseNeugen;
  bool fUseNueMRCC;

  bool fUseSmooth;
  double fSmoothWidth;
  
  MCReweight *mcr;

  
private:
  ClassDef(FitPTRW,1)

};
