///////////////////////////////////////////////////////////////////////////////

//

// Copyright David Abrahams 2002, Joel de Guzman, 2002.

// 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)

//

///////////////////////////////////////////////////////////////////////////////

#ifndef INIT_JDG20020820_HPP

#define INIT_JDG20020820_HPP



# include <boost/python/detail/prefix.hpp>



#include <boost/python/detail/type_list.hpp>

#include <boost/python/args_fwd.hpp>

#include <boost/python/detail/make_keyword_range_fn.hpp>

#include <boost/python/def_visitor.hpp>



#include <boost/mpl/if.hpp>

#include <boost/mpl/eval_if.hpp>

#include <boost/mpl/size.hpp>

#include <boost/mpl/iterator_range.hpp>

#include <boost/mpl/empty.hpp>

#include <boost/mpl/begin_end.hpp>

#include <boost/mpl/bool.hpp>

#include <boost/mpl/prior.hpp>

#include <boost/mpl/joint_view.hpp>

#include <boost/mpl/back.hpp>



#include <boost/type_traits/is_same.hpp>



#include <boost/preprocessor/enum_params_with_a_default.hpp>

#include <boost/preprocessor/enum_params.hpp>



#include <utility>



///////////////////////////////////////////////////////////////////////////////

#define BOOST_PYTHON_OVERLOAD_TYPES_WITH_DEFAULT                                \

    BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(                                        \

        BOOST_PYTHON_MAX_ARITY,                                                 \

        class T,                                                                \

        mpl::void_)                                                             \



#define BOOST_PYTHON_OVERLOAD_TYPES                                             \

    BOOST_PP_ENUM_PARAMS_Z(1,                                                   \

        BOOST_PYTHON_MAX_ARITY,                                                 \

        class T)                                                                \



#define BOOST_PYTHON_OVERLOAD_ARGS                                              \

    BOOST_PP_ENUM_PARAMS_Z(1,                                                   \

        BOOST_PYTHON_MAX_ARITY,                                                 \

        T)                                                                      \



///////////////////////////////////////////////////////////////////////////////

namespace boost { namespace python {



template <BOOST_PYTHON_OVERLOAD_TYPES_WITH_DEFAULT>

class init; // forward declaration





template <BOOST_PYTHON_OVERLOAD_TYPES_WITH_DEFAULT>

struct optional; // forward declaration



namespace detail

{

  namespace error

  {

    template <int keywords, int init_args>

    struct more_keywords_than_init_arguments

    {

        typedef char too_many_keywords[init_args - keywords >= 0 ? 1 : -1];

    };

  }



  //  is_optional<T>::value

  //

  //      This metaprogram checks if T is an optional

  //

#if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)



    template <class T>

    struct is_optional {



    private:



        template <BOOST_PYTHON_OVERLOAD_TYPES>

        static boost::type_traits::yes_type f(optional<BOOST_PYTHON_OVERLOAD_ARGS>);

        static boost::type_traits::no_type f(...);

        static T t();



    public:



        BOOST_STATIC_CONSTANT(

            bool, value =

                sizeof(f(t())) == sizeof(::boost::type_traits::yes_type));

        typedef mpl::bool_<value> type;

    };



#else



    template <class T>

    struct is_optional

      : mpl::false_

    {};



    template <BOOST_PYTHON_OVERLOAD_TYPES>

    struct is_optional<optional<BOOST_PYTHON_OVERLOAD_ARGS> >

      : mpl::true_

    {};

  

#endif



  template <int NDefaults>

  struct define_class_init_helper;



} // namespace detail



template <class DerivedT>

struct init_base : def_visitor<DerivedT>

{

    init_base(char const* doc_, detail::keyword_range const& keywords_)

        : m_doc(doc_), m_keywords(keywords_)

    {}

        

    init_base(char const* doc_)

        : m_doc(doc_)

    {}



    DerivedT const& derived() const

