//  (C) Copyright Gennadiy Rozental 2005-2008.
//  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: 49312 $
//
//  Description : implements facility to hide input traversing details
// ***************************************************************************

#ifndef BOOST_RT_CLA_ARGV_TRAVERSER_IPP_070604GER
#define BOOST_RT_CLA_ARGV_TRAVERSER_IPP_070604GER

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

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

// STL
#include <memory>
#include <cstring>

#ifdef BOOST_NO_STDC_NAMESPACE
namespace std { using ::memcpy; }
#endif

namespace boost {

namespace BOOST_RT_PARAM_NAMESPACE {

namespace cla {

// ************************************************************************** //
// **************          runtime::cla::argv_traverser        ************** //
// ************************************************************************** //

BOOST_RT_PARAM_INLINE
argv_traverser::argv_traverser()
: p_ignore_mismatch( false ), p_separator( BOOST_RT_PARAM_LITERAL( ' ' ) )
{
}

//____________________________________________________________________________//

BOOST_RT_PARAM_INLINE void
argv_traverser::init( int argc, char_type** argv )
{
    for( int index = 1; index < argc; ++index ) {
        m_buffer += argv[index];
        if( index != argc-1 )
            m_buffer += BOOST_RT_PARAM_LITERAL( ' ' );
    }

    m_remainder.reset( new char_type[m_buffer.size()+1] );
    m_remainder_size    = 0;
    m_work_buffer       = m_buffer;
    m_commited_end      = m_work_buffer.begin();

    BOOST_RT_PARAM_TRACE( "Input buffer: " << m_buffer );

    next_token();
}

//____________________________________________________________________________//

BOOST_RT_PARAM_INLINE void
argv_traverser::remainder( int& argc, char_type** argv )
{
    argc = 1;
    std::size_t pos = 0;
    while(pos < m_remainder_size ) {
        argv[argc++] = m_remainder.get() + pos;

        pos = std::find( m_remainder.get() + pos, m_remainder.get() + m_remainder_size, 
                         BOOST_RT_PARAM_LITERAL( ' ' ) ) - m_remainder.get();
        m_remainder[pos++] = BOOST_RT_PARAM_LITERAL( '\0' );
    }
}

//____________________________________________________________________________//

BOOST_RT_PARAM_INLINE cstring
argv_traverser::token() const
{
    return m_token;
}

//____________________________________________________________________________//

BOOST_RT_PARAM_INLINE void
argv_traverser::next_token()
{
    if( m_work_buffer.is_empty() )
        return;

    m_work_buffer.trim_left( m_token.size() ); // skip remainder of current token

    if( m_work_buffer.size() != m_buffer.size() ) // !! is there a better way to identify first token
        m_work_buffer.trim_left( 1 ); // skip separator if not first token;

    m_token.assign( m_work_buffer.begin(),
                    std::find( m_work_buffer.begin(), m_work_buffer.end(), p_separator ) );
}

//____________________________________________________________________________//

BOOST_RT_PARAM_INLINE cstring
argv_traverser::input() const
{
    return m_work_buffer;
}

//____________________________________________________________________________//

BOOST_RT_PARAM_INLINE void
argv_traverser::trim( std::size_t size )
{
    m_work_buffer.trim_left( size );

    if( size <= m_token.size() )
        m_token.trim_left( size );
    else {
        m_token.assign( m_work_buffer.begin(),
                        std::find( m_work_buffer.begin(), m_work_buffer.end(), p_separator ) );
    }
}

//____________________________________________________________________________//

BOOST_RT_PARAM_INLINE bool
argv_traverser::match_front( cstring str )
{
    return m_work_buffer.size() < str.size() ? false : m_work_buffer.substr( 0, str.size() ) == str;
}

//____________________________________________________________________________//

BOOST_RT_PARAM_INLINE bool
argv_traverser::match_front( char_type c )
{
    return first_char( m_work_buffer ) == c;
}

//____________________________________________________________________________//

BOOST_RT_PARAM_INLINE bool
argv_traverser::eoi() const
{
    return m_work_buffer.is_empty();
}

//____________________________________________________________________________//

BOOST_RT_PARAM_INLINE void
argv_traverser::commit()
{
    m_commited_end = m_work_buffer.begin();
}

//____________________________________________________________________________//

BOOST_RT_PARAM_INLINE void
argv_traverser::rollback()
{
    m_work_buffer.assign( m_commited_end, m_work_buffer.end() );
    m_token.assign( m_work_buffer.begin(),
                    std::find( m_work_buffer.begin(), m_work_buffer.end(), p_separator ) );

}

//____________________________________________________________________________//

BOOST_RT_PARAM_INLINE std::size_t
argv_traverser::input_pos() const
{
    return m_work_buffer.begin() - m_commited_end;
}

//____________________________________________________________________________//

BOOST_RT_PARAM_INLINE bool
argv_traverser::handle_mismatch()
{
    if( !p_ignore_mismatch )
        return false;

    std::memcpy( m_remainder.get() + m_remainder_size, token().begin(), token().size() );
    m_remainder_size += token().size();
    m_remainder[m_remainder_size++] = p_separator;

    next_token();
    commit();

    return true;
}

//____________________________________________________________________________//

} // namespace cla

} // namespace BOOST_RT_PARAM_NAMESPACE

} // namespace boost

#endif // BOOST_RT_CLA_ARGV_TRAVERSER_IPP_070604GER
