// @(#)root/tmva $Id: Option.h 34301 2010-07-02 11:32:29Z stelzer $   
// Author: Andreas Hoecker, Joerg Stelzer, Helge Voss

/**********************************************************************************
 * Project: TMVA - a Root-integrated toolkit for multivariate data analysis       *
 * Package: TMVA                                                                  *
 * Class  : Option                                                                *
 * Web    : http://tmva.sourceforge.net                                           *
 *                                                                                *
 * Description:                                                                   *
 *      Option container                                                          *
 *                                                                                *
 * Authors (alphabetical):                                                        *
 *      Andreas Hoecker <Andreas.Hocker@cern.ch> - CERN, Switzerland              *
 *      Joerg Stelzer   <Joerg.Stelzer@cern.ch>  - CERN, Switzerland              *
 *      Helge Voss      <Helge.Voss@cern.ch>     - MPI-K Heidelberg, Germany      *
 *                                                                                *
 * Copyright (c) 2005:                                                            *
 *      CERN, Switzerland                                                         * 
 *      U. of Victoria, Canada                                                    * 
 *      MPI-K Heidelberg, Germany                                                 * 
 *      LAPP, Annecy, France                                                      *
 *                                                                                *
 * Redistribution and use in source and binary forms, with or without             *
 * modification, are permitted according to the terms listed in LICENSE           *
 * (http://mva.sourceforge.net/license.txt)                                       *
 **********************************************************************************/

#ifndef ROOT_TMVA_Option
#define ROOT_TMVA_Option

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// Option                                                               //
//                                                                      //
// Class for MVA-option handling                                        //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include <iomanip>
#include <sstream>
#include <vector>

#ifndef ROOT_TObject
#include "TObject.h"
#endif
#ifndef ROOT_TString
#include "TString.h"
#endif
#ifndef ROOT_TList
#include "TList.h"
#endif
#ifndef ROOT_TMVA_MsgLogger
#include "TMVA/MsgLogger.h"
#endif

namespace TMVA {

   class Configurable;

   class OptionBase : public TObject {

   public:

      friend class Configurable;

      OptionBase( const TString& name, const TString& desc );
      virtual ~OptionBase() {}
         
      virtual const char* GetName() const { return fNameAllLower.Data(); }
      virtual const char* TheName() const { return fName.Data(); }
      virtual TString     GetValue(Int_t i=-1) const = 0;

      Bool_t IsSet() const { return fIsSet; }
      virtual Bool_t IsArrayOpt() const = 0;
      const TString& Description() const { return fDescription; }
      virtual Bool_t IsPreDefinedVal(const TString&) const = 0;
      virtual Bool_t HasPreDefinedVal() const = 0;
      virtual Int_t  GetArraySize() const = 0;
      virtual Bool_t SetValue( const TString& vs, Int_t i=-1 );

      using TObject::Print;
      virtual void Print( ostream&, Int_t levelofdetail=0 ) const = 0;

   private:

      virtual void SetValueLocal(const TString& vs, Int_t i=-1) = 0;         

      const TString fName;         // name of variable 
      TString fNameAllLower;       // name of variable 
      const TString fDescription;  // its description
      Bool_t        fIsSet;        // set by user ?

   protected:

      static MsgLogger* fgLogger;  // message logger

   };
      
   // ---------------------------------------------------------------------------

   template <class T>

   class Option : public OptionBase {
     
   public:

      Option( T& ref, const TString& name, const TString& desc ) : 
         OptionBase(name, desc), fRefPtr(&ref) {}
      virtual ~Option() {}

      // getters
      virtual TString  GetValue( Int_t i=-1 ) const;
      virtual const T& Value   ( Int_t i=-1 ) const;
      virtual Bool_t HasPreDefinedVal() const { return (fPreDefs.size()!=0); }
      virtual Bool_t IsPreDefinedVal( const TString& ) const; 
      virtual Bool_t IsArrayOpt()   const { return kFALSE; }
      virtual Int_t  GetArraySize() const { return 0; }

