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

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

#include "TKey.h"
#include "TMath.h"
#include "Math/MinimizerOptions.h"
#include "RooNLLVar.h"
#include "RooAbsPdf.h"
#include "RooDataSet.h"
#include "RooFitResult.h"
#include "RooRealVar.h"
#include "Math/ProbFuncMathCore.h"
#include "Math/QuantFuncMathCore.h"
#include "TStopwatch.h"

#include "QFramework/TQStringUtils.h"
#include "QFramework/TQIterator.h"
#include "QFramework/TQLibrary.h"

ClassImp(TSImpactCalculator)


//__________________________________________________________________________________|___________

TSImpactCalculator::TSImpactCalculator(RooWorkspace * ws, TQFolder* snapshots) : TSStatisticsCalculator("TSImpactCalculator",ws,snapshots) {
}


//__________________________________________________________________________________|___________

TSImpactCalculator::~TSImpactCalculator() {
}


//__________________________________________________________________________________|___________

void TSImpactCalculator::info(TString message) {

  std::cout << "SFramework/TSImpactCalculator: " << message.Data() << std::endl;
}


//__________________________________________________________________________________|___________

TQFolder* TSImpactCalculator::fit(TQTaggable* options, RooAbsPdf * pdf, RooDataSet * data, const RooArgSet& listOfPOIs, const RooArgSet& nuis, const TString& name, const TString& pname /*, bool invert*/) {
	// run a fit for a given setup
  options->setTagString("id",name);
  
  // take care of setting the "right" parameters constant/floating
  options->removeTags("constPars");
  options->removeTags("floatPars");
  options->setTagString("constPars",pname);
  std::vector<TString> poinames;
  TSUtils::getParameterNames(listOfPOIs,poinames);
  options->setTagString("floatPars",TQStringUtils::concat(poinames,","));
  info("running fit '"+name+"'");
  TQFolder * fitResult = fitPdfToData(pdf, data, nuis, options);
  fitResult->SetName(name);
  return fitResult;
}

//__________________________________________________________________________________|___________

void TSImpactCalculator::storeImpacts(TQFolder* result,const TString& parname, TQFolder* nominal,TQFolder* up,TQFolder* down){
  // store the impacts in a TQFolder
  if(!up){
    error("unable to retrieve fit results of up variation!");
    return;
  }
  if(!down){
    error("unable to retrieve fit results of down variation!");
    return;
  }
  TQFolderIterator itr(nominal->getListOfFolders("?"));
  while(itr.hasNext()){
    TQFolder * poi = itr.readNext();
    if (!poi) {
      continue;
    }
    TString poiname(poi->GetName());
    TQFolder* poiresult = result->getFolder(poiname+"+")->getFolder(parname+"+");
    double val_uncond = poi->getTagDoubleDefault("val", 0.);
    
    TQFolder* poi_up = up->getFolder(poiname);
    
    if(!poi_up){
      error("unable to retrieve up variation");
      return;
    }
    double val_up = poi_up->getTagDoubleDefault("val", 0.);
    
    TQFolder* poi_down = down->getFolder(poiname);
    if(!poi_down){
      error("unable to retrieve up variation");
      return;
    }
    double val_down = poi_down->getTagDoubleDefault("val", 0.);

    Double_t high = val_up - val_uncond;
    Double_t low  = val_down - val_uncond;
    Double_t span = std::abs(val_up - val_down);
    Double_t avg = 0.5 * (std::abs(val_up-val_uncond) + std::abs(val_down-val_uncond));
    double absval = std::abs(val_uncond);

    poiresult->setTagDouble("Avg", avg);
    poiresult->setTagDouble("Low", low);
    poiresult->setTagDouble("High", high);
    poiresult->setTagDouble("Span", span);
    poiresult->setTagDouble("Avg_Rel", avg / absval);
    poiresult->setTagDouble("Low_Rel", low / absval);
    poiresult->setTagDouble("High_Rel", high / absval);
    poiresult->setTagDouble("Span_Rel", span / absval);
  }
}

//__________________________________________________________________________________|___________

bool TSImpactCalculator::runPreFit(TQTaggable* options, const RooAbsCollection& listOfPOIs, TQFolder* target){
  TQTaggable snapshotoptions(options);
  
  TString datasetName = options->getTagStringDefault("dataset",options->getTagStringDefault("datasetName", "asimovData_1"));
  snapshotoptions.importTagsWithoutPrefix(options,"fit.");
  snapshotoptions.setTagString("datasetName",datasetName);
  
  // std::vector<TString> namesOfPOIs;
  // TSUtils::getParameterNames(listOfPOIs,namesOfPOIs);
  
  std::vector<TString> namesOfNPs;
  TSUtils::getParameterNames(this->getNuisanceParameters(options),namesOfNPs);
  // TString npnames = TQStringUtils::concat(namesOfNPs,",");

  TString minosVars = options->getValuesOfTags("singles.*");

  TString snapshot_uncond = options->getTagStringDefault("snapshot.unconditional","SnSh_AllVars_Impact_Unconditional_"+datasetName);
  TString snapshot_cond   = options->getTagStringDefault("snapshot.conditional",  "SnSh_AllVars_Impact_Conditional_"  +datasetName);
  
  snapshotoptions.setTagBool("runHesse", false);
  snapshotoptions.setTagBool("runMinos", true);
  snapshotoptions.setTagList("runMinosVars", minosVars);
  
  snapshotoptions.setTagString("id","unconditional");
  snapshotoptions.setTagString("snapshot",snapshot_uncond);
  snapshotoptions.setTagList("floatPars",namesOfNPs);
  
  // snapshotoptions.setTagString("resultName","unconditional");
  
  if(!this->loadSnapshot(&snapshotoptions)){
    error("failed obtaining unconditional snapshot!");
    return false;
  }
  
  TQFolder* nominal = TSUtils::convertParameterList(&listOfPOIs);
  nominal->SetName("nominal");
  target->addFolder(nominal);
  
  snapshotoptions.setTagString("id","conditional");
  snapshotoptions.setTagString("snapshot",snapshot_cond);
  snapshotoptions.removeTags("floatPars.*");
  // snapshotoptions.setTagString("floatPars",options->getTagStringDefault("floatPars",""));
  snapshotoptions.setTagList("constPars",namesOfNPs);
  snapshotoptions.setTagString("resultName","conditional");
  snapshotoptions.setTagString("snapshot.nominal",snapshot_uncond);
  this->setParametersConstFloat(fWorkspace->allVars(),&snapshotoptions);
  fWorkspace->saveSnapshot(snapshot_cond.Data(), fWorkspace->allVars());
  //  if(!this->loadSnapshot(&snapshotoptions)){
  // 	error("failed obtaining conditional snapshot!");
  // 	return false;
  // }
  return true;
}

