#include "QFramework/TQSampleInitializer.h"
#include "QFramework/TQSample.h"
#include "TFile.h"
#include "QFramework/TQUtils.h"
#include "QFramework/TQIterator.h"

#include "definitions.h"

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

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <cstdlib>

////////////////////////////////////////////////////////////////////////////////////////////////
//
// TQSampleInitializer:
//
// A simple sample initializer, capable of traversing a given sample folder structure,
// locating the nTuples in the form of ROOT files an TTrees contained therein.
// 
// This simple initializer variant has two means of determining the correct file path:
// - the ".xsp.filepath" tag which might be set on the individual sample
// - the file system directory announced to the initializer via readDirectory
// this variant will use the sample name in conjuction with the
// "filenamePrefix" and "filenameSuffix" tags to find the sample root file
//
// Additionally, the tree name is automatically determined with the following method
// - the file is opened and any objects inheriting from TTree are investigated
// - their names are compared to the tag "treeName" (default: "*" unless specified differently
//     by the tag with key "treeName" set on the initializer)
// - if there is exactly one match, the initializer proceeds.
// - in the case of zero or multiple matches, it will terminate with an error.
//
// After the tree name has been determined, the number of events is retrieved from
// the tree. In conjunction with the luminosity (can be set as "luminosity" tag on any
// parent folder of the sample or the sample itself, as well as on the initializer)
// and the tags "kfactor" and "filtereff" on the sample, it is used to set the sample
// normalization.
// 
////////////////////////////////////////////////////////////////////////////////////////////////

ClassImp(TQSampleInitializer)

TQSampleInitializer::TQSampleInitializer() :
TQSampleVisitor("init")
{
  // default constructor
}

TQSampleInitializer::TQSampleInitializer(const TString& dirname, int depth) :
TQSampleVisitor("init")
{
  this->readDirectory(dirname,depth);
}

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

namespace {
  inline TString makeFullPath(const TString& prefix, const TString& path){
    TString tmppath = TQStringUtils::makeASCII(path);
    int hashpos = tmppath.First("#");
    if(hashpos >= 0 && hashpos < tmppath.Length()) tmppath.Remove(hashpos);
    TString basepath(TQStringUtils::makeASCII(prefix));
    TQStringUtils::ensureLeadingText(tmppath,basepath);
    return tmppath;
  }
}

int TQSampleInitializer::visitSample(TQSample * sample, TString& message) {
  int retCode = this->visitSampleInternal(sample, message);
  if (retCode == visitOK) {
    //apply timestamp of initialization and traceID to this sample (important for merging!)
    this->stamp(sample);
  }
  return retCode;
}

