// Add some nice header here... :)

#ifndef BOOST_COMBINED_ARGUMENT_TYPE_HPP
#define BOOST_COMBINED_ARGUMENT_TYPE_HPP

#include <boost/type_traits/is_convertible.hpp>
#include <boost/type_traits/is_const.hpp>
#include <boost/type_traits/remove_const.hpp>

#include <boost/mpl/vector.hpp>
#include <boost/mpl/find.hpp>
#include <boost/mpl/if.hpp>

namespace boost {

template< typename L, typename R > struct combined_argument_type
{
private:
    typedef mpl::vector<
        const bool,
        const char,
        const signed char,
        const unsigned char,
        const wchar_t,
        const short,
        const unsigned short > cutted_types; // GRRR, vector should allow 16 elements!

    typedef mpl::vector<
        const int,
        const unsigned int,
        const long,
        const unsigned long,
        const long long,
        const unsigned long long,
        const float,
        const double,
        const long double > sorted_types;
    
    typedef typename remove_const< L >::type non_const_L;
    typedef typename remove_const< R >::type non_const_R;
    
    enum {
        is_const_L = ( is_const< L >::value || !is_const< R >::value ),
        is_const_R = ( is_const< R >::value || !is_const< L >::value ),
        pos_L = mpl::find< sorted_types, const L >::type::pos::value,
        pos_R = mpl::find< sorted_types, const R >::type::pos::value,
        l2r = is_const_R && is_convertible< non_const_L&, non_const_R& >::value,
        r2l = is_const_L && is_convertible< non_const_R&, non_const_L& >::value
    };

    typedef typename mpl::if_< is_convertible< R, L >, L, R >::type T1;
    typedef typename mpl::if_c< ( pos_L != -1 ) && ( pos_L < pos_R ), R, T1 >::type T2;
    typedef typename mpl::if_c< l2r, R&, T2 >::type T3;
    
public:
    typedef typename mpl::if_c< r2l, L&, T3 >::type type;
};

} // namespace boost

#endif // BOOST_COMBINED_ARGUMENT_TYPE_HPP

