//  (C) Copyright Gennadiy Rozental 2001.
//  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)

//  See http://www.boost.org/libs/test for the library home page.
//
//  File        : $RCSfile$
//
//  Version     : $Revision$
//
//  Description : argument factories for different kinds of parameters
// ***************************************************************************

#ifndef BOOST_TEST_UTILS_RUNTIME_ARGUMENT_FACTORY_HPP
#define BOOST_TEST_UTILS_RUNTIME_ARGUMENT_FACTORY_HPP

// Boost.Test Runtime parameters
#include <boost/test/utils/runtime/errors.hpp>
#include <boost/test/utils/runtime/argument.hpp>

// Boost.Test
#include <boost/test/utils/basic_cstring/io.hpp>
#include <boost/test/utils/basic_cstring/compare.hpp>
#include <boost/test/utils/string_cast.hpp>

// Boost
#include <boost/function/function2.hpp>

// STL
#include <vector>

#include <boost/test/detail/suppress_warnings.hpp>

namespace boost {
namespace runtime {

// ************************************************************************** //
// **************          runtime::value_interpreter          ************** //
// ************************************************************************** //

template<typename ValueType, bool is_enum>
struct value_interpreter;

//____________________________________________________________________________//

template<typename ValueType>
struct value_interpreter<ValueType, false> {
    template<typename Modifiers>
    explicit    value_interpreter( Modifiers const& ) {}

    ValueType interpret( cstring param_name, cstring source ) const
    {
        ValueType res;
        if( !unit_test::utils::string_as<ValueType>( source, res ) )
            BOOST_TEST_I_THROW( format_error( param_name ) << source <<
                                " can't be interpreted as value of parameter " << param_name << "." );
        return res;
    }
};

//____________________________________________________________________________//

template<>
struct value_interpreter<std::string, false> {
    template<typename Modifiers>
    explicit    value_interpreter( Modifiers const& ) {}

    std::string interpret( cstring, cstring source ) const
    {
        return std::string( source.begin(), source.size() );
    }
};

//____________________________________________________________________________//

template<>
struct value_interpreter<cstring, false> {
    template<typename Modifiers>
    explicit    value_interpreter( Modifiers const& ) {}

    cstring interpret( cstring, cstring source ) const
    {
        return source;
    }
};

//____________________________________________________________________________//

template<>
struct value_interpreter<bool, false> {
    template<typename Modifiers>
    explicit    value_interpreter( Modifiers const& ) {}

    bool    interpret( cstring param_name, cstring source ) const
    {
        static cstring const s_YES( "YES" );
        static cstring const s_Y( "Y" );
        static cstring const s_NO( "NO" );
        static cstring const s_N( "N" );
        static cstring const s_TRUE( "TRUE" );
        static cstring const s_FALSE( "FALSE" );
        static cstring const s_one( "1" );
        static cstring const s_zero( "0" );

        source.trim();

        if( source.is_empty() ||
            case_ins_eq( source, s_YES ) ||
            case_ins_eq( source, s_Y ) ||
            case_ins_eq( source, s_one ) ||
            case_ins_eq( source, s_TRUE ) )
            return true;

        if( case_ins_eq( source, s_NO ) ||
            case_ins_eq( source, s_N ) ||
            case_ins_eq( source, s_zero ) ||
            case_ins_eq( source, s_FALSE ) )
            return false;

        BOOST_TEST_I_THROW( format_error( param_name ) << source << " can't be interpreted as bool value." );
    }
};

//____________________________________________________________________________//

template<typename EnumType>
struct value_interpreter<EnumType, true> {
    template<typename Modifiers>
    explicit        value_interpreter( Modifiers const& m )
#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) && !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX)
    : m_name_to_value( m[enum_values<EnumType>::value] )
    {
    }
#else
    {
        std::vector<std::pair<cstring,EnumType> > const& values = m[enum_values<EnumType>::value];

        m_name_to_value.insert( values.begin(), values.end() );
    }
#endif

    EnumType        interpret( cstring param_name, cstring source ) const
    {
        typename std::map<cstring,EnumType>::const_iterator found = m_name_to_value.find( source );

        BOOST_TEST_I_ASSRT( found != m_name_to_value.end(),
                            format_error( param_name ) << source <<
                            " is not a valid enumeration value name for parameter " << param_name << "." );

        return found->second;
    }

private:
    // Data members
    std::map<cstring,EnumType>  m_name_to_value;
};

//____________________________________________________________________________//

// ************************************************************************** //
// **************           runtime::argument_factory          ************** //
// ************************************************************************** //

template<typename ValueType, bool is_enum, bool repeatable>
class argument_factory;

//____________________________________________________________________________//

template<typename ValueType, bool is_enum>
class argument_factory<ValueType, is_enum, false> {
public:
    template<typename Modifiers>
    explicit    argument_factory( Modifiers const& m )
    : m_interpreter( m )
    , m_optional_value( nfp::opt_get( m, optional_value, ValueType() ) )
    , m_default_value( nfp::opt_get( m, default_value, ValueType() ) )
    {
    }

    void        produce_argument( cstring source, cstring param_name, arguments_store& store ) const
    {
        store.set( param_name, source.empty() ? m_optional_value : m_interpreter.interpret( param_name, source ) );
    }

    void        produce_default( cstring param_name, arguments_store& store ) const
    {
        store.set( param_name, m_default_value );
    }

private:
    // Data members
    typedef value_interpreter<ValueType, is_enum> interp_t;
    interp_t    m_interpreter;
    ValueType   m_optional_value;
    ValueType   m_default_value;
};

//____________________________________________________________________________//

template<typename ValueType, bool is_enum>
class argument_factory<ValueType, is_enum, true> {
public:
    template<typename Modifiers>
    explicit    argument_factory( Modifiers const& m )
    : m_interpreter( m )
    {
    }

    void        produce_argument( cstring source, cstring param_name, arguments_store& store ) const
    {
        ValueType value = m_interpreter.interpret( param_name, source );

        if( store.has( param_name ) ) {
            std::vector<ValueType>& values = store.get<std::vector<ValueType> >( param_name );
            values.push_back( value );
        }
        else {
            std::vector<ValueType> values( 1, value );

            store.set( param_name, values );
        }

    }
    void        produce_default( cstring param_name, arguments_store& store ) const
    {
        store.set( param_name, std::vector<ValueType>() );
    }

private:
    // Data members
    value_interpreter<ValueType, is_enum> m_interpreter;
};

//____________________________________________________________________________//

} // namespace runtime
} // namespace boost

#include <boost/test/detail/enable_warnings.hpp>

#endif // BOOST_TEST_UTILS_RUNTIME_ARGUMENT_FACTORY_HPP
