#ifndef ROOT_TMVA_Factory
#define ROOT_TMVA_Factory
#include <string>
#include <vector>
#include <map>
#ifndef ROOT_TCut
#include "TCut.h"
#endif
#ifndef ROOT_TMVA_Configurable
#include "TMVA/Configurable.h"
#endif
#ifndef ROOT_TMVA_Types
#include "TMVA/Types.h"
#endif
#ifndef ROOT_TMVA_DataSet
#include "TMVA/DataSet.h"
#endif
class TFile;
class TTree;
class TDirectory;
namespace TMVA {
   class IMethod;
   class MethodBase;
   class DataInputHandler;
   class DataSetInfo;
   class DataSetManager;
   class VariableTransformBase;
   class Factory : public Configurable {
   public:
      typedef std::vector<IMethod*> MVector;
      
      Factory( TString theJobName, TFile* theTargetFile, TString theOption = "" );
      
      virtual ~Factory();
      virtual const char*  GetName() const { return "Factory"; }
      
      void AddSignalTrainingEvent    ( const std::vector<Double_t>& event, Double_t weight = 1.0 );
      void AddBackgroundTrainingEvent( const std::vector<Double_t>& event, Double_t weight = 1.0 );
      void AddSignalTestEvent        ( const std::vector<Double_t>& event, Double_t weight = 1.0 );
      void AddBackgroundTestEvent    ( const std::vector<Double_t>& event, Double_t weight = 1.0 );
      void AddTrainingEvent( const TString& className, const std::vector<Double_t>& event, Double_t weight );
      void AddTestEvent    ( const TString& className, const std::vector<Double_t>& event, Double_t weight );
      void AddEvent        ( const TString& className, Types::ETreeType tt, const std::vector<Double_t>& event, Double_t weight );
      Bool_t UserAssignEvents(UInt_t clIndex);
      TTree* CreateEventAssignTrees( const TString& name );
      DataSetInfo& AddDataSet( DataSetInfo& );
      DataSetInfo& AddDataSet( const TString&  );
      
      
      void SetInputTrees( const TString& signalFileName, const TString& backgroundFileName, 
                          Double_t signalWeight=1.0, Double_t backgroundWeight=1.0 );
      void SetInputTrees( TTree* inputTree, const TCut& SigCut, const TCut& BgCut );
      
      void SetInputTrees( TTree* signal, TTree* background, 
                          Double_t signalWeight=1.0, Double_t backgroundWeight=1.0) ;
      void AddSignalTree( TTree* signal,    Double_t weight=1.0, Types::ETreeType treetype = Types::kMaxTreeType );
      void AddSignalTree( TString datFileS, Double_t weight=1.0, Types::ETreeType treetype = Types::kMaxTreeType );
      void AddSignalTree( TTree* signal, Double_t weight, const TString& treetype );      
      
      void SetSignalTree( TTree* signal, Double_t weight=1.0);
      void AddBackgroundTree( TTree* background, Double_t weight=1.0, Types::ETreeType treetype = Types::kMaxTreeType );
      void AddBackgroundTree( TString datFileB,  Double_t weight=1.0, Types::ETreeType treetype = Types::kMaxTreeType );
      void AddBackgroundTree( TTree* background, Double_t weight, const TString & treetype );
      
      void SetBackgroundTree( TTree* background, Double_t weight=1.0 );
      void SetSignalWeightExpression( const TString& variable );
      void SetBackgroundWeightExpression( const TString& variable );
      
      void AddRegressionTree( TTree* tree, Double_t weight = 1.0,  
                              Types::ETreeType treetype = Types::kMaxTreeType ) { 
         AddTree( tree, "Regression", weight, "", treetype ); 
      }
      
      
      void SetTree( TTree* tree, const TString& className, Double_t weight ); 
      void AddTree( TTree* tree, const TString& className, Double_t weight=1.0,
                    const TCut& cut = "",
                    Types::ETreeType tt = Types::kMaxTreeType );
      void AddTree( TTree* tree, const TString& className, Double_t weight, const TCut& cut, const TString& treeType );
      
