#include "QFramework/TQIterator.h"
#include "QFramework/TQUtils.h"

#include "SFramework/TSContourScanner.h"
#include "SFramework/TSUtils.h"

#include <RooMinimizer.h>
#include <Math/Minimizer.h>
#if ROOT_VERSION_CODE < ROOT_VERSION(6,28,0)  
#include "RooMinimizerFcn.h"
#endif

//__________________________________________________________________________________|___________

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


//__________________________________________________________________________________|___________

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

//__________________________________________________________________________________|___________

void TSContourScanner::info(TString message) {
  std::cout << "SFramework/TSContourScanner: " << message.Data() << std::endl;
}

//__________________________________________________________________________________|___________

TQFolder * TSContourScanner::runCalculation(TQFolder * options){

  // expect valid inputs
  if (!fWorkspace || !fModelConfig || !options){
    return NULL;
  }

  // dataset name
  const TString datasetName = options->getTagStringDefault("datasetName", "obsData");

  TString logfile;
  // bool redirect = options->getTagString("fit.logToFile",logfile); // BW: hashed (unused variable)
  TQTaggable fitOptions;
  fitOptions.importTagsWithoutPrefix(*options,"fit.");
  fitOptions.setTagBool("adjustPOI",false);
  fitOptions.setTagString("datasetName",datasetName);

  // iterate of parameters to scan
  TQFolderIterator itr(options->getListOfFolders("?"));
  std::vector<TQFolder*> params;
  std::vector<TString> parnames;
  while(itr.hasNext()){
    TQFolder* par = itr.readNext();
    if(!par) continue;
    TString parname= par->getTagStringDefault("name",par->GetName());	
    params.push_back(par);
    parnames.push_back(parname);
    RooRealVar* v = fWorkspace->var(parname);
    if(!v){
      error(TString::Format("unable to retrieve parameter '%s' from workspace!",parname.Data()));
      return NULL;
    } else {
      v->setConstant(false);
    }
  }

  // load nominal snapshot to start from
  TString snapshotInitial = options->getTagStringDefault("snapshot.initial",options->getTagStringDefault("snapshot",TString::Format("SnSh_AllVars_Unconditional_%s", datasetName.Data())));
  fitOptions.setTagString("snapshot",snapshotInitial);
  if(!this->loadSnapshot(&fitOptions)){
    error(TString::Format("unable to load snapshot '%s'",snapshotInitial.Data()));
    return NULL;
  } else {
    info(TString::Format("using snapshot '%s'",snapshotInitial.Data()));
  }

  RooArgSet allVars(fWorkspace->allVars());
  const size_t npar(parnames.size());
  
  info("refitting unconditional minimum");
  this->setParametersConstFloat(&allVars,&fitOptions);
  
  for(size_t ipar=0; ipar<npar; ++ipar){
    auto p = parnames[ipar];
    fWorkspace->var(p)->setConstant(false);
  }
  
  this->setup(datasetName,&fitOptions);

  TString var1name,var2name;
  if(!options->getTagString("x",var1name)){
    throw std::runtime_error("unable to retrieve variable 'x'");
  }
  if(!options->getTagString("y",var2name)){
    throw std::runtime_error("unable to retrieve variable 'y'");
  }
  
  RooRealVar* var1 = fWorkspace->var(var1name);
  RooRealVar* var2 = fWorkspace->var(var2name);
  
  int npoints = options->getTagIntegerDefault("n",50);
  TQIterator itrthrsh(options->getListOfKeys("thresholds.*"),true);
  std::map<TString,double> thresholds;
  while(itrthrsh.hasNext()){
    TString key(itrthrsh.readNext()->GetName());
    double val;
    if(options->getTagDouble(key,val)){
      TQStringUtils::removeLeadingText(key,"thresholds.");
      thresholds[key] = val;
    }
  }
  if(thresholds.size() == 0){
    thresholds["1sigma"]= 2.296; // 1sigma
    thresholds["2sigma"]= 6.180; // 2sigma
    thresholds["3sigma"]=11.829; // 3sigma
    thresholds["4sigma"]=19.334; // 4sigma
    thresholds["5sigma"]=28.744; // 5sigma
  }
    
  TQFolder* result = this->contour(&fitOptions, var1,var2,npoints,thresholds);
  if(result){
    result->setTagString("content","contour");
    result->setTagInteger("nDim",2);  
    result->setTagString("x",var1name);
    result->setTagString("y",var2name);  
  }
  return result;
}

