/*****************************************************************************
 * Project: RooFit                                                           *
 * Package: RooFitCore                                                       *
 * @(#)root/roofitcore:$Id: RooNumGenFactory.cxx 28259 2009-04-16 16:21:16Z wouter $
 * Authors:                                                                  *
 *   WV, Wouter Verkerke, UC Santa Barbara, verkerke@slac.stanford.edu       *
 *   DK, David Kirkby,    UC Irvine,         dkirkby@uci.edu                 *
 *                                                                           *
 * Copyright (c) 2000-2005, Regents of the University of California          *
 *                          and Stanford University. All rights reserved.    *
 *                                                                           *
 * Redistribution and use in source and binary forms,                        *
 * with or without modification, are permitted according to the terms        *
 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)             *
 *****************************************************************************/

//////////////////////////////////////////////////////////////////////////////
//
// BEGIN_HTML
// RooNumGenFactory is a factory to instantiate numeric integrators
// from a given function binding and a given configuration. The factory
// searches for a numeric integrator registered with the factory that
// has the ability to perform the numeric integration. The choice of
// method may depend on the number of dimensions integrated,
// the nature of the integration limits (closed or open ended) and
// the preference of the caller as encoded in the configuration object.
// END_HTML
//

#include "TClass.h"
#include "Riostream.h"

#include "RooFit.h"

#include "RooNumGenFactory.h"
#include "RooArgSet.h"
#include "RooAbsFunc.h"
#include "RooNumGenConfig.h"
#include "RooNumber.h"

#include "RooAcceptReject.h"
#include "RooFoamGenerator.h"
#include "RooSentinel.h"


#include "RooMsgService.h"

using namespace std ;

ClassImp(RooNumGenFactory)
;

RooNumGenFactory* RooNumGenFactory::_instance = 0 ;



//_____________________________________________________________________________
RooNumGenFactory::RooNumGenFactory()
{
  // Constructor. Register all known integrators by calling
  // their static registration functions

  _instance = this ;

  RooAcceptReject::registerSampler(*this) ;
  RooFoamGenerator::registerSampler(*this) ;

  // Prepare default
  RooNumGenConfig::defaultConfig().method1D(kFALSE,kFALSE).setLabel("RooFoamGenerator") ;
  RooNumGenConfig::defaultConfig().method1D(kTRUE ,kFALSE).setLabel("RooAcceptReject") ;
  RooNumGenConfig::defaultConfig().method1D(kFALSE,kTRUE ).setLabel("RooAcceptReject") ;
  RooNumGenConfig::defaultConfig().method1D(kTRUE, kTRUE ).setLabel("RooAcceptReject") ;
  
  RooNumGenConfig::defaultConfig().method2D(kFALSE,kFALSE).setLabel("RooFoamGenerator") ;
  RooNumGenConfig::defaultConfig().method2D(kTRUE ,kFALSE).setLabel("RooAcceptReject") ;
  RooNumGenConfig::defaultConfig().method2D(kFALSE,kTRUE ).setLabel("RooAcceptReject") ;
  RooNumGenConfig::defaultConfig().method2D(kTRUE, kTRUE ).setLabel("RooAcceptReject") ;
  
  RooNumGenConfig::defaultConfig().methodND(kFALSE,kFALSE).setLabel("RooFoamGenerator") ;
  RooNumGenConfig::defaultConfig().methodND(kTRUE ,kFALSE).setLabel("RooAcceptReject") ;
  RooNumGenConfig::defaultConfig().methodND(kFALSE,kTRUE ).setLabel("RooAcceptReject") ;
  RooNumGenConfig::defaultConfig().methodND(kTRUE, kTRUE ).setLabel("RooAcceptReject") ;
  
}



//_____________________________________________________________________________
RooNumGenFactory::~RooNumGenFactory()
{
  // Destructor

  std::map<std::string,RooAbsNumGenerator*>::iterator iter = _map.begin() ;
  while (iter != _map.end()) {
    delete iter->second ;
    ++iter ;
  }  
}


