#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <vector>
#include <iterator>

#include "RooDataSet.h"
#include "RooCategory.h"
#include "RooStats/ModelConfig.h"
#include "RooStats/ProofConfig.h"
#include "RooStats/ToyMCSampler.h"

#include "QFramework/TQIterator.h"

#include "SFramework/TSHypothesisTest.h"

#include "QFramework/TQLibrary.h"

#include "SFramework/TSUtils.h"

ClassImp(TSHypothesisTest)

//__________________________________________________________________________________|___________

TSHypothesisTest::Hypothesis::Hypothesis(const char* hname) :
  fName(hname)
{
  // do nothing
}

//__________________________________________________________________________________|___________

TSHypothesisTest::Hypothesis::Hypothesis(const char* hname, const char* pname, double val) :
  fName(hname)
{
  // add one parameter
  this->fParameters[pname]=val;
}

//__________________________________________________________________________________|___________

TSHypothesisTest::Hypothesis::Hypothesis(TQFolder* f){
  // constructor based on a TQFolder
  this->fName = f->getTagStringDefault("name",f->GetName());
  TQIterator pars(f->getListOfKeys("pars.*"), true);
  while(pars.hasNext()){
    TString tag = pars.readNext()->GetName();
    double val;
    if(!f->getTagDouble(tag,val)){
      throw std::runtime_error("unable to retrieve tag!");
    }
    TQStringUtils::removeLeading(tag,"pars.");
    this->addParameter(tag.Data(),val);
  }
}  

//__________________________________________________________________________________|___________

void TSHypothesisTest::Hypothesis::addParametersToList(const RooAbsCollection& allPars, RooAbsCollection& toList){
  // add all parameters in this hypothesis to the list
  for(auto p:this->fParameters){
    RooAbsArg* par = allPars.find(p.first.c_str());
    if(!par) throw std::runtime_error(TString::Format("unable to find parameter '%s' in list!",p.first.c_str()).Data());
    toList.add(*par);
  }
}

//__________________________________________________________________________________|___________

const char* TSHypothesisTest::Hypothesis::getParameterNameString() {
  // get a comma separated string of all parameter names
  std::stringstream ss;
  bool first = true;
  for(auto p:this->fParameters){
    ss << p.first;
    if(!first) ss << ",";
    first = false;
  }
  return ss.str().c_str();
}

//__________________________________________________________________________________|___________
    
void TSHypothesisTest::Hypothesis::addParameter(const char* pname, double val){
  // add a parameter to this hypothesis
  this->fParameters[pname]=val;
}

//__________________________________________________________________________________|___________

const char* TSHypothesisTest::Hypothesis::name() const {
  // get the name of this hypothesis
  return fName.c_str();
}

//__________________________________________________________________________________|___________

void TSHypothesisTest::Hypothesis::setParameters(RooAbsCollection& pars) const {
  // apply the parameter settings of this hypothesis
  ROOFIT_ITERATE(pars,RooAbsArg,obj){
    RooRealVar* par = dynamic_cast<RooRealVar*>(obj);
    for(auto p:this->fParameters){
      if(p.first.compare(par->GetName()) == 0){
        par->setVal(p.second);
      }
    }
  }
}

//__________________________________________________________________________________|___________

void TSHypothesisTest::Hypothesis::setParametersConstant(RooAbsCollection& pars, bool setConstant) const {
  // control the const property of all parameters of this hypothesis
  ROOFIT_ITERATE(pars,RooAbsArg,obj){
    RooRealVar* par = dynamic_cast<RooRealVar*>(obj);
    for(auto p:this->fParameters){
      if(p.first.compare(par->GetName()) == 0){
        par->setConstant(setConstant);
      }
    }
  }
}

//__________________________________________________________________________________|___________

TSHypothesisTest::TSHypothesisTest(RooWorkspace * ws, TQFolder* snapshots) : TSStatisticsCalculator("TSHypothesisTest",ws,snapshots) {
  // constructor
}


//__________________________________________________________________________________|___________

TSHypothesisTest::~TSHypothesisTest() {
  // default destructor
}

//__________________________________________________________________________________|___________

