#ifndef __IDMS_CONFIGURATION_H
#define __IDMS_CONFIGURATION_H

#include <map>
#include <bitset>
#include <iomanip>
#include <string>
#include <ostream>
#include <sstream>
#include <set>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/ref.hpp>


/**
 * A ConfigNode is the key data element in a configuration tree. Think of it as
 * a directory (or if you want, as the windows registry) where every directory
 * is a ConfigNode and consists of either several key/value std::pairs (the files)
 * or other ConfigNodes (the further subdirectories). Additionally to their
 * contents, they all can have comments which are saved in the trees.
 * @note This class is for internal use only and its symbols are not exported.
 */
// TODO move this into the Configuration as a private type.
struct ConfigNodeBase
{
    std::string comment;

    /**
     * We don't need this for querying the configuration, but for iterating
     * over it in case of loading, saving and printing.
     */
    virtual bool is_subnode( void ) = 0;

    virtual void clear() = 0;

    virtual ConfigNodeBase* clone() const = 0;

    virtual void printon( std::ostream& o, int level, std::bitset<32>& bs ) const = 0;

    virtual void dot( std::ostream& ) const = 0;

    virtual ~ConfigNodeBase( void ) { }

    ConfigNodeBase( const std::string& c ) : comment(c) { }
};

class ConfigFunctor;
class ConfigEvent;

struct ConfigValue : public ConfigNodeBase
{
    std::string value;
    /**
     * Since we expect this to be mostly empty, we only allocate one when
     * needed. This is some kind of a trade-off, but we cannot really afford
     * to have the up to 72 Bytes overhead (usually 36 on 32Bit gcc, but
     * still).
     */

    std::set<ConfigFunctor*>* functors;
    bool deprecated;

    virtual bool is_subnode( void ) { return false; }
    virtual void clear()  { }

    virtual ConfigValue* clone() const
    {
        ConfigValue* cv = new ConfigValue( value, comment);
        cv->deprecated = deprecated;
        return cv;
    }

    virtual void dot( std::ostream& ) const;

    virtual void printon( std::ostream& o, int level, std::bitset<32>& bs ) const;

    ConfigValue( const std::string& v, const std::string& c  ) : ConfigNodeBase(c), value(v), functors(0), deprecated(false) 
    { 
    }
};

struct ConfigNode : public ConfigNodeBase
{
    /**
     * FIXME
     * std::maps the value names to their values (std::pair.first) and the comment of
     * that entry (std::pair.second).
     * Comment is anything that is above the value, and on the same line as the
     * value assignment.
     * Newlines should be preserved where possible, but an output module is
     * allowed to move the comment which is on the same line to another valid
     * position
     * @note <b> Important !</b> These are the restrictions about configuration
     * paths and values :
     * - Configuration path delimiter is always /
     * - This delimiter may never occur in the key
     * - Allowed characters in path and key are only those which can be used for
     *   the filesystem too.    
     * - For maximum compatibility you should stick to ASCII
     * - Although there is no real limit for the configuration value and path
     *   size, implementations of the loaders are required to load 256 Bytes for
     *   the value only.
     * - Values may be converted to other character std::sets if necessary, but it is
     *   recommended to stick to utf8
     */

    /**
     * FIXME: comment wrong
     * To be consistent with the normal use of filesystems, we do not allow an
     * directory to be of the same name as a "file" in it.
     * Also directories can have comments, although no values.
     */

    /**
     * Our virtual configuration entries. Can either be some subnode, or some value entry.
     */
    std::map<std::string, ConfigNodeBase* > entries;

    virtual void dot( std::ostream& ) const;
    /**
     * We don't need to do anything else.
     */
    inline ConfigNode( const std::string& comment) : ConfigNodeBase(comment) { }

    virtual ~ConfigNode();

    /**
     * This properly clears out this ConfigNode and deletes all subnodes.
     */
    virtual void clear();
    virtual bool is_subnode( void ) { return true; }

    virtual ConfigNode* clone() const;

    /**
     * This prints on a give std::ostream. Mainly for Configuration operator<< use.
     */
    virtual void printon( std::ostream&, int level, std::bitset<32>& ) const;

};

/**
 * A path leads to a config directory which contains several name/value std::pairs.
 * Paths are delimited by / and may never contain characters like
 * @todo Create an iterator with usable iteration semantics.
 */
class Configuration
{
    friend std::ostream& operator<<( std::ostream& o, const Configuration& c );
    friend class ConfigSaver;
    private:
    /**
     * @todo TODO FIXME if we want to save the whole config to one single file,
     * this wont be allowed, since we ask for a subconfig. we have to redo that
     * config part too.
     */
    ConfigNode root;

    std::string global_basepath;

    std::string basepath;