//__________________________________________________________________________________|___________

TQFolder * TSImpactCalculator::runCalculation(TQFolder * config) {

  if (!fWorkspace || !fModelConfig || !config) {
    return NULL;
  }

  /* get the Point Of Interest, PDF, and data */
  TString datasetName   = config->getTagStringDefault("dataset",config->getTagStringDefault("datasetName", "asimovData_1"));
  RooArgSet nuis	= this->getNuisanceParameters(config);
  RooArgSet listOfPOIs	= this->getPOIs(config);
  RooArgSet nuisAndPOIs(nuis);
  nuisAndPOIs.add(listOfPOIs);

  if (listOfPOIs.getSize() == 0) {
    error("no POIs found!");
    return NULL;
  }

  // iterate over groups ("group.*") and determine individual uncertainties on POIs
  TSUtils::expandKeys(&nuisAndPOIs,config);
  TQIterator itrGroups(config->getListOfKeys("group.*"), true);

 TQFolder* retval = TQFolder::newFolder(config->GetName());
  if(!runPreFit(config,listOfPOIs,retval)){
    return NULL;
  }
  bool invert = config->getTagBoolDefault("invert",false);

  TString startingsnapshot;
  if (invert){
    startingsnapshot = config->getTagStringDefault("snapshot.conditional",
						   TString::Format("SnSh_AllVars_Impact_Conditional_%s",datasetName.Data()));
  }
  else {
    startingsnapshot = config->getTagStringDefault("snapshot.unconditional",
						   TString::Format("SnSh_AllVars_Impact_Unconditional_%s",datasetName.Data()));
  }
  
  TQFolder* nominal = retval->getFolder("nominal");

  TQFolder* fitresults = retval->getFolder("FitResults+");
  TQFolder* impacts = retval->getFolder(this->fFolderName+"+");
  RooAbsPdf* pdf = fModelConfig->GetPdf();
  RooDataSet* data = dynamic_cast<RooDataSet*>(fWorkspace->data(datasetName));

  TQTaggable fitOptions;
  fitOptions.setTagBool("runHesse", false);
  fitOptions.setTagBool("runMinos", false);
  fitOptions.setTagBool("loadSnapshot", false);

  fitOptions.importTagsWithoutPrefix(config,"fit.");
  // do not change paramteter values, we're re-using the snapshot from the unconditional fit.
  // Changing paramter values here increases the chances to converge to a different minimum causing all sorts of problems!
  fitOptions.removeTags("initParam*");

  // iterate over groups ("group.*") and determine individual uncertainties on POIs
  while (itrGroups.hasNext()) {
    TString name = itrGroups.readNext()->GetName();
    TString filter;
    if(!config->getTagString(name, filter)) continue;
    TQStringUtils::removeLeadingText(name, "group.");
    if(filter.Contains("*")){
      error("wildcarded groups not supported: "+filter);
      continue;
    }

    std::vector<RooRealVar*> params;
    ROOFIT_ITERATE(nuisAndPOIs,RooAbsArg,var){
      if(TQStringUtils::matchesFilter(var->GetName(),filter)){
        params.push_back((RooRealVar*)var);
      }
    }

    if(params.size() == 0){
      warn(TString::Format("no variables found matching filter '%s'",filter.Data()));
      continue;
    }

    this->fWorkspace->loadSnapshot(startingsnapshot);
    for(auto var:params){
      const double nom = var->getVal();
      const double up = var->getErrorHi();
      var->setVal(nom+std::abs(up));
      std::cout<<"Set parameter "<< var->GetName()<<" to "<<nom+std::abs(up)<<std::endl;
    }

    TQFolder* fitresultUp = fit(&fitOptions, pdf, data, listOfPOIs, nuisAndPOIs, name+"_Up", name /*, invert*/);
    fitresults->addObject(fitresultUp);

    this->fWorkspace->loadSnapshot(startingsnapshot);
    for(auto var:params){
      const double nom = var->getVal();
      const double down = var->getErrorLo();
      var->setVal(nom-std::abs(down));
      std::cout<<"Set parameter "<< var->GetName()<<" to "<<nom-std::abs(down)<<std::endl;
    }

    TQFolder* fitresultDown = fit(&fitOptions, pdf, data, listOfPOIs, nuisAndPOIs, name+"_Down", name /*, invert*/);
    fitresults->addObject(fitresultDown);

    if(impacts && nominal && fitresultUp && fitresultDown){
      this->storeImpacts(impacts,name,
                         nominal,fitresultUp->getFolder("floatParsFinal"),fitresultDown->getFolder("floatParsFinal"));
    } else {
      error(TString::Format("error measuring impact for '%s'",name.Data()));
    }
  }

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