//  (C) Copyright Gennadiy Rozental 2005-2007.

//  Use, modification, and distribution are subject to 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: 43798 $

//

//  Description : implements parser - public interface for CLA parsing and accessing

// ***************************************************************************



#ifndef BOOST_RT_CLA_PARSER_IPP_062904GER

#define BOOST_RT_CLA_PARSER_IPP_062904GER



// Boost.Runtime.Parameter

#include <boost/test/utils/runtime/config.hpp>

#include <boost/test/utils/runtime/trace.hpp>

#include <boost/test/utils/runtime/argument.hpp>



#include <boost/test/utils/runtime/cla/argv_traverser.hpp>

#include <boost/test/utils/runtime/cla/parameter.hpp>

#include <boost/test/utils/runtime/cla/modifier.hpp>

#include <boost/test/utils/runtime/cla/validation.hpp>

#include <boost/test/utils/runtime/cla/parser.hpp>



// Boost.Test

#include <boost/test/utils/basic_cstring/io.hpp>

#include <boost/test/utils/foreach.hpp>



// Boost

#include <boost/lexical_cast.hpp>



namespace boost {



namespace BOOST_RT_PARAM_NAMESPACE {



namespace cla {



// ************************************************************************** //

// **************             runtime::cla::parser             ************** //

// ************************************************************************** //



BOOST_RT_PARAM_INLINE

parser::parser( cstring program_name )

{

    assign_op( m_program_name, program_name, 0 );

}



//____________________________________________________________________________//



BOOST_RT_PARAM_INLINE parser::param_iterator

parser::first_param() const

{

    return m_parameters.begin();

}



//____________________________________________________________________________//



BOOST_RT_PARAM_INLINE parser::param_iterator

parser::last_param() const

{

    return m_parameters.end();

}



//____________________________________________________________________________//



BOOST_RT_PARAM_INLINE argument const&

parser::valid_argument( cstring string_id ) const

{

    const_argument_ptr arg = (*this)[string_id];



    BOOST_RT_PARAM_VALIDATE_LOGIC( !!arg, "Actual argument for parameter " << string_id << " is not present" );



    return *arg;

}



//____________________________________________________________________________//



BOOST_RT_PARAM_INLINE parser&

parser::operator<<( parameter_ptr new_param )

{

    BOOST_TEST_FOREACH( parameter_ptr, old_param, m_parameters ) {

        BOOST_RT_PARAM_VALIDATE_LOGIC( !old_param->conflict_with( *new_param ) && 

                                       !new_param->conflict_with( *old_param ),

            BOOST_RT_PARAM_LITERAL( "Definition of parameter " )                << new_param->id_2_report() << 

            BOOST_RT_PARAM_LITERAL( " conflicts with defintion of parameter " ) << old_param->id_2_report() );

    }



    m_parameters.push_back( new_param );



    return *this;

}



//____________________________________________________________________________//



BOOST_RT_PARAM_INLINE void

parser::parse( int& argc, char_type** argv )

{

    if( m_program_name.empty() ) {

        m_program_name.assign( argv[0] );

        dstring::size_type pos = m_program_name.find_last_of( BOOST_RT_PARAM_LITERAL( "/\\" ) );



        if( pos != cstring::npos )

            m_program_name.erase( 0, pos+1 );

    }



    m_traverser.init( argc, argv );



    try {

        while( !m_traverser.eoi() ) {

            parameter_ptr found_param;



            BOOST_RT_PARAM_TRACE( "Total " << m_parameters.size() << " parameters registered" );



            BOOST_TEST_FOREACH( parameter_ptr const&, curr_param, m_parameters ) {

                BOOST_RT_PARAM_TRACE( "Try parameter " << curr_param->id_2_report() );



                if( curr_param->matching( m_traverser, !found_param ) ) {

                    BOOST_RT_PARAM_TRACE( "Match found" );

                    BOOST_RT_CLA_VALIDATE_INPUT( !found_param, (m_traverser.rollback(),m_traverser), "Ambiguous input" );



                    found_param = curr_param;

                }



                m_traverser.rollback();

            }



            if( !found_param ) {

                BOOST_RT_PARAM_TRACE( "No match found" );

                BOOST_RT_CLA_VALIDATE_INPUT( m_traverser.handle_mismatch(), m_traverser,

                                             BOOST_RT_PARAM_LITERAL( "Unexpected input" ) );



                continue;

            }



            BOOST_RT_PARAM_TRACE( "Parse argument value" );

            found_param->produce_argument( m_traverser );



            m_traverser.commit();

        }



        BOOST_TEST_FOREACH( parameter_ptr const&, curr_param, m_parameters ) {

            if( !curr_param->p_optional && !curr_param->actual_argument() ) {

                curr_param->produce_argument( *this );



                BOOST_RT_PARAM_VALIDATE_LOGIC( curr_param->actual_argument(),

                    BOOST_RT_PARAM_LITERAL( "Required argument for parameter " ) << curr_param->id_2_report()

                        << BOOST_RT_PARAM_LITERAL( " is missing" ) );

            }

        }

    }

    catch( bad_lexical_cast const& ) {

        BOOST_RT_PARAM_REPORT_LOGIC_ERROR( 

            BOOST_RT_PARAM_LITERAL( "String to value convertion error during input parsing" ) );

    }



    m_traverser.remainder( argc, argv );

}



//____________________________________________________________________________//



BOOST_RT_PARAM_INLINE const_argument_ptr

parser::operator[]( cstring string_id ) const

{

    parameter_ptr found_param;



    BOOST_TEST_FOREACH( parameter_ptr const&, curr_param, m_parameters ) {

        if( curr_param->responds_to( string_id ) ) {

            BOOST_RT_PARAM_VALIDATE_LOGIC( !found_param,

                                           BOOST_RT_PARAM_LITERAL( "Ambiguous parameter string id: " ) << string_id );



            found_param = curr_param;

        }

    }



    BOOST_RT_PARAM_VALIDATE_LOGIC( found_param, BOOST_RT_PARAM_LITERAL( "Unknown parameter: " ) << string_id );



    return found_param->actual_argument();

}



//____________________________________________________________________________//



BOOST_RT_PARAM_INLINE cstring

parser::get( cstring string_id ) const

{

    return get<cstring>( string_id );

}



//____________________________________________________________________________//



BOOST_RT_PARAM_INLINE void

parser::usage( out_stream& ostr )

{

    if( m_program_name.empty() )

        assign_op( m_program_name, BOOST_RT_PARAM_CSTRING_LITERAL( "<program>" ), 0 );



    format_stream fs;



    fs << m_program_name;



    BOOST_TEST_FOREACH( parameter_ptr const&, curr_param, m_parameters ) {

        fs << BOOST_RT_PARAM_LITERAL( ' ' );



        if( curr_param->p_optional )

            fs << BOOST_RT_PARAM_LITERAL( '[' );



        curr_param->usage_info( fs );



        if( curr_param->p_optional )

            fs << BOOST_RT_PARAM_LITERAL( ']' );



        if( curr_param->p_multiplicable ) {

            fs << BOOST_RT_PARAM_CSTRING_LITERAL( " ... " );

            

            if( curr_param->p_optional )

                fs << BOOST_RT_PARAM_LITERAL( '[' );



            curr_param->usage_info( fs );



            if( curr_param->p_optional )

                fs << BOOST_RT_PARAM_LITERAL( ']' );

        }

    }



    ostr << BOOST_RT_PARAM_CSTRING_LITERAL( "Usage:\n" ) << fs.str() << std::endl;

}



//____________________________________________________________________________//



BOOST_RT_PARAM_INLINE void

parser::help( out_stream& ostr )

{

    usage( ostr );



    bool need_where = true;



    BOOST_TEST_FOREACH( parameter_ptr const&, curr_param, m_parameters ) {

        if( curr_param->p_description->empty() )

            continue;



        if( need_where ) {

            ostr << BOOST_RT_PARAM_CSTRING_LITERAL( "where:\n" );

            need_where = false;

        }



        ostr << curr_param->id_2_report() << BOOST_RT_PARAM_CSTRING_LITERAL( " - " ) << curr_param->p_description << std::endl;

    }

}



//____________________________________________________________________________//



} // namespace cla



} // namespace BOOST_RT_PARAM_NAMESPACE



} // namespace boost



#endif // BOOST_RT_CLA_PARSER_IPP_062904GER

