//
// Copyright (C) 2003-2008 Greg Landrum and Rational Discovery LLC
//
//  @@ All Rights Reserved @@
//  This file is part of the RDKit.
//  The contents are covered by the terms of the BSD license
//  which is included in the file license.txt, found at the root
//  of the RDKit source tree.
//
/*! \file Dict.h

  \brief Defines the Dict class

*/  
#ifndef __RD_DICT_H__
#define __RD_DICT_H__

#include <map>
#include <string>
#include <vector>
#include <boost/any.hpp>
#include <RDGeneral/Exceptions.h>
#include <boost/lexical_cast.hpp>

namespace RDKit{
  typedef std::vector<std::string> STR_VECT;

  //! \brief The \c Dict class can be used to store objects of arbitrary
  //!        type keyed by \c strings.
  //!
  //!  The actual storage is done using \c boost::any objects.
  //!
  class Dict {
  public:
    typedef std::map<std::string, boost::any> DataType;
    Dict(){
      _data.clear();
    };

    Dict(const Dict &other) : _data(other._data) {};

    Dict &operator=(const Dict &other) {
      _data = other._data;
      return *this;
    };

    //----------------------------------------------------------
    //! \brief Returns whether or not the dictionary contains a particular
    //!        key.
    bool hasVal(const char *what) const{
      std::string key(what);
      return hasVal(key);
    };
    bool hasVal(const std::string &what) const {
      return _data.find(what)!=_data.end();
    };

    //----------------------------------------------------------
    //! Returns the set of keys in the dictionary
    /*!
       \return  a \c STR_VECT
    */
    STR_VECT keys() const {
      STR_VECT res;
      DataType::const_iterator item;
      for (item = _data.begin(); item != _data.end(); item++) {
        res.push_back(item->first);
      }
      return res;
    }

    //----------------------------------------------------------
    //! \brief Gets the value associated with a particular key
    /*!
       \param what  the key to lookup
       \param res   a reference used to return the result
    
       <B>Notes:</b>
        - If \c res is a \c std::string, every effort will be made
          to convert the specified element to a string using the
          \c boost::lexical_cast machinery.
        - If the dictionary does not contain the key \c what,
          a KeyErrorException will be thrown.
    */
    template <typename T>
    void getVal(const std::string &what,T &res) const {
      res = getVal<T>(what);
    };
    //! \overload
    template <typename T>
    T getVal(const std::string &what) const {
      DataType::const_iterator pos=_data.find(what);
      if(pos==_data.end())
	throw KeyErrorException(what);
      const boost::any &val = pos->second;
      return fromany<T>(val);
    }

    //! \overload
    template <typename T>
    T getVal(const char *what,T &res) const {  // FIX: it doesn't really fit that this returns T
      std::string key(what);
      res = getVal<T>(key);
      return res; 
    };
    //! \overload
    template <typename T>
    T getVal(const char *what) const {
      std::string key(what);
      return getVal<T>(key);
    };

    //! \overload
    void getVal(const std::string &what, std::string &res) const;
    //! \overload
    void getVal(const char *what, std::string &res) const { getVal(std::string(what),res); };

    //----------------------------------------------------------
    //! \brief Potentially gets the value associated with a particular key
    //!        returns true on success/false on failure.
    /*!
       \param what  the key to lookup
       \param res   a reference used to return the result
    
       <B>Notes:</b>
        - If \c res is a \c std::string, every effort will be made
          to convert the specified element to a string using the
          \c boost::lexical_cast machinery.
        - If the dictionary does not contain the key \c what,
          a KeyErrorException will be thrown.
    */

    template <typename T>
    bool getValIfPresent(const std::string &what,T &res) const {
      DataType::const_iterator pos=_data.find(what);
      if(pos==_data.end())
	return false;
      const boost::any &val = pos->second;
      res = fromany<T>(val);
      return true;
    };

    template <typename T>
    bool getValIfPresent(const char *what,T &res) const {
      std::string key(what);
      return getValIfPresent<T>(key, res);
    };

    //! \overload
    bool getValIfPresent(const std::string &what, std::string &res) const;
    //! \overload
    bool getValIfPresent(const char *what, std::string &res) const {
      return getValIfPresent(std::string(what),res);
    };
    
    //----------------------------------------------------------
    //! \brief Sets the value associated with a key
    /*!
      
       \param what the key to set
       \param val  the value to store
    
       <b>Notes:</b>
          - If \c val is a <tt>const char *</tt>, it will be converted
             to a \c std::string for storage.
          - If the dictionary already contains the key \c what,
            the value will be replaced.
    */
    template <typename T>
    void setVal(const std::string &what, T &val){
      _data[what] = toany(val);
    };
    //! \overload
    template <typename T>
    void setVal(const char *what, T &val){
      std::string key = what;
      setVal(key,val);
    };
    //! \overload
    void setVal(const std::string &what, const char *val){
      std::string h(val);
      setVal(what,h);
    }



    //----------------------------------------------------------
    //! \brief Clears the value associated with a particular key,
    //!     removing the key from the dictionary.
    /*!
     
       \param what the key to clear
    
     <b>Notes:</b>
        - If the dictionary does not contain the key \c what,
          a KeyErrorException will be thrown.
    */
    void clearVal(const std::string &what) {
      if(! this->hasVal(what) ) throw KeyErrorException(what);
      _data.erase(what);
    };

    //! \overload
    void clearVal(const char *what) {
      std::string key=what;
      clearVal(key);
    };

    //----------------------------------------------------------
    //! \brief Clears all keys (and values) from the dictionary.
    //!
    void reset(){
      _data.clear();
    };

    //----------------------------------------------------------
    //! Converts a \c boost::any to type \c T
    /*!
       \param arg a \c boost::any reference

       \returns the converted object of type \c T
    */
    template <typename T>
      T fromany(const boost::any &arg) const;
    

    //----------------------------------------------------------
    //! Converts an instance of type \c T to \c boost::any
    /*!
       \param arg the object to be converted

       \returns a \c boost::any instance
    */
    template <typename T>
      boost::any toany(T arg) const;

  private:
    DataType _data; //!< the actual dictionary
  };
}
#endif