//_____________________________________________________________________________
RooNumGenFactory::RooNumGenFactory(const RooNumGenFactory& other) : TObject(other)
{
  // Copy constructor
}



//_____________________________________________________________________________
RooNumGenFactory& RooNumGenFactory::instance()
{
  // Static method returning reference to singleton instance of factory

  if (_instance==0) {
    new RooNumGenFactory ;
    RooSentinel::activate() ;
  } 
  return *_instance ;
}


//_____________________________________________________________________________
void RooNumGenFactory::cleanup()
{
  // Cleanup routine called by atexit() handler installed by RooSentinel

  if (_instance) {
    delete _instance ;
    _instance = 0 ;
  }
}



//_____________________________________________________________________________
Bool_t RooNumGenFactory::storeProtoSampler(RooAbsNumGenerator* proto, const RooArgSet& defConfig) 
{
  // Method accepting registration of a prototype numeric integrator along with a RooArgSet of its
  // default configuration options and an optional list of names of other numeric integrators
  // on which this integrator depends. Returns true if integrator was previously registered

  TString name = proto->IsA()->GetName() ;

  if (getProtoSampler(name)) {
    //cout << "RooNumGenFactory::storeSampler() ERROR: integrator '" << name << "' already registered" << endl ;
    return kTRUE ;
  }

  // Add to factory 
  _map[name.Data()] = proto ;

  // Add default config to master config
  RooNumGenConfig::defaultConfig().addConfigSection(proto,defConfig) ;
  
  return kFALSE ;
}



//_____________________________________________________________________________
const RooAbsNumGenerator* RooNumGenFactory::getProtoSampler(const char* name) 
{
  // Return prototype integrator with given (class) name

  if (_map.count(name)==0) {
    return 0 ;
  } 
  
  return _map[name] ;
}



//_____________________________________________________________________________
RooAbsNumGenerator* RooNumGenFactory::createSampler(RooAbsReal& func, const RooArgSet& genVars, const RooArgSet& condVars, const RooNumGenConfig& config, Bool_t verbose, RooAbsReal* maxFuncVal) 
{
  // Construct a numeric integrator instance that operates on function 'func' and is configured
  // with 'config'. If ndimPreset is greater than zero that number is taken as the dimensionality
  // of the integration, otherwise it is queried from 'func'. This function iterators over list
  // of available prototype integrators and returns an clone attached to the given function of
  // the first class that matches the specifications of the requested integration considering
  // the number of dimensions, the nature of the limits (open ended vs closed) and the user
  // preference stated in 'config'

  // Find method defined configuration
  Int_t ndim = genVars.getSize() ;
  Bool_t cond = (condVars.getSize() > 0) ? kTRUE : kFALSE ;

  Bool_t hasCat(kFALSE) ;
  TIterator* iter = genVars.createIterator() ;
  RooAbsArg* arg ;
  while ((arg=(RooAbsArg*)iter->Next())) {
    if (arg->IsA()==RooCategory::Class()) {
      hasCat=kTRUE ;
      break ;
    }
  }
  delete iter ;


  TString method ;
  switch(ndim) {
  case 1:
    method = config.method1D(cond,hasCat).getLabel() ;
    break ;

  case 2:
    method = config.method2D(cond,hasCat).getLabel() ;
    break ;

  default:
    method = config.methodND(cond,hasCat).getLabel() ;
    break ;
  }

  // Check that a method was defined for this case
  if (!method.CompareTo("N/A")) {
    oocoutE((TObject*)0,Integration) << "RooNumGenFactory::createSampler: No sampler method has been defined for " 
				     << (cond?"a conditional ":"a ") << ndim << "-dimensional p.d.f" << endl ;
    return 0 ;    
  }

  // Retrieve proto integrator and return clone configured for the requested integration task
  const RooAbsNumGenerator* proto = getProtoSampler(method) ;  
  RooAbsNumGenerator* engine =  proto->clone(func,genVars,condVars,config,verbose,maxFuncVal) ;
  return engine ;
}