      // setters
      virtual void AddPreDefVal(const T&);
      using OptionBase::Print;
      virtual void Print       ( ostream&, Int_t levelofdetail=0 ) const;
      virtual void PrintPreDefs( ostream&, Int_t levelofdetail=0 ) const;

   protected:

      T& Value(Int_t=-1);
  
      virtual void   SetValueLocal( const TString& val, Int_t i=-1 );
      virtual Bool_t IsPreDefinedValLocal( const T& ) const;

      T* fRefPtr;
      std::vector<T> fPreDefs;  // templated vector
   };      

   template<typename T>
   class Option<T*> : public Option<T> {

   public:

      Option( T*& ref, Int_t size, const TString& name, const TString& desc ) : 
         Option<T>(*ref,name, desc), fVRefPtr(&ref), fSize(size) {}
      virtual ~Option() {}

      TString GetValue( Int_t i ) const {
         std::stringstream str;
         str << std::scientific << Value(i);
         return str.str();
      }
      const T& Value( Int_t i ) const { return (*fVRefPtr)[i]; }
      virtual Bool_t IsArrayOpt()   const { return kTRUE; }
      virtual Int_t  GetArraySize() const { return fSize; }
   
      using Option<T>::Print;
      virtual void Print( ostream&, Int_t levelofdetail=0 ) const;

      virtual Bool_t SetValue( const TString& val, Int_t i=0 );

      T& Value(Int_t i) { return (*fVRefPtr)[i]; }
      T ** fVRefPtr;
      Int_t fSize;

   };

} // namespace

namespace TMVA {

   //______________________________________________________________________
   template<class T>
   inline const T& TMVA::Option<T>::Value( Int_t ) const {
      return *fRefPtr;
   }

   template<class T>
   inline T& TMVA::Option<T>::Value( Int_t ) {
      return *fRefPtr;
   }

   template<class T>
   inline TString TMVA::Option<T>::GetValue( Int_t ) const {
      std::stringstream str;      
      str << std::scientific << this->Value();
      return str.str();
   }

   template<>
   inline TString TMVA::Option<Bool_t>::GetValue( Int_t ) const {
      return Value() ? "True" : "False";
   }

   template<>
   inline TString TMVA::Option<Bool_t*>::GetValue( Int_t i ) const {
      return Value(i) ? "True" : "False";
   }

   template<class T>
   inline Bool_t TMVA::Option<T>::IsPreDefinedVal( const TString& val ) const 
   {
      // template 
      T tmpVal;
      std::stringstream str(val.Data());
      str >> tmpVal;
      return IsPreDefinedValLocal(tmpVal);
   }

   template<class T>
   inline Bool_t TMVA::Option<T>::IsPreDefinedValLocal(const T& val) const 
   {
      // template
      if (fPreDefs.size()==0) return kTRUE; // if nothing pre-defined then allow everything

      typename std::vector<T>::const_iterator predefIt;
      predefIt = fPreDefs.begin();
      for (;predefIt!=fPreDefs.end(); predefIt++) 
         if ( (*predefIt)==val ) return kTRUE;
      
      return kFALSE;
   }

   template<>
   inline Bool_t TMVA::Option<TString>::IsPreDefinedValLocal( const TString& val ) const 
   {
      // template specialization for Bool_t 
      TString tVal(val);
      tVal.ToLower();
      if (fPreDefs.size()==0) return kFALSE; // if nothing pre-defined then allow everything
      Bool_t foundPreDef = kFALSE;   
      std::vector<TString>::const_iterator predefIt;
      predefIt = fPreDefs.begin();
      for (;predefIt!=fPreDefs.end(); predefIt++) {
         TString s(*predefIt);
         s.ToLower();
         if (s==tVal) { foundPreDef = kTRUE; break; }
      }
      return foundPreDef;
   }

   //______________________________________________________________________
   template<class T>
   inline void TMVA::Option<T>::AddPreDefVal( const T& val ) 
   {
      // template 
      fPreDefs.push_back(val);
   }