int TQSampleInitializer::visitSampleInternal(TQSample * sample, TString& message){
  // visit and initialize the given sample
  if(sample->isSubSample()) return visitOK;
  if(sample->getTagBoolDefault("~isInitialized",false)) return visitSKIPPEDOK;
  
  TString sampleName = sample->GetName();
  sample->getTagString("name",sampleName);
  sample->getTagString(".xsp.sampleName",sampleName);
  TString samplePath = sample->getPath();
  
  /* get the best filepath */
  TString filename = this->getTagStringDefault("filenamePrefix","") + sampleName + this->getTagStringDefault("filenameSuffix",".root");
  TString subdir = this->getTagStringDefault("subdirectory","./");
  
  TString path;
  TString fullpath;
  TString fpattern;
  if(sample->getTagString(".xsp.filepath",path)){
    // the path is already given by the XSectionParser
    // there is not much to do, just visit the sample to make sure that it's readable and contains the right tree
    fullpath = sample->replaceInText(path);
    fullpath = TQStringUtils::makeASCII(fullpath);
    if(!this->initializeSample(sample,fullpath,message)) {
      if (getExitOnFail()) exit(66); 
      return visitFAILED;
    }
    this->setSampleNormalization(sample);
    return visitOK;
  } else if(this->fPaths){
    // no given absolute path, but a set of paths on local file system or EOS
    // this is the default case
    DEBUGclass("trying to retrieve object paths for filename '%s' from subdir '%s'",filename.Data(),subdir.Data());
    //this->fPaths->print("rdt");
    //the first lever of folders represents different storage systems (for hybrid reading, e.g., partially from a remote storage/server and some mounted offline storage)
    TList* paths = new TList();
    paths->SetOwner(true);
    TQFolderIterator storageSystems(this->fPaths->getListOfFolders("?"),true); //first level of subfolders represents different file systems/sources (to not mix up local and remote file access)
    while (storageSystems.hasNext()) {
      TQFolder* storageSys = storageSystems.readNext();
      storageSys->detachFromBase(); //temporarily detach it from the base folder to prevent the name of this folder to appear in the paths obtained below
      DEBUG("scanning storage system '%s'",storageSys->GetName());
      TList* thesePaths = storageSys->getObjectPaths(filename,subdir,TObjString::Class());
      DEBUG("found %d paths matching '%s' in '%s'",thesePaths->GetEntries(),filename.Data(),subdir.Data());
      this->fPaths->addFolder(storageSys);
      TQStringIterator pathItr(thesePaths,false); //don't delete the list after iteration
      while (pathItr.hasNext()) {
        TObjString* p = pathItr.readNext();
        if (!p) continue;
        //modify the name into a full name making use of the basepath set for this particular storageSys
        TString s = makeFullPath(storageSys->getTagStringDefault("basepath",""),p->GetName());
        DEBUGclass("found path %s",s.Data());
        p->SetString( s );
      }
      //thesePaths now contains the modified paths (after makeFullPath)
      thesePaths->SetOwner(false); //we'll add these objects to another list which should take over ownership
      DEBUG("adding %d paths",thesePaths->GetEntries());
      paths->AddAll(thesePaths); //add all the (full) paths to the main list
      delete thesePaths; //delete the (local) helper list
    }
    DEBUGclass("trying to initialize sample '%s' from list of length %d",sample->getPath().Data(),paths->GetEntries());
    paths->SetOwner(true);
    if(!paths || (paths->GetEntries() == 0)){
      // there is no path at all
      // emit an error message
      message = "no valid file path given for '"+filename+"'";
      #ifdef _DEBUG_
      this->fPaths->print("rdt");
      #endif
      if(paths) delete paths;
      paths = nullptr;
      return visitWARN;
    } 
    if(paths->GetEntries() == 1){
      DEBUGclass("found exactly one entry for file '%s' in '%s'",filename.Data(),subdir.Data());
      // there is exactly one path
      // do the usual thing
      //fullpath = makeFullPath(this->fPaths->getTagStringDefault("basepath","."),paths->First()->GetName());
      fullpath = paths->First()->GetName();
      delete paths;
      paths = nullptr;
      if(!this->initializeSample(sample,fullpath,message)) { 
        if (getExitOnFail()) exit(66); 
        return visitFAILED;
      }
      this->setSampleNormalization(sample,1.);
      return visitOK;
    }
    if(paths->GetEntries() > 0){
      // we have more than one matching path
      // this is experimental sample-splitting 
      // use with caution and beware of errors
      DEBUGclass("found %d entries for file '%s' in '%s'",paths->GetEntries(),filename.Data(),subdir.Data());
      TQIterator itr(paths,true); //iterator takes ownership of 'paths' list here
      int nOK = 0;
      double neventstotal = 0;
      while(itr.hasNext()){
	//fullpath = makeFullPath(this->fPaths->getTagStringDefault("basepath","."),itr.readNext()->GetName());
	      fullpath = itr.readNext()->GetName();
        TString tmppath(fullpath);
        DEBUGclass("adding %s",fullpath.Data());
        TQSample* subSample = new TQSample(TQFolder::makeValidIdentifier(TQFolder::getPathTail(tmppath)));
        if(!sample->addSubSample(subSample)){
          if(sample->getObject(subSample->GetName())){
            message = TString::Format("unable to add subsample '%s', an object of this name exists already", subSample->GetName());
          } else {
            message = TString::Format("unable to add subsample '%s'", subSample->GetName());
          }
          DEBUGclass("error: %s",message.Data());
          delete subSample;
          return visitFAILED;
        } 
        TString subMessage;
        if(this->initializeSample(subSample,fullpath,subMessage)){
          DEBUGclass("initialized %s: %s",fullpath.Data(),subMessage.Data());
          nOK++;
          neventstotal += subSample->getTagDoubleDefault(".init.sumOfWeights",0);
          subSample->setTagString(TString::Format(".%s.message",this->GetName()), TQStringUtils::compactify(subMessage));
        } 
      }
      TQSampleIterator sitr(sample->getListOfFolders("?",TQSample::Class()),true);
      while(sitr.hasNext()){
        TQSample* s = sitr.readNext();
        this->setSampleNormalization(s,s->getTagDoubleDefault(".init.sumOfWeights",0)/neventstotal);
      }
      sample->setTagDouble(".init.sumOfWeightsTotal",neventstotal);
      if(nOK == paths->GetEntries()){
        message = TString::Format("multisample n=%d: all OK",nOK);
        return visitOK;
      } else if(nOK == 0){
        message = TString::Format("multisample n=%d: all FAILED",paths->GetEntries());
        if (getExitOnFail()) exit(66);
        return visitFAILED;
      } else {
        message = TString::Format("multisample n=%d: %d succeeded, %d failed",paths->GetEntries(),nOK,(paths->GetEntries()-nOK));
        if (getExitOnFail()) exit(66); 
        return visitFAILED;
      }
    }
  } else if(this->getTagString("dCacheFilePattern",fpattern)){
    // path is on dCache
    // highly experimental
    TList* l = TQUtils::lsdCache(sample->replaceInText(fpattern),TQLibrary::getLocalGroupDisk(), TQLibrary::getDQ2PathHead(), TQLibrary::getdCachePathHead(), TQLibrary::getDQ2cmd());
    if(l->GetEntries() == 0){
      message = "no such dataset";
      if (getExitOnFail()) exit(66); 
      return visitFAILED;
    } else if(l->GetEntries() == 1){
      TObjString* s = dynamic_cast<TObjString*>(l->First());
      fullpath = TQStringUtils::makeASCII(s->GetName());
      if(!this->initializeSample(sample,fullpath,message)) {
	if (getExitOnFail()) exit(66); 
	return visitFAILED;
      }
      return visitOK;
    } else {
      TQStringIterator itr(l,true);
      while(itr.hasNext()){
        TObjString* s = itr.readNext();
        fullpath = TQStringUtils::makeASCII(s->GetName());
        TQSample* subSample = sample->addSelfAsSubSample(fullpath);
        if(!this->initializeSample(subSample,fullpath,message)) {
	  if (getExitOnFail()) exit(66); 
	  return visitFAILED;
	}
      }
      return visitOK;
    }
  } else {
    message = "no known paths";
    if (getExitOnFail()) exit(66); 
    return visitFAILED;
  }
  
  return visitWARN;
}


bool TQSampleInitializer::getExitOnFail() {
  return this->getTagBoolDefault("exitOnFail",false);
}

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