//__________________________________________________________________________________|___________

namespace {
int getVarIndex(RooMinimizer& minimizer, RooRealVar* v){
  // Verify that both variables are floating parameters of PDF

#if ROOT_VERSION_CODE < ROOT_VERSION(6,26,0)     
  auto list = TSUtils::getFunction(minimizer)->GetFloatParamList();
  int index= list->index(v);
  if(index < 0) {
    throw std::runtime_error(TString::Format("TSContourScanner::contour: %s is not a parameter of the Nll!",v->GetName()).Data());
  }
  return index;
#else
  ROOT::Math::Minimizer* mini = minimizer.fitter()->GetMinimizer();
  for(size_t i=0; i<mini->NFree(); ++i){
    if(mini->VariableName(i) == v->GetName()){
      return i;
    }
  }
  return -1;
#endif
}
}

//__________________________________________________________________________________|___________

TQFolder * TSContourScanner::contour(TQTaggable* options, RooRealVar* var1, RooRealVar* var2, unsigned int npoints, const std::map<TString,double>& thresholds){
  // slightly modified copy of RooMinimizer::contour
  RooMinimizer minimizer(*(this->fNll));

  TString minType = options->getTagStringDefault("minimizerType", ROOT::Math::MinimizerOptions::DefaultMinimizerType());
  TString minAlgo = options->getTagStringDefault("minimizerAlgo", ROOT::Math::MinimizerOptions::DefaultMinimizerAlgo());
  minimizer.minimize(minType,minAlgo);

  double min1 = var1->getVal();
  double min2 = var2->getVal();  
  
  this->resetOffset();

  double val = this->fNll->getVal();
  if(!TQUtils::isNum(val)){
    error("Nll value is non-numeric!");
    return NULL;
  }
  
  minimizer.minos(RooArgSet(*var1,*var2));

  info("determined minimum and errors:");
  info(TString::Format("  %s = %g +%g -%g",var1->GetName(),var1->getVal(),fabs(var1->getErrorHi()),fabs(var1->getErrorLo())));
  info(TString::Format("  %s = %g +%g -%g",var2->GetName(),var2->getVal(),fabs(var2->getErrorHi()),fabs(var2->getErrorLo())));

  /// BW: hashed the following 5 lines (unused variables)
  //double epsilon = 1e-9;
  // double var1up = (fabs(var1->getErrorHi()) < epsilon) ? var1->getMax()-var1->getVal() : var1->getErrorHi();
  // double var1dn = (fabs(var1->getErrorLo()) < epsilon) ? var2->getVal()-var2->getMin() : var1->getErrorLo();
  // double var2up = (fabs(var2->getErrorHi()) < epsilon) ? var1->getMax()-var1->getVal() : var2->getErrorHi();
  // double var2dn = (fabs(var2->getErrorLo()) < epsilon) ? var2->getVal()-var2->getMin() : var2->getErrorLo();
  
  if(!minimizer.fitter()) throw std::runtime_error("internal RooFit error: cannot access fitter!");
  ROOT::Math::Minimizer* mini = minimizer.fitter()->GetMinimizer();
  if(!mini) throw std::runtime_error("internal RooFit error: cannot access minimizer!");

  var1->setConstant(true);
  var2->setConstant(true);
#if ROOT_VERSION_CODE < ROOT_VERSION(6,26,0)      
  RooArgList params (*TSUtils::getFunction(minimizer)->GetFloatParamList());
#else
  RooArgList params;
  for(size_t i=0; i<mini->NFree(); ++i){
    params.add(*fWorkspace->var(mini->VariableName(i).c_str()));
  }
#endif
  RooArgList* paramSave = (RooArgList*) params.snapshot() ;

  int index1 = ::getVarIndex(minimizer,var1);
  int index2 = ::getVarIndex(minimizer,var2);
  
  TQFolder* result = new TQFolder("Contours");
  result->setTagDouble("xmin",min1);
  result->setTagDouble("ymin",min2);  
  
  // remember our original value of ERRDEF  
  Double_t errdef= mini->ErrorDef();

  for(auto it:thresholds){
    params = *paramSave;
    info("minimum and errors after correction");  
    info(TString::Format("  %s = %g +%g -%g",var1->GetName(),var1->getVal(),fabs(var1->getErrorHi()),fabs(var1->getErrorLo())));
    info(TString::Format("  %s = %g +%g -%g",var2->GetName(),var2->getVal(),fabs(var2->getErrorHi()),fabs(var2->getErrorLo())));

    TString key = it.first;
    double val = it.second;
    info(TString::Format("calculating contour for %s at %g with %d points",key.Data(),val,npoints));
    
    if(val < 0){
      throw std::runtime_error("cannot calculate contour for negative Nll value!");
    }
    // set the value corresponding to the threshold contour
    mini->SetErrorDef(val*errdef);
   
    // calculate and draw the contour
    Double_t *xcoor = new Double_t[npoints+1];
    Double_t *ycoor = new Double_t[npoints+1];

    var1->Print();
    var2->Print();
    bool ret = mini->Contour(index1,index2,npoints,xcoor,ycoor);
   
    if (!ret) {
      warn(TString::Format("TSContourScanner::contour::Minuit did not return a contour graph %s for %f",key.Data(),val).Data());
    } else {
      xcoor[npoints] = xcoor[0];
      ycoor[npoints] = ycoor[0];
      
      TQFolder* cnt = result->getFolder(TString::Format("contour_%s+",key.Data())) ;
      cnt->setTagInteger("n",npoints);  
      cnt->setTagString("label",key);
      cnt->setTagDouble("threshold",val);      
      
      for(size_t i=0; i<npoints; ++i){
        TQFolder* p = cnt->getFolder(TString::Format("p.%d+",(int)(i)));
        p->setTagDouble("x",xcoor[i]);
        p->setTagDouble("y",ycoor[i]);
      }
    }
    
    delete [] xcoor;
    delete [] ycoor;
  }
  
  
  // restore the original ERRDEF
  mini->SetErrorDef(errdef);
  
  // restore parameter values
  params = *paramSave ;
  delete paramSave ;
  
  return result ;
}
 TSContourScanner.cxx:1
 TSContourScanner.cxx:2
 TSContourScanner.cxx:3
 TSContourScanner.cxx:4
 TSContourScanner.cxx:5
 TSContourScanner.cxx:6
 TSContourScanner.cxx:7
 TSContourScanner.cxx:8
 TSContourScanner.cxx:9
 TSContourScanner.cxx:10
 TSContourScanner.cxx:11
 TSContourScanner.cxx:12
 TSContourScanner.cxx:13
 TSContourScanner.cxx:14
 TSContourScanner.cxx:15
 TSContourScanner.cxx:16
 TSContourScanner.cxx:17
 TSContourScanner.cxx:18
 TSContourScanner.cxx:19
 TSContourScanner.cxx:20
 TSContourScanner.cxx:21
 TSContourScanner.cxx:22
 TSContourScanner.cxx:23
 TSContourScanner.cxx:24
 TSContourScanner.cxx:25
 TSContourScanner.cxx:26
 TSContourScanner.cxx:27
 TSContourScanner.cxx:28
 TSContourScanner.cxx:29
 TSContourScanner.cxx:30
 TSContourScanner.cxx:31
 TSContourScanner.cxx:32
 TSContourScanner.cxx:33
 TSContourScanner.cxx:34
 TSContourScanner.cxx:35
 TSContourScanner.cxx:36
 TSContourScanner.cxx:37
 TSContourScanner.cxx:38
 TSContourScanner.cxx:39
 TSContourScanner.cxx:40
 TSContourScanner.cxx:41
 TSContourScanner.cxx:42
 TSContourScanner.cxx:43
 TSContourScanner.cxx:44
 TSContourScanner.cxx:45
 TSContourScanner.cxx:46
 TSContourScanner.cxx:47
 TSContourScanner.cxx:48
 TSContourScanner.cxx:49
 TSContourScanner.cxx:50
 TSContourScanner.cxx:51
 TSContourScanner.cxx:52
 TSContourScanner.cxx:53
 TSContourScanner.cxx:54
 TSContourScanner.cxx:55
 TSContourScanner.cxx:56
 TSContourScanner.cxx:57
 TSContourScanner.cxx:58
 TSContourScanner.cxx:59
 TSContourScanner.cxx:60
 TSContourScanner.cxx:61
 TSContourScanner.cxx:62
 TSContourScanner.cxx:63
 TSContourScanner.cxx:64
 TSContourScanner.cxx:65
 TSContourScanner.cxx:66
 TSContourScanner.cxx:67
 TSContourScanner.cxx:68
 TSContourScanner.cxx:69
 TSContourScanner.cxx:70
 TSContourScanner.cxx:71
 TSContourScanner.cxx:72
 TSContourScanner.cxx:73
 TSContourScanner.cxx:74
 TSContourScanner.cxx:75
 TSContourScanner.cxx:76
 TSContourScanner.cxx:77
 TSContourScanner.cxx:78
 TSContourScanner.cxx:79
 TSContourScanner.cxx:80
 TSContourScanner.cxx:81
 TSContourScanner.cxx:82
 TSContourScanner.cxx:83
 TSContourScanner.cxx:84
 TSContourScanner.cxx:85
 TSContourScanner.cxx:86
 TSContourScanner.cxx:87
 TSContourScanner.cxx:88
 TSContourScanner.cxx:89
 TSContourScanner.cxx:90
 TSContourScanner.cxx:91
 TSContourScanner.cxx:92
 TSContourScanner.cxx:93
 TSContourScanner.cxx:94
 TSContourScanner.cxx:95
 TSContourScanner.cxx:96
 TSContourScanner.cxx:97
 TSContourScanner.cxx:98
 TSContourScanner.cxx:99
 TSContourScanner.cxx:100
 TSContourScanner.cxx:101
 TSContourScanner.cxx:102
 TSContourScanner.cxx:103
 TSContourScanner.cxx:104
 TSContourScanner.cxx:105
 TSContourScanner.cxx:106
 TSContourScanner.cxx:107
 TSContourScanner.cxx:108
 TSContourScanner.cxx:109
 TSContourScanner.cxx:110
 TSContourScanner.cxx:111
 TSContourScanner.cxx:112
 TSContourScanner.cxx:113
 TSContourScanner.cxx:114
 TSContourScanner.cxx:115
 TSContourScanner.cxx:116
 TSContourScanner.cxx:117
 TSContourScanner.cxx:118
 TSContourScanner.cxx:119
 TSContourScanner.cxx:120
 TSContourScanner.cxx:121
 TSContourScanner.cxx:122
 TSContourScanner.cxx:123
 TSContourScanner.cxx:124
 TSContourScanner.cxx:125
 TSContourScanner.cxx:126
 TSContourScanner.cxx:127
 TSContourScanner.cxx:128
 TSContourScanner.cxx:129
 TSContourScanner.cxx:130
 TSContourScanner.cxx:131
 TSContourScanner.cxx:132
 TSContourScanner.cxx:133
 TSContourScanner.cxx:134
 TSContourScanner.cxx:135
 TSContourScanner.cxx:136
 TSContourScanner.cxx:137
 TSContourScanner.cxx:138
 TSContourScanner.cxx:139
 TSContourScanner.cxx:140
 TSContourScanner.cxx:141
 TSContourScanner.cxx:142
 TSContourScanner.cxx:143
 TSContourScanner.cxx:144
 TSContourScanner.cxx:145
 TSContourScanner.cxx:146
 TSContourScanner.cxx:147
 TSContourScanner.cxx:148
 TSContourScanner.cxx:149
 TSContourScanner.cxx:150
 TSContourScanner.cxx:151
 TSContourScanner.cxx:152
 TSContourScanner.cxx:153
 TSContourScanner.cxx:154
 TSContourScanner.cxx:155
 TSContourScanner.cxx:156
 TSContourScanner.cxx:157
 TSContourScanner.cxx:158
 TSContourScanner.cxx:159
 TSContourScanner.cxx:160
 TSContourScanner.cxx:161
 TSContourScanner.cxx:162
 TSContourScanner.cxx:163
 TSContourScanner.cxx:164
 TSContourScanner.cxx:165
 TSContourScanner.cxx:166
 TSContourScanner.cxx:167
 TSContourScanner.cxx:168
 TSContourScanner.cxx:169
 TSContourScanner.cxx:170
 TSContourScanner.cxx:171
 TSContourScanner.cxx:172
 TSContourScanner.cxx:173
 TSContourScanner.cxx:174
 TSContourScanner.cxx:175
 TSContourScanner.cxx:176
 TSContourScanner.cxx:177
 TSContourScanner.cxx:178
 TSContourScanner.cxx:179
 TSContourScanner.cxx:180
 TSContourScanner.cxx:181
 TSContourScanner.cxx:182
 TSContourScanner.cxx:183
 TSContourScanner.cxx:184
 TSContourScanner.cxx:185
 TSContourScanner.cxx:186
 TSContourScanner.cxx:187
 TSContourScanner.cxx:188
 TSContourScanner.cxx:189
 TSContourScanner.cxx:190
 TSContourScanner.cxx:191
 TSContourScanner.cxx:192
 TSContourScanner.cxx:193
 TSContourScanner.cxx:194
 TSContourScanner.cxx:195
 TSContourScanner.cxx:196
 TSContourScanner.cxx:197
 TSContourScanner.cxx:198
 TSContourScanner.cxx:199
 TSContourScanner.cxx:200
 TSContourScanner.cxx:201
 TSContourScanner.cxx:202
 TSContourScanner.cxx:203
 TSContourScanner.cxx:204
 TSContourScanner.cxx:205
 TSContourScanner.cxx:206
 TSContourScanner.cxx:207
 TSContourScanner.cxx:208
 TSContourScanner.cxx:209
 TSContourScanner.cxx:210
 TSContourScanner.cxx:211
 TSContourScanner.cxx:212
 TSContourScanner.cxx:213
 TSContourScanner.cxx:214
 TSContourScanner.cxx:215
 TSContourScanner.cxx:216
 TSContourScanner.cxx:217
 TSContourScanner.cxx:218
 TSContourScanner.cxx:219
 TSContourScanner.cxx:220
 TSContourScanner.cxx:221
 TSContourScanner.cxx:222
 TSContourScanner.cxx:223
 TSContourScanner.cxx:224
 TSContourScanner.cxx:225
 TSContourScanner.cxx:226
 TSContourScanner.cxx:227
 TSContourScanner.cxx:228
 TSContourScanner.cxx:229
 TSContourScanner.cxx:230
 TSContourScanner.cxx:231
 TSContourScanner.cxx:232
 TSContourScanner.cxx:233
 TSContourScanner.cxx:234
 TSContourScanner.cxx:235
 TSContourScanner.cxx:236
 TSContourScanner.cxx:237
 TSContourScanner.cxx:238
 TSContourScanner.cxx:239
 TSContourScanner.cxx:240
 TSContourScanner.cxx:241
 TSContourScanner.cxx:242
 TSContourScanner.cxx:243
 TSContourScanner.cxx:244
 TSContourScanner.cxx:245
 TSContourScanner.cxx:246
 TSContourScanner.cxx:247
 TSContourScanner.cxx:248
 TSContourScanner.cxx:249
 TSContourScanner.cxx:250
 TSContourScanner.cxx:251
 TSContourScanner.cxx:252
 TSContourScanner.cxx:253
 TSContourScanner.cxx:254
 TSContourScanner.cxx:255
 TSContourScanner.cxx:256
 TSContourScanner.cxx:257
 TSContourScanner.cxx:258
 TSContourScanner.cxx:259
 TSContourScanner.cxx:260
 TSContourScanner.cxx:261
 TSContourScanner.cxx:262
 TSContourScanner.cxx:263
 TSContourScanner.cxx:264
 TSContourScanner.cxx:265
 TSContourScanner.cxx:266
 TSContourScanner.cxx:267
 TSContourScanner.cxx:268
 TSContourScanner.cxx:269
 TSContourScanner.cxx:270
 TSContourScanner.cxx:271
 TSContourScanner.cxx:272
 TSContourScanner.cxx:273