   template<>
   inline void TMVA::Option<Bool_t>::AddPreDefVal( const Bool_t& ) 
   {
      // template specialization for Bool_t 
      *fgLogger << kFATAL << "<AddPreDefVal> predefined values for Option<Bool_t> don't make sense" 
                << Endl;
   }

   template<>
   inline void TMVA::Option<Float_t>::AddPreDefVal( const Float_t& ) 
   {
      // template specialization for Float_t 
      *fgLogger << kFATAL << "<AddPreDefVal> predefined values for Option<Float_t> don't make sense" 
                << Endl;
   }

   template<class T>
   inline void TMVA::Option<T>::Print( ostream& os, Int_t levelofdetail ) const 
   {
      // template specialization for TString printing
      os << TheName() << ": " << "\"" << GetValue() << "\"" << " [" << Description() << "]";
      this->PrintPreDefs(os,levelofdetail);
   }

   template<class T>
   inline void TMVA::Option<T*>::Print( ostream& os, Int_t levelofdetail ) const 
   {
      // template specialization for TString printing
      for (Int_t i=0; i<fSize; i++) {
         if (i==0)
            os << this->TheName() << "[" << i << "]: " << "\"" << this->GetValue(i) << "\"" << " [" << this->Description() << "]";
         else
            os << "    " << this->TheName() << "[" << i << "]: " << "\"" << this->GetValue(i) << "\"";
         if (i!=fSize-1) os << std::endl;
      }
      this->PrintPreDefs(os,levelofdetail);
   }

   //______________________________________________________________________
   template<class T>
   inline void TMVA::Option<T>::PrintPreDefs( ostream& os, Int_t levelofdetail ) const 
   {
      // template specialization for TString printing
      if (HasPreDefinedVal() && levelofdetail>0) {
         os << std::endl << "PreDefined - possible values are:" << std::endl;
         typename std::vector<T>::const_iterator predefIt;
         predefIt = fPreDefs.begin();
         for (;predefIt!=fPreDefs.end(); predefIt++) {
            os << "                       ";
            os << "  - " << (*predefIt) << std::endl;
         }
      }
   }
   
   //______________________________________________________________________
   template<class T>
   inline Bool_t TMVA::Option<T*>::SetValue( const TString& val, Int_t ind ) 
   {
      // template 
      if (ind >= fSize) return kFALSE;
      std::stringstream str(val.Data());
      if (ind < 0) {
         str >> Value(0);
         for (Int_t i=1; i<fSize; i++) Value(i) = Value(0);      
      } 
      else {
         str >> Value(ind);
      }
      return kTRUE;
   }

   template<class T>
   inline void TMVA::Option<T>::SetValueLocal( const TString& val, Int_t i ) 
   {
      // template 
      std::stringstream str(val.Data());
      str >> Value(i);
   }

   template<>
   inline void TMVA::Option<TString>::SetValueLocal( const TString& val, Int_t ) 
   {
      // set TString value
      TString valToSet(val);
      if (fPreDefs.size()!=0) {
         TString tVal(val);
         tVal.ToLower();
         std::vector<TString>::const_iterator predefIt;
         predefIt = fPreDefs.begin();
         for (;predefIt!=fPreDefs.end(); predefIt++) {
            TString s(*predefIt);
            s.ToLower();
            if (s==tVal) { valToSet = *predefIt; break; }
         }
      }

      std::stringstream str(valToSet.Data());
      str >> Value(-1);
   }

   template<>
   inline void TMVA::Option<Bool_t>::SetValueLocal( const TString& val, Int_t ) 
   {
      // set Bool_t value
      TString valToSet(val);
      valToSet.ToLower();
      if (valToSet=="1" || valToSet=="true" || valToSet=="ktrue" || valToSet=="t") {
         this->Value() = true;
      }
      else if (valToSet=="0" || valToSet=="false" || valToSet=="kfalse" || valToSet=="f") {
         this->Value() = false;
      }
      else {
         *fgLogger << kFATAL << "<SetValueLocal> value \'" << val 
                   << "\' can not be interpreted as boolean" << Endl;
      }
   }
}
#endif