    {

        return *static_cast<DerivedT const*>(this);

    }

    

    char const* doc_string() const

    {

        return m_doc;

    }



    detail::keyword_range const& keywords() const

    {

        return m_keywords;

    }



    static default_call_policies call_policies()

    {

        return default_call_policies();

    }



 private:

    //  visit

    //

    //      Defines a set of n_defaults + 1 constructors for its

    //      class_<...> argument. Each constructor after the first has

    //      one less argument to its right. Example:

    //

    //          init<int, optional<char, long, double> >

    //

    //      Defines:

    //

    //          __init__(int, char, long, double)

    //          __init__(int, char, long)

    //          __init__(int, char)

    //          __init__(int)

    template <class classT>

    void visit(classT& cl) const

    {

        typedef typename DerivedT::signature signature;

        typedef typename DerivedT::n_arguments n_arguments;

        typedef typename DerivedT::n_defaults n_defaults;

    

        detail::define_class_init_helper<n_defaults::value>::apply(

            cl

          , derived().call_policies()

          , signature()

          , n_arguments()

          , derived().doc_string()

          , derived().keywords());

    }

    

    friend class python::def_visitor_access;

    

 private: // data members

    char const* m_doc;

    detail::keyword_range m_keywords;

};



template <class CallPoliciesT, class InitT>

class init_with_call_policies

    : public init_base<init_with_call_policies<CallPoliciesT, InitT> >

{

    typedef init_base<init_with_call_policies<CallPoliciesT, InitT> > base;

 public:

    typedef typename InitT::n_arguments n_arguments;

    typedef typename InitT::n_defaults n_defaults;

    typedef typename InitT::signature signature;



    init_with_call_policies(

        CallPoliciesT const& policies_

        , char const* doc_

        , detail::keyword_range const& keywords

        )

        : base(doc_, keywords)

        , m_policies(policies_)

    {}



    CallPoliciesT const& call_policies() const

    {

        return this->m_policies;

    }

    

 private: // data members

    CallPoliciesT m_policies;

};



//

// drop1<S> is the initial length(S) elements of S

//

namespace detail

{

  template <class S>

  struct drop1

    : mpl::iterator_range<

          typename mpl::begin<S>::type

        , typename mpl::prior<

              typename mpl::end<S>::type

          >::type

      >

  {};

}



template <BOOST_PYTHON_OVERLOAD_TYPES>

class init : public init_base<init<BOOST_PYTHON_OVERLOAD_ARGS> >

{

    typedef init_base<init<BOOST_PYTHON_OVERLOAD_ARGS> > base;

 public:

    typedef init<BOOST_PYTHON_OVERLOAD_ARGS> self_t;



    init(char const* doc_ = 0)

        : base(doc_)

    {

    }



    template <std::size_t N>

    init(char const* doc_, detail::keywords<N> const& kw)

        : base(doc_, kw.range())

    {

        typedef typename detail::error::more_keywords_than_init_arguments<

            N, n_arguments::value + 1

            >::too_many_keywords assertion;

    }



    template <std::size_t N>

    init(detail::keywords<N> const& kw, char const* doc_ = 0)

        : base(doc_, kw.range())

    {

        typedef typename detail::error::more_keywords_than_init_arguments<

            N, n_arguments::value + 1

            >::too_many_keywords assertion;

    }



    template <class CallPoliciesT>

    init_with_call_policies<CallPoliciesT, self_t>

    operator[](CallPoliciesT const& policies) const

    {

        return init_with_call_policies<CallPoliciesT, self_t>(

            policies, this->doc_string(), this->keywords());

    }



    typedef detail::type_list<BOOST_PYTHON_OVERLOAD_ARGS> signature_;



    typedef detail::is_optional<

        typename mpl::eval_if<

            mpl::empty<signature_>

          , mpl::false_

          , mpl::back<signature_>

        >::type

    > back_is_optional;

    

