// Copyright (C) 2012  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#ifndef DLIB_GET_OPTiON_Hh_
#define DLIB_GET_OPTiON_Hh_

#include "get_option_abstract.h"
#include "../string.h"
#include "../is_kind.h"

namespace dlib
{

// ----------------------------------------------------------------------------------------

    class option_parse_error : public error
    {
    public:
        option_parse_error(const std::string& option_string, const std::string& str):
            error(EOPTION_PARSE,"Error parsing argument for option '" + option_string + "', offending string is '" + str + "'.") {}
    };

// ----------------------------------------------------------------------------------------

    template <typename config_reader_type, typename T>
    T impl_config_reader_get_option (
        const config_reader_type& cr,
        const std::string& option_name,
        const std::string& full_option_name,
        T default_value
    )
    {
        std::string::size_type pos = option_name.find_first_of(".");
        if (pos == std::string::npos)
        {
            if (cr.is_key_defined(option_name))
            {
                try{ return string_cast<T>(cr[option_name]); }
                catch (string_cast_error&) { throw option_parse_error(full_option_name, cr[option_name]); }
            }
        }
        else
        {
            std::string block_name = option_name.substr(0,pos);
            if (cr.is_block_defined(block_name))
            {
                return impl_config_reader_get_option(cr.block(block_name), 
                                                     option_name.substr(pos+1),
                                                     full_option_name,
                                                     default_value);
            }
        }

        return default_value;
    }

// ----------------------------------------------------------------------------------------

    template <typename cr_type, typename T>
    typename enable_if<is_config_reader<cr_type>,T>::type get_option (
        const cr_type& cr,
        const std::string& option_name,
        T default_value
    )
    {
        return impl_config_reader_get_option(cr, option_name, option_name, default_value);
    }

// ----------------------------------------------------------------------------------------

    template <typename parser_type, typename T>
    typename disable_if<is_config_reader<parser_type>,T>::type get_option (
        const parser_type& parser,
        const std::string& option_name,
        T default_value
    )
    {
        // make sure requires clause is not broken
        DLIB_ASSERT( parser.option_is_defined(option_name) == true &&
                     parser.option(option_name).number_of_arguments() == 1,
            "\t T get_option()"
            << "\n\t option_name: " << option_name
            << "\n\t parser.option_is_defined(option_name):            " << parser.option_is_defined(option_name)
            << "\n\t parser.option(option_name).number_of_arguments(): " << parser.option(option_name).number_of_arguments()
            );

        if (parser.option(option_name))
        {
            try
            {
                default_value = string_cast<T>(parser.option(option_name).argument()); 
            }
            catch (string_cast_error&) 
            { 
                throw option_parse_error(option_name, parser.option(option_name).argument()); 
            }
        }
        return default_value;
    }

// ----------------------------------------------------------------------------------------

    template <typename parser_type, typename cr_type, typename T>
    typename disable_if<is_config_reader<parser_type>,T>::type get_option (
        const parser_type& parser,
        const cr_type& cr,
        const std::string& option_name,
        T default_value
    )
    {
        // make sure requires clause is not broken
        DLIB_ASSERT( parser.option_is_defined(option_name) == true &&
                     parser.option(option_name).number_of_arguments() == 1,
            "\t T get_option()"
            << "\n\t option_name: " << option_name
            << "\n\t parser.option_is_defined(option_name):            " << parser.option_is_defined(option_name)
            << "\n\t parser.option(option_name).number_of_arguments(): " << parser.option(option_name).number_of_arguments()
            );

        if (parser.option(option_name))
            return get_option(parser, option_name, default_value); 
        else
            return get_option(cr, option_name, default_value);
    }

// ----------------------------------------------------------------------------------------

    template <typename parser_type, typename cr_type, typename T>
    typename disable_if<is_config_reader<parser_type>,T>::type get_option (
        const cr_type& cr,
        const parser_type& parser,
        const std::string& option_name,
        T default_value
    )
    {
        // make sure requires clause is not broken
        DLIB_ASSERT( parser.option_is_defined(option_name) == true &&
                     parser.option(option_name).number_of_arguments() == 1,
            "\t T get_option()"
            << "\n\t option_name: " << option_name
            << "\n\t parser.option_is_defined(option_name):            " << parser.option_is_defined(option_name)
            << "\n\t parser.option(option_name).number_of_arguments(): " << parser.option(option_name).number_of_arguments()
            );

        if (parser.option(option_name))
            return get_option(parser, option_name, default_value); 
        else
            return get_option(cr, option_name, default_value);
    }

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    template <typename T>
    inline std::string get_option (
        const T& cr,
        const std::string& option_name,
        const char* default_value
    )
    {
        return get_option(cr, option_name, std::string(default_value));
    }

// ----------------------------------------------------------------------------------------

    template <typename T, typename U>
    inline std::string get_option (
        const T& parser,
        const U& cr,
        const std::string& option_name,
        const char* default_value
    )
    {
        return get_option(parser, cr, option_name, std::string(default_value));
    }

// ----------------------------------------------------------------------------------------

}

#endif // DLIB_GET_OPTiON_Hh_