// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)


#ifndef BOOST_VARIABLES_MAP_VP_2003_05_19
#define BOOST_VARIABLES_MAP_VP_2003_05_19

#include <boost/program_options/config.hpp>

#include <boost/any.hpp>
#include <boost/shared_ptr.hpp>

#include <string>
#include <map>
#include <set>

namespace boost { namespace program_options {

    template<class charT>
    class basic_parsed_options;

    class value_semantic;
    class variables_map;

    // forward declaration

    /** Stores in 'm' all options that are defined in 'options'. 
        If 'm' already has a non-defaulted value of an option, that value
        is not changed, even if 'options' specify some value.        
    */
    BOOST_PROGRAM_OPTIONS_DECL void store(const basic_parsed_options<char>& options, variables_map& m,
                    bool utf8 = false);

    /** Stores in 'm' all options that are defined in 'options'. 
        If 'm' already has a non-defaulted value of an option, that value
        is not changed, even if 'options' specify some value.        
        This is wide character variant.
    */
    BOOST_PROGRAM_OPTIONS_DECL void store(const basic_parsed_options<wchar_t>& options, 
                    variables_map& m);


    /** Runs all 'notify' function for options in 'm'. */
    BOOST_PROGRAM_OPTIONS_DECL void notify(variables_map& m);

    /** Class holding value of option. Contains details about how the 
        value is set and allows to conveniently obtain the value.
    */
    class BOOST_PROGRAM_OPTIONS_DECL variable_value {
    public:
        variable_value() : m_defaulted(false) {}
        variable_value(const boost::any& v, bool defaulted) 
        : v(v), m_defaulted(defaulted) 
        {}

        /** If stored value if of type T, returns that value. Otherwise,
            throws boost::bad_any_cast exception. */
       template<class T>
       const T& as() const {
           return boost::any_cast<const T&>(v);
       }
       /** @overload */
       template<class T>
       T& as() {
           return boost::any_cast<T&>(v);
       }

        /// Returns true if no value is stored.
        bool empty() const;
        /** Returns true if the value was not explicitly
            given, but has default value. */
        bool defaulted() const;
        /** Returns the contained value. */
        const boost::any& value() const;

        /** Returns the contained value. */
        boost::any& value();
    private:
        boost::any v;
        bool m_defaulted;
        // Internal reference to value semantic. We need to run
        // notifications when *final* values of options are known, and
        // they are known only after all sources are stored. By that
        // time options_description for the first source might not
        // be easily accessible, so we need to store semantic here.
        shared_ptr<const value_semantic> m_value_semantic;

        friend void store(const basic_parsed_options<char>& options, 
              variables_map& m, bool);
        friend void notify(variables_map& m);
    };

    /** Implements string->string mapping with convenient value casting
        facilities. */
    class BOOST_PROGRAM_OPTIONS_DECL abstract_variables_map {
    public:
        abstract_variables_map();
        abstract_variables_map(const abstract_variables_map* next);

        virtual ~abstract_variables_map() {}

        /** Obtains the value of variable 'name', from *this and
            possibly from the chain of variable maps.

            - if there's no value in *this.
                - if there's next variable map, returns value from it
                - otherwise, returns empty value

            - if there's defaulted value
                - if there's next varaible map, which has a non-defauled
                  value, return that
                - otherwise, return value from *this

            - if there's a non-defauled value, returns it.
        */
        const variable_value& operator[](const std::string& name) const;

        /** Sets next variable map, which will be used to find
           variables not found in *this. */
        void next(abstract_variables_map* next);

    private:
        /** Returns value of variable 'name' stored in *this, or
            empty value otherwise. */
        virtual const variable_value& get(const std::string& name) const = 0;

        const abstract_variables_map* m_next;
    };

    /** Concrete variables map which store variables in real map. 
        
        This class is derived from std::map<std::string, variable_value>,
        so you can use all map operators to examine its content.
    */
    class BOOST_PROGRAM_OPTIONS_DECL variables_map : public abstract_variables_map,
                               public std::map<std::string, variable_value>
    {
    public:
        variables_map();
        variables_map(const abstract_variables_map* next);

        // Resolve conflict between inherited operators.
        const variable_value& operator[](const std::string& name) const
        { return abstract_variables_map::operator[](name); }

    private:
        /** Implementation of abstract_variables_map::get
            which does 'find' in *this. */
        const variable_value& get(const std::string& name) const;

        /** Names of option with 'final' values -- which should not
            be changed by subsequence assignments. */
        std::set<std::string> m_final;

        friend void store(const basic_parsed_options<char>& options, 
                          variables_map& xm,
                          bool utf8);
    };


    /*
     * Templates/inlines
     */

    inline bool
    variable_value::empty() const
    {
        return v.empty();
    }

    inline bool
    variable_value::defaulted() const
    {
        return m_defaulted;
    }

    inline
    const boost::any&
    variable_value::value() const
    {
        return v;
    }

    inline
    boost::any&
    variable_value::value()
    {
        return v;
    }

}}

#endif