      void SetInputVariables  ( std::vector<TString>* theVariables ); 
      void AddVariable        ( const TString& expression, const TString& title, const TString& unit,
                                char type='F', Double_t min = 0, Double_t max = 0 );
      void AddVariable        ( const TString& expression, char type='F',
                                Double_t min = 0, Double_t max = 0 );
      void AddTarget          ( const TString& expression, const TString& title = "", const TString& unit = "",
                                Double_t min = 0, Double_t max = 0 );
      void AddRegressionTarget( const TString& expression, const TString& title = "", const TString& unit = "",
                                Double_t min = 0, Double_t max = 0 )
      {
         AddTarget( expression, title, unit, min, max );
      }
      void AddSpectator         ( const TString& expression, const TString& title = "", const TString& unit = "",
                                Double_t min = 0, Double_t max = 0 );
      
      void SetWeightExpression( const TString& variable, const TString& className = "" );
      
      void SetCut( const TString& cut, const TString& className = "" );
      void SetCut( const TCut& cut, const TString& className = "" );
      void AddCut( const TString& cut, const TString& className = "" );
      void AddCut( const TCut& cut, const TString& className = "" );
      
      void PrepareTrainingAndTestTree( const TCut& cut, const TString& splitOpt );
      void PrepareTrainingAndTestTree( TCut sigcut, TCut bkgcut, const TString& splitOpt );
      
      void PrepareTrainingAndTestTree( const TCut& cut, Int_t Ntrain, Int_t Ntest = -1 );
      void PrepareTrainingAndTestTree( const TCut& cut, Int_t NsigTrain, Int_t NbkgTrain, Int_t NsigTest, Int_t NbkgTest, 
                                       const TString& otherOpt="SplitMode=Random:!V" );
      MethodBase* BookMethod( TString theMethodName, TString methodTitle, TString theOption = "" );
      MethodBase* BookMethod( Types::EMVA theMethod,  TString methodTitle, TString theOption = "" );
      MethodBase* BookMethod( TMVA::Types::EMVA , 
                              TString , 
                              TString , 
                              TMVA::Types::EMVA , 
                              TString  ) { return 0; } 
      
      void OptimizeAllMethods                 (TString fomType="ROCIntegral", TString fitType="FitGA");
      void OptimizeAllMethodsForClassification(TString fomType="ROCIntegral", TString fitType="FitGA") { OptimizeAllMethods(fomType,fitType); }
      void OptimizeAllMethodsForRegression    (TString fomType="ROCIntegral", TString fitType="FitGA") { OptimizeAllMethods(fomType,fitType); }
      
      void TrainAllMethods                 ();
      void TrainAllMethodsForClassification( void ) { TrainAllMethods(); }
      void TrainAllMethodsForRegression    ( void ) { TrainAllMethods(); }
      
      void TestAllMethods();
      
      void EvaluateAllMethods( void );
      void EvaluateAllVariables( TString options = "" ); 
  
      
      void DeleteAllMethods( void );
      
      IMethod* GetMethod( const TString& title ) const;
      Bool_t Verbose( void ) const { return fVerbose; }
      void SetVerbose( Bool_t v=kTRUE );
      
      
      
      
      virtual void MakeClass( const TString& methodTitle = "" ) const;
      
      
      
      
      void PrintHelpMessage( const TString& methodTitle = "" ) const;
      static TDirectory* RootBaseDir() { return (TDirectory*)fgTargetFile; }
   private:
      
      void Greetings();
      void WriteDataInformation();
      DataInputHandler&        DataInput() { return *fDataInputHandler; }
      DataSetInfo&             DefaultDataSetInfo();
      void                     SetInputTreesFromEventAssignTrees();
   private:
      
      DataSetManager* fDataSetManager; 
      static TFile*                             fgTargetFile;     
      DataInputHandler*                         fDataInputHandler;
      std::vector<TMVA::VariableTransformBase*> fDefaultTrfs;     
      
      TString                                   fOptions;         
      TString                                   fTransformations; 
      Bool_t                                    fVerbose;         
      MVector                                   fMethods;         
      TString                                   fJobName;         
      
      enum DataAssignType { kUndefined = 0, 
                            kAssignTrees,
                            kAssignEvents };
      DataAssignType                            fDataAssignType;  
      std::vector<TTree*>                       fTrainAssignTree; 
      std::vector<TTree*>                       fTestAssignTree;  
      Int_t                                     fATreeType;          
      Float_t                                   fATreeWeight;        
      Float_t*                                  fATreeEvent;         
      Types::EAnalysisType                      fAnalysisType;    
   protected:
      ClassDef(Factory,0)  
   };
} 
#endif