    //    const ConfigNode* find_path( const std::string& path ) const;

    const std::map<std::string,ConfigNodeBase*>& get_entries( void ) const { return root.entries; }

    /*
       ConfigIterator begin();
       ConfigIterator begin() const;
       ConfigIterator end();
       ConfigIterator end() const;
       */

    // The bool tells us whether the value could be found. If its not found, what
    // the std::string contains is unspecified.
    /**
     * This shall return a std::string reference to the stored value, as well as a
     * bool telling us if the value was found.
     */
    enum nodetype
    {
        none,	 // nothing valid was returned. Means nothing was found, or something was invalid.
        value,   // The pointer contains a value
        node,    // The pointer contains a node
        inserted // The node given was inserted.
    };
    //    typedef std::pair<boost::reference_wrapper<const ConfigValue>,bool> const_getpair;

    union u_node 
    { 
        ConfigValue* value; 
        ConfigNode* node; 
        u_node( ConfigValue* v ) : value(v) { }
        u_node( ConfigNode* n  ) : node(n)  { }
        u_node( ) : node(0) { }
    };

    typedef std::pair< const u_node , nodetype > const_getpair;

    const_getpair internal_get( const std::string& p ) const;

    // This definitely inserts some path.
    // If the value specified is a path already, this will throw.
    // The bool specifies, whether the entry was created.
    // The std::string is in any case a reference to the std::string value at the specified path.
    // (Note: in either case the comment will be updated when specified)

    /**
     * @param lastpath is to be set to true, if the last element in the path
     * list shall be a path, not a value.
     */
    //    typedef std::pair<boost::reference_wrapper<ConfigValue>,bool> getpair;
    typedef std::pair< u_node , nodetype > getpair;
    getpair internal_insert( const std::string& path, const std::string& comment, bool lastpath );

    template<class T>
        std::pair<T,bool> convert_to( const std::string& ) const;

    template<class T>
        std::pair<std::string,bool> convert_to( const T& ) const;

    public:

    void dot( std::ostream& o ) { root.dot(o); }
    //    Configuration get_subconfig( const std::string& path) const;
    /**
     * @param bp is the base path at which the loading will be appended. Like
     * bp = "/home/$USER/.iwear" and then load from "output/..." so an
     * application can std::set its own path. 
     * @param gbp is the global base path to be loaded from like "/usr/share"
     * etc.
     */
    Configuration( const std::string& gbp, const std::string& bp) ;

    ~Configuration();

    const std::string& get_global_basepath( void ) { return global_basepath; }

    const std::string& get_basepath( void ) { return basepath; }

    /**
     * Gets a configuration value, and returns it as a T using a std::stringstream
     * based conversion sequence.
     * @throw iwruntime_error when the configuration value was not found.
     * @throw iwruntime_error When the conversion to the requested type failed.
     * @throw iwruntime_error When the specified path was a subpath
     */
    template<class T> T get( const std::string& path ) const;

    /**
     * Like the const version, this returns a configuration value as T.
     * But unlike the const version, if the value is not found, it will instead
     * be inserted. Then the specified default value will be returned.
     * If the value was found, but the conversion didn't work, then the default
     * value will be inserted instead of the value.
     * @note If the passed default value is not convertible to a std::string, the
     * configuration will not be altered, and this value will be returned
     * instead.
     * @throw iwruntime_error When the specified path was a subpath
     * @warning due to a bug in the current version, this overwrites a comment
     * also when it was already there. @todo
     */
    template<class T> T get( const std::string& path, const T&, const std::string& comment = "" );

    /**
     * Gets a configuration value. Returns the default value passed, when the
     * path wasn't found. Also returns the default value, when the type
     * conversion failed.  
     * @throw iwruntime_error When the specified path was a subpath
     * @todo Check if we can do some RVO here.
     */
    template<class T> T get( const std::string& path, const T& ) const;

    // TODO add a decent operator[] implementation. (try to distinguish between
    // rhs and lhs by some proxy objects)

    /**
     * This will register an event, which will be emitted, when the
     * corresponding entry is changed. It will then change the value of the
     * element given as second parameter to what was std::set into the
     * configuration. Failings to do so will silently be ignored.
     * @throw iwruntime_error If the path was not found in the configuration hierarchy
     * @throw iwlogic_error when the path was marked as deprecated
     * @warning You have to make sure yourself that whatever T& refers to never
     * goes out of scope, until the event is unregistered.
     */
    template<class T>
        void register_event( const std::string&, T& );

    /**
     * Use this to deregister an event via the event pointer you got. This is
     * the recommended version to do it instead of having to pass the
     * parameters etc. again. (Of course, if its easier for you to do it that
     * way, feel free to do it, it just is most likely slower and we cannot
     * guarantee anything for now *g*)
     */
    void deregister_event( ConfigEvent* );