void TSHypothesisTest::runFit(TQFolder* results, RooDataSet* data, const TString& name, TQFolder* options){
  // run a fit of one hypotheses on one toy (or real) dataset
  bool store = options->getTagBoolDefault("storeResults",false);
  TQFolder* result = this->fitPdfToData(this->getPdf(),data,this->getNuisanceParameters(options),options);
  
  if(!result) return;
  double minNll = result->getTagDoubleDefault("minNll",0);
  int status = result->getTagIntegerDefault("status",-1);
  
  results->setTagDouble(name+".minNll",minNll);
  results->setTagDouble(name+".status",status);
  
  if(store){
    result->setName(name);
    results->addFolder(result);
  } else {
    delete result;
  }
}


//__________________________________________________________________________________|___________

TQFolder* TSHypothesisTest::runFit(RooDataSet* data, TQFolder* options, RooAbsCollection& pois, const std::vector<const Hypothesis*>& hypotheses){
  // run a fit of all hypotheses on one toy (or real) dataset
  TString snapshot = options->getTagStringDefault("snapshot","SnSh_AllVars_Nominal");

  TQFolder* result = new TQFolder(data->GetName());

  for(const auto& h:hypotheses){
    this->fWorkspace->loadSnapshot(snapshot);
    h->setParameters(pois);
    TSUtils::setParametersConstant(&pois,true);
    runFit(result,data,h->name(),options);
  }
  
  this->fWorkspace->loadSnapshot(snapshot);
  TSUtils::setParametersConstant(&pois,false);
  runFit(result,data,"uncond",options);
  
  return result;
}

//__________________________________________________________________________________|___________

