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

// Boost.Runtime.Param
#include <boost/test/utils/runtime/cla/named_parameter.hpp>
#include <boost/test/utils/runtime/cla/parser.hpp>

namespace rt  = boost::runtime;
namespace cla = boost::runtime::cla;

// STL
#include <iostream>
#include <iterator>

//_____________________________________________________________________//

struct Point : std::pair<int,int> {
    bool parse( rt::cstring& in ) {
        in.trim_left();

        if( first_char( in ) != '(' )
            return false;

        in.trim_left( 1 );
        rt::cstring::size_type pos = in.find( ")" );

        if( pos == rt::cstring::npos )
            return false;
        
        rt::cstring ss( in.begin(), pos );
        pos = ss.find( "," );

        if( pos == rt::cstring::npos )
            return false;

        rt::cstring f( ss.begin(), pos );
        rt::cstring s( ss.begin()+pos+1, ss.end() );

        f.trim();
        s.trim();

        try {
            first  = boost::lexical_cast<int>( f );
            second = boost::lexical_cast<int>( s );
        }
        catch( boost::bad_lexical_cast const& ) {
            return false;
        }

        in.trim_left( ss.end()+1 );
        return true;
    }
};

std::ostream& operator<<( std::ostream& ostr, Point const& p )
{
    ostr << '(' << p.first << ',' << p.second << ')';

    return ostr;
}

struct Segment : std::pair<Point,Point> {
    bool parse( rt::cstring& in ) {
        in.trim_left();

        if( first_char( in ) != '[' )
            return false;

        in.trim_left( 1 );

        if( !first.parse( in ) )
            return false;

        in.trim_left();

        if( first_char( in ) != ',' )
            return false;

        in.trim_left( 1 );

        if( !second.parse( in ) )
            return false;

        in.trim_left();

        if( first_char( in ) != ']' )
            return false;

        in.trim_left( 1 );

        return true;
    }
};

std::ostream& operator<<( std::ostream& ostr, Segment const& p )
{
    ostr << '[' << p.first << ',' << p.second << ']';

    return ostr;
}

struct Circle : std::pair<Point,int> {
    bool parse( rt::cstring& in ) {
        in.trim_left();

        if( first_char( in ) != '[' )
            return false;

        in.trim_left( 1 );

        if( !first.parse( in ) )
            return false;

        in.trim_left();

        if( first_char( in ) != ','  )
            return false;

        in.trim_left( 1 );

        rt::cstring::size_type pos = in.find( "]" );

        if( pos == rt::cstring::npos )
            return false;

        rt::cstring ss( in.begin(), pos );
        ss.trim();

        try {
            second = boost::lexical_cast<int>( ss );
        }
        catch( boost::bad_lexical_cast const& ) {
            return false;
        }

        in.trim_left( pos+1 );

        return true;
    }
};

std::ostream& operator<<( std::ostream& ostr, Circle const& p )
{
    ostr << '[' << p.first << ',' << p.second << ']';

    return ostr;
}

//_____________________________________________________________________//

template<typename T>
class ShapeIdPolicy : public cla::identification_policy {
    rt::cstring m_name;
    rt::cstring m_usage_str;
public:
    explicit ShapeIdPolicy( rt::cstring name )
    : cla::identification_policy( boost::rtti::type_id<ShapeIdPolicy<T> >() )
    , m_name( name ) {}

    virtual bool        responds_to( rt::cstring name ) const       { return m_name == name; }
    virtual bool        conflict_with( cla::identification_policy const& ) const { return false; }
    virtual rt::cstring id_2_report() const                         { return m_name; }
    virtual void        usage_info( rt::format_stream& fs ) const   { fs << m_name; }

    virtual bool        matching( cla::parameter const& p, cla::argv_traverser& tr, bool primary ) const
    {
        T s;

        rt::cstring in = tr.input();
        return s.parse( in );
    }
};

//_____________________________________________________________________//

template<typename T>
class ShapeArgumentFactory : public cla::argument_factory {
    rt::cstring m_usage_str;
public:
    explicit ShapeArgumentFactory( rt::cstring usage ) : m_usage_str( usage ) {}

    // Argument factory interface
    virtual rt::argument_ptr    produce_using( cla::parameter& p, cla::argv_traverser& tr )
    {
        T s;

        rt::cstring in = tr.input();
        s.parse( in );
        tr.trim( in.begin() - tr.input().begin() );

        if( !p.actual_argument() ) {
            rt::argument_ptr   res;

            rt::typed_argument<std::list<T> >* new_arg = new rt::typed_argument<std::list<T> >( p );

            new_arg->p_value.value.push_back( s );
            res.reset( new_arg );

            return res;
        }
        else {
            std::list<T>& arg_values = rt::arg_value<std::list<T> >( *p.actual_argument() );
            arg_values.push_back( s );

            return p.actual_argument();
        }
    }
    virtual rt::argument_ptr    produce_using( cla::parameter& p, cla::parser const& ) { return rt::argument_ptr(); }
    virtual void                argument_usage_info( rt::format_stream& fs ) { fs << m_usage_str; }
};

//_____________________________________________________________________//

struct SegmentParam : cla::parameter {
    SegmentParam()
    : cla::parameter( m_id_policy, m_arg_factory )
    , m_id_policy( "segment" )
    , m_arg_factory( ":((P1x,P1y), (P2x,P2y)) ... ((P1x,P1y), (P2x,P2y))" )
    {}

    ShapeIdPolicy<Segment>          m_id_policy;
    ShapeArgumentFactory<Segment>   m_arg_factory;
};

inline boost::shared_ptr<SegmentParam>
segment_param() { return boost::shared_ptr<SegmentParam>( new SegmentParam ); }

//_____________________________________________________________________//

struct CircleParam : cla::parameter {
    CircleParam()
        : cla::parameter( m_id_policy, m_arg_factory )
        , m_id_policy( "circle" )
        , m_arg_factory( ":((P1x,P1y), R) ... ((P1x,P1y), R)" )
    {}

    ShapeIdPolicy<Circle>          m_id_policy;
    ShapeArgumentFactory<Circle>   m_arg_factory;
};

inline boost::shared_ptr<CircleParam>
circle_param() { return boost::shared_ptr<CircleParam>( new CircleParam ); }

//_____________________________________________________________________//

int main() {
    char* argv[] = { "basic", "[(1,", "1)", ",", "(7,", "-1", ")]", "[(", "1,1)", ",7", "]", "[(3,", "1", ")", ",", "2]",
    "[", "(2,7", "),", "(5", ",1", ")]" };
    int argc = sizeof(argv)/sizeof(char*);

    try {
        cla::parser P;

        P << circle_param()  - cla::optional
          << segment_param() - cla::optional;

        P.parse( argc, argv );

        boost::optional<std::list<Segment> > segments;
        boost::optional<std::list<Circle> > circles;

        P.get( "segment", segments );

        if( segments ) {
            std::cout << "segments : ";
            std::copy( segments->begin(), segments->end(), std::ostream_iterator<Segment>( std::cout, "; " ) );
            std::cout << std::endl;
        }

        P.get( "circle", circles );

        if( circles ) {
            std::cout << "circles : ";
            std::copy( circles->begin(), circles->end(), std::ostream_iterator<Circle>( std::cout, "; " ) );
            std::cout << std::endl;
        }
    }
    catch( rt::logic_error const& ex ) {
        std::cout << "Logic error: " << ex.msg() << std::endl;
        return -1;
    }

    return 0;
}

// EOF