    /**
     * Here we call the passed boost::function object. _1 and _2 are the name
     * of the configuration option, and the value resp. 
     * @throw iwruntime_error If the path was not found in the configuration hierarchy
     * @throw iwlogic_error when the path was marked as deprecated
     */
    void register_event( const std::string&, const boost::function<void(const std::string&,const std::string&)>& );

    /**
     * This inserts a subnode path and adds some comment for it. No values will
     * be inserted. 
     * @throw iwruntime_error If the subnode already existed as a value.
     */
    void insert( const std::string& path, const std::string& comment, int);

    /**
     * Inserts a config value, if it existed before, it will be overwritten.
     * The comment will only be updated, when it is not empty.
     */
    template<class T> 
        void insert( const std::string& path, const T& val, const std::string& comment = "");

    /**
     * This marks a path as deprecated. If the path exist, it will indeed be
     * marked. If it didn't exist, a path will be inserted, with empty values
     * and marked as deprecated.
     * Whenever a program requests a deprecated configuration option, a message
     * of warning level will be emitted to the console. This shall aid as a
     * hint for programmers.
     * @note No configuration saving class may ever store deprecated values to
     * its storage.
     * @warning Never shall a deprecated value be registered for change
     * notification. 
     */
    void deprecate( const std::string& path );

    /**
     * This completely removes the specified path from the configuration. All
     * subpaths, if applicable, are removed too.
     * This shall be used one or two releases, after some configuration option
     * has been deprecated.
     * If the path didn't exist, no action is performed.
     */
    void remove( const std::string& path );
};

template<> void Configuration::insert( const std::string&, const std::string&, const std::string& );

template<class T> void Configuration::insert( const std::string& s, const T& t, const std::string& c)
{
    std::stringstream ss;
    ss << std::boolalpha << t;
    insert(s,ss.str(),c);
}

// generic case of conversion to something
template<class T>
std::pair<T,bool> Configuration::convert_to( const std::string& val ) const
{
    std::stringstream ss(val);
    T t;
    bool ret = (ss>>t);
    return std::make_pair(t,ret);
}


// specialized to return some std::string
template<class T>
std::pair<std::string,bool> Configuration::convert_to( const T& val ) const
{
    std::stringstream ss;
    bool ret = (ss<<val);
    return std::make_pair(ss.str(),ret);
}

// specialized conversion for timeval
/*
   template<>
   inline std::pair<timeval,bool> Configuration::convert_to( const std::string& val ) const
   {
   return std::make_pair(time_from_string(val.c_str()),true);
   }*/

// specialized so that the stringstream does not eat up the rest of a string
template<>
inline std::pair<std::string,bool> Configuration::convert_to( const std::string& val ) const
{
    return std::make_pair(val,true);
}


// specialized to convert to some bool
template<>
inline std::pair<bool,bool> Configuration::convert_to( const std::string& val ) const
{
    if( val == "true" || val == "yes" || val == "wahr" ) return std::make_pair(true,true);
    if( val == "false" || val == "no" || val == "falsch" ) return std::make_pair(false,true);

    return std::make_pair(false,false);
}

template<class T>
T Configuration::get( const std::string& path ) const
{
    const_getpair v = internal_get(path);
    if( v.second == value )
    {
        //	d_dbg << "Trying to convert " << v.first.value->value << " to type <" << typenameof(T()) << ">" << std::endl;
        std::pair<T,bool> r = convert_to<T>(v.first.value->value);
        if( r.second )
        {
            return r.first;
        }
        throw std::runtime_error("Failed to convert the configuration value");
    }
    throw std::runtime_error("Could not find specified configuration value");
}

    template<class T>
T Configuration::get( const std::string& path, const T& t, const std::string& com)
{
    std::pair<std::string,bool> nv = convert_to(t);
    if( ! nv.second ) // conversion failed, don't even bother to insert.
    {
        return t;
    }

    getpair v = internal_insert(path,com,false);
    if( v.second == inserted ) // was inserted, so new one
    {
        v.first.value->value = nv.first;
        return t; // Return it
    }
    else if ( v.second == value ) // was already there, so get it from there
    {
        std::pair<T,bool> r = convert_to<T>(v.first.value->value);
        if( r.second ) // conversion just fine
        {
            return r.first;
        }
        v.first.value->value = nv.first;
        // conversion failed, return the default
    } 
    // here some other failure was there...
    return t;
}

template<class T>
T Configuration::get( const std::string& path, const T& t) const
{
    const_getpair v = internal_get(path);
    if( v.second == value )
    {
        std::pair<T,bool> r = convert_to<T>(v.first.value->value);
        if( r.second )
        {
            return r.first.value;
        }
    }
    return t;
}

#endif