TQFolder * TSHypothesisTest::runCalculation(TQFolder * options){
  // perform the calculation
  if(!this->fModelConfig) return NULL;
  
  RooArgSet pois;
  std::vector<const Hypothesis*> hypotheses;
  TQFolder* result = new TQFolder("result");
  TQFolder* hypos = result->getFolder("hypotheses+");
  TQFolderIterator itr(options->getListOfFolders("?"));
  while(itr.hasNext()){
    TQFolder* f = itr.readNext();
    hypos->addFolder(f->copy());
    Hypothesis* h = new Hypothesis(f);
    h->addParametersToList(this->fWorkspace->allVars(),pois);
    hypotheses.push_back(h);
  }
  
  RooArgSet nuis (*(this->fModelConfig->GetNuisanceParameters()));
  RooArgSet globs(*(this->fModelConfig->GetGlobalObservables()));
  
  RooArgSet allVars;
  allVars.add(pois);
  allVars.add(nuis);
  allVars.add(globs);

  RooArgSet obs(*(this->fModelConfig->GetObservables()));
  
  RooCategory* indexCat = this->getIndexCategory();
  if(indexCat){
    indexCat->setIndex(0);    
    obs.add(*indexCat);
  }
  
  RooAbsPdf* pdf = this->getPdf();
  
  int nworkers = options->getTagIntegerDefault("nWorkers",8);
  RooStats::ProofConfig pc(*this->fWorkspace, nworkers, TString::Format("workers=%d",nworkers), false);
  RooStats::ToyMCSampler sampler;
  sampler.SetUseMultiGen(indexCat);
  sampler.SetPdf(*pdf);
  sampler.SetObservables(obs);
  sampler.SetNuisanceParameters(nuis);
  sampler.SetGlobalObservables(globs);
  sampler.SetParametersForTestStat(pois);
  sampler.SetProofConfig(&pc);
  
  TQFolder* dataresults = result->getFolder("data+");
  for(const auto& dsname:options->getTagVString("dataName")){
    RooDataSet* data = dynamic_cast<RooDataSet*>(this->fWorkspace->data(dsname));
    if(!data){
      this->error(TString::Format("unable to find dataset '%s', skipping",dsname.Data()));
      continue;
    }
    TQFolder* fitresult = this->runFit(data,options,pois,hypotheses);
    dataresults->addFolder(fitresult);
  }
  
  int nToys = options->getTagIntegerDefault("nToys",1000);
  bool saveToys = options->getTagBoolDefault("saveToys",false);

  TQFolder* toyresults = result->getFolder("toys+");
  for(auto h:hypotheses){
    TQFolder* f = new TQFolder(h->name());
    toyresults->addFolder(f);
  }
  
  for (int itoy = 0; itoy < nToys; ++itoy) {
    for(auto h:hypotheses){
      h->setParameters(pois);
      TSUtils::setParametersConstant(&pois,true);
      
      RooDataSet* toyData = dynamic_cast<RooDataSet*>(sampler.GenerateToyData(allVars));
      if(!toyData) throw std::runtime_error("unable to generate toy data!");
      toyData->SetName(TString::Format("toy_%d",itoy).Data());
      if(saveToys){
        fWorkspace->import(*toyData);
      }
      TQFolder* fitresult = this->runFit(toyData,options,pois,hypotheses);
      toyresults->getFolder(h->name())->addFolder(fitresult);
    }
  }
  
  for(auto h:hypotheses){
    delete h;
  }

  return result;
}


 TSHypothesisTest.cxx:1
 TSHypothesisTest.cxx:2
 TSHypothesisTest.cxx:3
 TSHypothesisTest.cxx:4
 TSHypothesisTest.cxx:5
 TSHypothesisTest.cxx:6
 TSHypothesisTest.cxx:7
 TSHypothesisTest.cxx:8
 TSHypothesisTest.cxx:9
 TSHypothesisTest.cxx:10
 TSHypothesisTest.cxx:11
 TSHypothesisTest.cxx:12
 TSHypothesisTest.cxx:13
 TSHypothesisTest.cxx:14
 TSHypothesisTest.cxx:15
 TSHypothesisTest.cxx:16
 TSHypothesisTest.cxx:17
 TSHypothesisTest.cxx:18
 TSHypothesisTest.cxx:19
 TSHypothesisTest.cxx:20
 TSHypothesisTest.cxx:21
 TSHypothesisTest.cxx:22
 TSHypothesisTest.cxx:23
 TSHypothesisTest.cxx:24
 TSHypothesisTest.cxx:25
 TSHypothesisTest.cxx:26
 TSHypothesisTest.cxx:27
 TSHypothesisTest.cxx:28
 TSHypothesisTest.cxx:29
 TSHypothesisTest.cxx:30
 TSHypothesisTest.cxx:31
 TSHypothesisTest.cxx:32
 TSHypothesisTest.cxx:33
 TSHypothesisTest.cxx:34
 TSHypothesisTest.cxx:35
 TSHypothesisTest.cxx:36
 TSHypothesisTest.cxx:37
 TSHypothesisTest.cxx:38
 TSHypothesisTest.cxx:39
 TSHypothesisTest.cxx:40
 TSHypothesisTest.cxx:41
 TSHypothesisTest.cxx:42
 TSHypothesisTest.cxx:43
 TSHypothesisTest.cxx:44
 TSHypothesisTest.cxx:45
 TSHypothesisTest.cxx:46
 TSHypothesisTest.cxx:47
 TSHypothesisTest.cxx:48
 TSHypothesisTest.cxx:49
 TSHypothesisTest.cxx:50
 TSHypothesisTest.cxx:51
 TSHypothesisTest.cxx:52
 TSHypothesisTest.cxx:53
 TSHypothesisTest.cxx:54
 TSHypothesisTest.cxx:55
 TSHypothesisTest.cxx:56
 TSHypothesisTest.cxx:57
 TSHypothesisTest.cxx:58
 TSHypothesisTest.cxx:59
 TSHypothesisTest.cxx:60
 TSHypothesisTest.cxx:61
 TSHypothesisTest.cxx:62
 TSHypothesisTest.cxx:63
 TSHypothesisTest.cxx:64
 TSHypothesisTest.cxx:65
 TSHypothesisTest.cxx:66
 TSHypothesisTest.cxx:67
 TSHypothesisTest.cxx:68
 TSHypothesisTest.cxx:69
 TSHypothesisTest.cxx:70
 TSHypothesisTest.cxx:71
 TSHypothesisTest.cxx:72
 TSHypothesisTest.cxx:73
 TSHypothesisTest.cxx:74
 TSHypothesisTest.cxx:75
 TSHypothesisTest.cxx:76
 TSHypothesisTest.cxx:77
 TSHypothesisTest.cxx:78
 TSHypothesisTest.cxx:79
 TSHypothesisTest.cxx:80
 TSHypothesisTest.cxx:81
 TSHypothesisTest.cxx:82
 TSHypothesisTest.cxx:83
 TSHypothesisTest.cxx:84
 TSHypothesisTest.cxx:85
 TSHypothesisTest.cxx:86
 TSHypothesisTest.cxx:87
 TSHypothesisTest.cxx:88
 TSHypothesisTest.cxx:89
 TSHypothesisTest.cxx:90
 TSHypothesisTest.cxx:91
 TSHypothesisTest.cxx:92
 TSHypothesisTest.cxx:93
 TSHypothesisTest.cxx:94
 TSHypothesisTest.cxx:95
 TSHypothesisTest.cxx:96
 TSHypothesisTest.cxx:97
 TSHypothesisTest.cxx:98
 TSHypothesisTest.cxx:99
 TSHypothesisTest.cxx:100
 TSHypothesisTest.cxx:101
 TSHypothesisTest.cxx:102
 TSHypothesisTest.cxx:103
 TSHypothesisTest.cxx:104
 TSHypothesisTest.cxx:105
 TSHypothesisTest.cxx:106
 TSHypothesisTest.cxx:107
 TSHypothesisTest.cxx:108
 TSHypothesisTest.cxx:109
 TSHypothesisTest.cxx:110
 TSHypothesisTest.cxx:111
 TSHypothesisTest.cxx:112
 TSHypothesisTest.cxx:113
 TSHypothesisTest.cxx:114
 TSHypothesisTest.cxx:115
 TSHypothesisTest.cxx:116
 TSHypothesisTest.cxx:117
 TSHypothesisTest.cxx:118
 TSHypothesisTest.cxx:119
 TSHypothesisTest.cxx:120
 TSHypothesisTest.cxx:121
 TSHypothesisTest.cxx:122
 TSHypothesisTest.cxx:123
 TSHypothesisTest.cxx:124
 TSHypothesisTest.cxx:125
 TSHypothesisTest.cxx:126
 TSHypothesisTest.cxx:127
 TSHypothesisTest.cxx:128
 TSHypothesisTest.cxx:129
 TSHypothesisTest.cxx:130
 TSHypothesisTest.cxx:131
 TSHypothesisTest.cxx:132
 TSHypothesisTest.cxx:133
 TSHypothesisTest.cxx:134
 TSHypothesisTest.cxx:135
 TSHypothesisTest.cxx:136
 TSHypothesisTest.cxx:137
 TSHypothesisTest.cxx:138
 TSHypothesisTest.cxx:139
 TSHypothesisTest.cxx:140
 TSHypothesisTest.cxx:141
 TSHypothesisTest.cxx:142
 TSHypothesisTest.cxx:143
 TSHypothesisTest.cxx:144
 TSHypothesisTest.cxx:145
 TSHypothesisTest.cxx:146
 TSHypothesisTest.cxx:147
 TSHypothesisTest.cxx:148
 TSHypothesisTest.cxx:149
 TSHypothesisTest.cxx:150
 TSHypothesisTest.cxx:151
 TSHypothesisTest.cxx:152
 TSHypothesisTest.cxx:153
 TSHypothesisTest.cxx:154
 TSHypothesisTest.cxx:155
 TSHypothesisTest.cxx:156
 TSHypothesisTest.cxx:157
 TSHypothesisTest.cxx:158
 TSHypothesisTest.cxx:159
 TSHypothesisTest.cxx:160
 TSHypothesisTest.cxx:161
 TSHypothesisTest.cxx:162
 TSHypothesisTest.cxx:163
 TSHypothesisTest.cxx:164
 TSHypothesisTest.cxx:165
 TSHypothesisTest.cxx:166
 TSHypothesisTest.cxx:167
 TSHypothesisTest.cxx:168
 TSHypothesisTest.cxx:169
 TSHypothesisTest.cxx:170
 TSHypothesisTest.cxx:171
 TSHypothesisTest.cxx:172
 TSHypothesisTest.cxx:173
 TSHypothesisTest.cxx:174
 TSHypothesisTest.cxx:175
 TSHypothesisTest.cxx:176
 TSHypothesisTest.cxx:177
 TSHypothesisTest.cxx:178
 TSHypothesisTest.cxx:179
 TSHypothesisTest.cxx:180
 TSHypothesisTest.cxx:181
 TSHypothesisTest.cxx:182
 TSHypothesisTest.cxx:183
 TSHypothesisTest.cxx:184
 TSHypothesisTest.cxx:185
 TSHypothesisTest.cxx:186
 TSHypothesisTest.cxx:187
 TSHypothesisTest.cxx:188
 TSHypothesisTest.cxx:189
 TSHypothesisTest.cxx:190
 TSHypothesisTest.cxx:191
 TSHypothesisTest.cxx:192
 TSHypothesisTest.cxx:193
 TSHypothesisTest.cxx:194
 TSHypothesisTest.cxx:195
 TSHypothesisTest.cxx:196
 TSHypothesisTest.cxx:197
 TSHypothesisTest.cxx:198
 TSHypothesisTest.cxx:199
 TSHypothesisTest.cxx:200
 TSHypothesisTest.cxx:201
 TSHypothesisTest.cxx:202
 TSHypothesisTest.cxx:203
 TSHypothesisTest.cxx:204
 TSHypothesisTest.cxx:205
 TSHypothesisTest.cxx:206
 TSHypothesisTest.cxx:207
 TSHypothesisTest.cxx:208
 TSHypothesisTest.cxx:209
 TSHypothesisTest.cxx:210
 TSHypothesisTest.cxx:211
 TSHypothesisTest.cxx:212
 TSHypothesisTest.cxx:213
 TSHypothesisTest.cxx:214
 TSHypothesisTest.cxx:215
 TSHypothesisTest.cxx:216
 TSHypothesisTest.cxx:217
 TSHypothesisTest.cxx:218
 TSHypothesisTest.cxx:219
 TSHypothesisTest.cxx:220
 TSHypothesisTest.cxx:221
 TSHypothesisTest.cxx:222
 TSHypothesisTest.cxx:223
 TSHypothesisTest.cxx:224
 TSHypothesisTest.cxx:225
 TSHypothesisTest.cxx:226
 TSHypothesisTest.cxx:227
 TSHypothesisTest.cxx:228
 TSHypothesisTest.cxx:229
 TSHypothesisTest.cxx:230
 TSHypothesisTest.cxx:231
 TSHypothesisTest.cxx:232
 TSHypothesisTest.cxx:233
 TSHypothesisTest.cxx:234
 TSHypothesisTest.cxx:235
 TSHypothesisTest.cxx:236
 TSHypothesisTest.cxx:237
 TSHypothesisTest.cxx:238
 TSHypothesisTest.cxx:239
 TSHypothesisTest.cxx:240
 TSHypothesisTest.cxx:241
 TSHypothesisTest.cxx:242
 TSHypothesisTest.cxx:243
 TSHypothesisTest.cxx:244
 TSHypothesisTest.cxx:245
 TSHypothesisTest.cxx:246
 TSHypothesisTest.cxx:247
 TSHypothesisTest.cxx:248
 TSHypothesisTest.cxx:249
 TSHypothesisTest.cxx:250
 TSHypothesisTest.cxx:251
 TSHypothesisTest.cxx:252
 TSHypothesisTest.cxx:253
 TSHypothesisTest.cxx:254
 TSHypothesisTest.cxx:255
 TSHypothesisTest.cxx:256
 TSHypothesisTest.cxx:257
 TSHypothesisTest.cxx:258
 TSHypothesisTest.cxx:259
 TSHypothesisTest.cxx:260
 TSHypothesisTest.cxx:261
 TSHypothesisTest.cxx:262
 TSHypothesisTest.cxx:263
 TSHypothesisTest.cxx:264
 TSHypothesisTest.cxx:265
 TSHypothesisTest.cxx:266
 TSHypothesisTest.cxx:267
 TSHypothesisTest.cxx:268
 TSHypothesisTest.cxx:269
 TSHypothesisTest.cxx:270
 TSHypothesisTest.cxx:271
 TSHypothesisTest.cxx:272
 TSHypothesisTest.cxx:273
 TSHypothesisTest.cxx:274