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

// #define _DEBUG_
#include "QFramework/TQLibrary.h"

#include "SFramework/TSSignificanceCalculator.h"

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

#include "QFramework/TQStringUtils.h"
#include "QFramework/TQUtils.h"
#include "SFramework/TSUtils.h"
#include "QFramework/TQIterator.h"

ClassImp(TSSignificanceCalculator)


//__________________________________________________________________________________|___________

TSSignificanceCalculator::TSSignificanceCalculator() : TSStatisticsCalculator("TSSignificanceCalculator") {
}


//__________________________________________________________________________________|___________

TSSignificanceCalculator::TSSignificanceCalculator(RooWorkspace * ws) : TSStatisticsCalculator("TSSignificanceCalculator",ws) {
}


//__________________________________________________________________________________|___________

TSSignificanceCalculator::~TSSignificanceCalculator() {
}


//__________________________________________________________________________________|___________

void TSSignificanceCalculator::info(TString message) {

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

//__________________________________________________________________________________|___________

TQFolder* TSSignificanceCalculator::runFit(TQFolder* result, RooAbsPdf* pdf, RooDataSet* data, const TString& fitid,const RooArgSet& pois, const RooArgSet& nuis, bool conditional, TQTaggable* fitOptions, bool save){
  fitOptions->setTagString("id",fitid);

  info(TString::Format("runCalculation(): Running %s fit on %s",(conditional ? "conditional" : "unconditional"), data->GetName()));
  TQFolder* fitResult = fitPdfToData(pdf, data, nuis, fitOptions);
  
  // save status after conditional fit as snapshot
  if(save){
    result->addObject(fitResult, "FitResults+ ::"+fitid);
    RooArgSet nuisAndPois(pois,nuis);
    TString snsh(TString::Format("SnSh_NuisPOI_%s_%s",(conditional ? "Conditional" : "Unconditional"),fitid.Data()));
    info(TString::Format("runCalculation(): saving snapshot %s",snsh.Data()));
    fWorkspace->saveSnapshot(snsh, nuisAndPois);
  }
  
  return fitResult;
}

#define nan std::numeric_limits<double>::quiet_NaN()

//__________________________________________________________________________________|___________

void TSSignificanceCalculator::makeSummary(TQFolder* target, TQFolder* result_constrained, TQFolder* result_unconstrained, const TString& label, RooArgSet& pois, bool blinded){
	if (!result_constrained || !result_unconstrained) return;
  int status_constrained = result_constrained->getTagIntegerDefault("status", -1);
  int strategy_constrained = result_constrained->getTagIntegerDefault("strategy", -1);
  int status_unconstrained = result_unconstrained->getTagIntegerDefault("status", -1);
  int strategy_unconstrained = result_unconstrained->getTagIntegerDefault("strategy", -1);
  bool ok = true;
  double min_constrained   = result_constrained->getTagDoubleDefault("minNll",nan);
  double min_unconstrained = result_unconstrained->getTagDoubleDefault("minNll",nan);
  int ndim_constrained = result_constrained->getTagIntegerDefault("nDim",-1);
  int ndim_unconstrained = result_unconstrained->getTagIntegerDefault("nDim",-1);
  
  if(status_constrained < 0 || !TQUtils::isNum(min_constrained)){
    error(TString::Format("fit error: constrained fit failed with status '%d' using strategy '%d' in %d dimensions, minimum was '%g'",status_constrained,strategy_constrained,ndim_constrained,min_constrained));
    ok=false;
  }
  if(status_unconstrained < 0 || !TQUtils::isNum(min_unconstrained)){
    error(TString::Format("fit error: unconstrained fit failed with status '%d' using strategy '%d' in %d dimensions, minimum was '%g'",status_unconstrained,strategy_unconstrained,ndim_constrained,min_unconstrained));
    ok=false;
  }

  int ndof = pois.getSize();
  if(ndof < 1){
    error(TString::Format("fit error: cannot calculate significance with n_dof = %d",ndof));
    ok = false;
  }
  Double_t D = 2 * (min_constrained - min_unconstrained);
  TQFolder* floatParsFinal = result_unconstrained->getFolder("floatParsFinal");
  if(floatParsFinal){
    ROOFIT_ITERATE(pois,RooAbsArg,obj){
      TQFolder* f = floatParsFinal->getFolder(obj->GetName());
      if(f && !blinded){
        double value = f->getTagDoubleDefault("val",0);
        double errLow = f->getTagDoubleDefault("errLow",0);
        double errHigh = f->getTagDoubleDefault("errHigh",0);
        target->setTagDouble(TString::Format("%s_%s_val",obj->GetName(),label.Data()),value);
        target->setTagDouble(TString::Format("%s_%s_errLow",obj->GetName(),label.Data()),errLow);
        target->setTagDouble(TString::Format("%s_%s_errHigh",obj->GetName(),label.Data()),errHigh);
        if(value<0) D = 0;
      }
    }
  } else {
    ok = false;
  }
  if(ok){
    if(D >= 0){
      double pval = (ndof == 1 ? 1.-ROOT::Math::normal_cdf(sqrt(D),1,0) : ROOT::Math::chisquared_cdf_c(D,ndof));
      double Z0 = RooStats::PValueToSignificance(pval);
      if(!blinded){
	target->setTagInteger(TString::Format("ndof_%s",label.Data()), ndof);	
	target->setTagDouble(TString::Format("Z0_%s",label.Data()), Z0);
        target->setTagDouble(TString::Format("p0_%s",label.Data()), pval);
        info(TString::Format("%s. significance Z0 = %.1f (p=%g), nll was %.3f (constrained, ndim=%d) vs %.3f (unconstrained, ndim=%d)",label.Data(), Z0, pval, min_constrained, ndim_constrained, min_unconstrained, ndim_unconstrained));
      } else {
        warn(TString::Format("%s. fit results retrieved successfully, suppressing printout due to active blinding!",label.Data()));
      }
    } else {
      error(TString::Format("fit error: Nll minimum for constrained fit better than for unconstrained: %.3f < %.3f",min_constrained,min_unconstrained));
      target->setTagDouble(TString::Format("minNll_%s_constrained",label.Data()), min_constrained);
      target->setTagDouble(TString::Format("minNll_%s_unconstrained",label.Data()), min_unconstrained);
    } 
  } else {
    target->setTagDouble(TString::Format("minNll_%s_constrained",label.Data()), min_constrained);
    target->setTagDouble(TString::Format("minNll_%s_unconstrained",label.Data()), min_unconstrained);
  }
}

//__________________________________________________________________________________|___________
                 
TQFolder * TSSignificanceCalculator::runCalculation(TQFolder * config) {
  DEBUGclass("starting calculation");
  if (!fWorkspace || !fModelConfig || !config) {
    return NULL;
  }
  
  RooArgSet pois(this->getPOIs(config));
  RooArgSet nuis(this->getNuisanceParameters(config));
  RooAbsPdf *		pdf			= fModelConfig->GetPdf();
  TString dataName = config->getTagStringDefault("dataset","asimovData_1");
  RooDataSet *	data		= (RooDataSet*)fWorkspace->data(dataName);
  if (!data) {
    error(TString::Format("unable to obtain dataset '%s'!",dataName.Data()));
    return NULL;
  }


  Bool_t doBlinded			= config->getTagBoolDefault("blinded",false);
  Bool_t save	= config->getTagBoolDefault("save",true);

  TQTaggable fitOptions;
  fitOptions.importTagsWithoutPrefix(config,"fit.");
  fitOptions.setTagBool("reuseNll",config->getTagBoolDefault("fit.reuseNll",true));
  
  // the folder to write the results to 
  TQFolder * result = TQFolder::newFolder("Significance");
  
  TString name(config->getTagStringDefault("name",config->GetName()));
  TString pname(config->getTagStringDefault("parameter","mu"));
  
  TQFolder * fitResult_mu0	= NULL;
  TQFolder * fitResult_muhat	= NULL;

  TString snapshotName = fitOptions.getTagStringDefault("snapshot","SnSh_AllVars_Nominal");
  TString snapshotCond   = config->getTagStringDefault("snapshot.conditional",  snapshotName);
  TString snapshotUncond = config->getTagStringDefault("snapshot.unconditional",snapshotName);  
  
  
  std::vector<TString> constPars = fitOptions.getTagVString("constPars");
  std::vector<TString> floatPars = fitOptions.getTagVString("floatPars");
  std::vector<TString> constParsExcept = fitOptions.getTagVString("constPars.except");
  std::vector<TString> floatParsExcept = fitOptions.getTagVString("floatPars.except");  
  std::vector<TString> floatParsWithPOIs(floatPars);
  std::vector<TString> constParsWithPOIs(constPars);
  std::vector<TString> poinames;
  TSUtils::getParameterNames(pois,floatParsWithPOIs);
  TSUtils::getParameterNames(pois,constParsWithPOIs);
  TSUtils::getParameterNames(pois,poinames);
  std::vector<TString> floatParsCond  (floatPars);
  std::vector<TString> constParsCond  (constParsWithPOIs);
  std::vector<TString> floatParsUncond(floatParsWithPOIs);
  std::vector<TString> constParsUncond(constPars);
  TQUtils::append(floatParsCond  ,fitOptions.getTagVString("floatPars.conditional"));
  TQUtils::append(constParsCond  ,fitOptions.getTagVString("constPars.conditional"));
  TQUtils::append(floatParsUncond,fitOptions.getTagVString("floatPars.unconditional"));
  TQUtils::append(constParsUncond,fitOptions.getTagVString("constPars.unconditional"));

  TQTaggable fitOptions_uncond(fitOptions);
  fitOptions_uncond.removeTag("snapshot");        
  fitOptions_uncond.setTagString("snapshot",snapshotUncond);  
  fitOptions_uncond.removeTags("floatPars.*");
  fitOptions_uncond.removeTags("constPars.*");
  fitOptions_uncond.setTagList("floatPars",floatParsUncond);
  fitOptions_uncond.setTagList("constPars",constParsUncond);
  fitOptions_uncond.setTagList("floatPars.except",floatParsExcept);
  fitOptions_uncond.setTagList("constPars.except",constParsExcept);      

  TQTaggable fitOptions_cond(fitOptions);
  //some options are pointless for the conditional fit, so semi-hardcode them
  fitOptions_cond.removeTag("snapshot");      
  fitOptions_cond.setTagString("snapshot",snapshotCond);  
  fitOptions_cond.removeTags("floatPars.*");
  fitOptions_cond.removeTags("constPars.*");
  fitOptions_cond.setTagList("floatPars",floatParsCond);
  fitOptions_cond.setTagList("constPars",constParsCond);
  fitOptions_cond.setTagList("floatPars.except",floatParsExcept);
  fitOptions_cond.setTagList("constPars.except",constParsExcept);    

  bool uncondFirst = config->getTagBoolDefault("doUnconditionalFirst",true);
  if(uncondFirst){
    fitResult_muhat = runFit(result,pdf,data,TString::Format("%s_%shat",name.Data(),pname.Data()), pois,nuis,false,&fitOptions_uncond,save);
  }
  if (config->getTagBoolDefault("doConditional",true)) {
    if(config->getTagBoolDefault("conditionPOIs",true)){
      for(const auto& poi:poinames){
	double val = config->getTagDoubleDefault("conditionPOIvalue", 0.);
	info(TString::Format("for conditional fit, choosing '%s=%g'",poi.Data(),val));
	fitOptions_cond.setTagDouble("presetParam."+poi, val);
      }
    }
    fitResult_mu0   = runFit(result,pdf,data,TString::Format("%s_%s0"  ,name.Data(),pname.Data()), pois,nuis,true ,&fitOptions_cond,save);
  }
  if(!uncondFirst){
    fitResult_muhat = runFit(result,pdf,data,TString::Format("%s_%shat",name.Data(),pname.Data()), pois,nuis,false,&fitOptions_uncond,save);
  }
  
  makeSummary(result,fitResult_mu0,fitResult_muhat,name.Data(),pois,doBlinded);
  
  if(!save){
    delete fitResult_mu0;
    delete fitResult_muhat;
  }

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