    typedef typename mpl::eval_if<

        back_is_optional

      , mpl::back<signature_>

      , mpl::vector0<>

    >::type optional_args;



    typedef typename mpl::eval_if<

        back_is_optional

      , mpl::if_<

            mpl::empty<optional_args>

          , detail::drop1<signature_>

          , mpl::joint_view<

                detail::drop1<signature_>

              , optional_args

            >

        >

      , signature_

    >::type signature;



    // TODO: static assert to make sure there are no other optional elements



    // Count the number of default args

    typedef mpl::size<optional_args> n_defaults;

    typedef mpl::size<signature> n_arguments;

};



///////////////////////////////////////////////////////////////////////////////

//

//  optional

//

//      optional<T0...TN>::type returns a typelist.

//

///////////////////////////////////////////////////////////////////////////////

template <BOOST_PYTHON_OVERLOAD_TYPES>

struct optional

    : detail::type_list<BOOST_PYTHON_OVERLOAD_ARGS>

{

};



namespace detail

{

  template <class ClassT, class CallPoliciesT, class Signature, class NArgs>

  inline void def_init_aux(

      ClassT& cl

      , Signature const&

      , NArgs

      , CallPoliciesT const& policies

      , char const* doc

      , detail::keyword_range const& keywords_

      )

  {

      cl.def(

          "__init__"

        , detail::make_keyword_range_constructor<Signature,NArgs>(

              policies

            , keywords_

            , (typename ClassT::metadata::holder*)0

          )

        , doc

      );

  }



  ///////////////////////////////////////////////////////////////////////////////

  //

  //  define_class_init_helper<N>::apply

  //

  //      General case

  //

  //      Accepts a class_ and an arguments list. Defines a constructor

  //      for the class given the arguments and recursively calls

  //      define_class_init_helper<N-1>::apply with one fewer argument (the

  //      rightmost argument is shaved off)

  //

  ///////////////////////////////////////////////////////////////////////////////

  template <int NDefaults>

  struct define_class_init_helper

  {



      template <class ClassT, class CallPoliciesT, class Signature, class NArgs>

      static void apply(

          ClassT& cl

          , CallPoliciesT const& policies

          , Signature const& args

          , NArgs

          , char const* doc

          , detail::keyword_range keywords)

      {

          detail::def_init_aux(cl, args, NArgs(), policies, doc, keywords);



          if (keywords.second > keywords.first)

              --keywords.second;



          typedef typename mpl::prior<NArgs>::type next_nargs;

          define_class_init_helper<NDefaults-1>::apply(

              cl, policies, Signature(), next_nargs(), doc, keywords);

      }

  };



  ///////////////////////////////////////////////////////////////////////////////

  //

  //  define_class_init_helper<0>::apply

  //

  //      Terminal case

  //

  //      Accepts a class_ and an arguments list. Defines a constructor

  //      for the class given the arguments.

  //

  ///////////////////////////////////////////////////////////////////////////////

  template <>

  struct define_class_init_helper<0> {



      template <class ClassT, class CallPoliciesT, class Signature, class NArgs>

      static void apply(

          ClassT& cl

        , CallPoliciesT const& policies

        , Signature const& args

        , NArgs

        , char const* doc

        , detail::keyword_range const& keywords)

      {

          detail::def_init_aux(cl, args, NArgs(), policies, doc, keywords);

      }

  };

}



}} // namespace boost::python



#undef BOOST_PYTHON_OVERLOAD_TYPES_WITH_DEFAULT

#undef BOOST_PYTHON_OVERLOAD_TYPES

#undef BOOST_PYTHON_OVERLOAD_ARGS

#undef BOOST_PYTHON_IS_OPTIONAL_VALUE

#undef BOOST_PYTHON_APPEND_TO_INIT



///////////////////////////////////////////////////////////////////////////////

#endif // INIT_JDG20020820_HPP

















