// Copyright Daniel Wallin 2006. Use, modification and distribution is

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



#ifndef BOOST_PARAMETER_PYTHON_060209_HPP

# define BOOST_PARAMETER_PYTHON_060209_HPP



# include <boost/mpl/vector.hpp>

# include <boost/mpl/fold.hpp>

# include <boost/mpl/prior.hpp>

# include <boost/mpl/shift_right.hpp>

# include <boost/mpl/shift_left.hpp>

# include <boost/mpl/bitand.hpp>

# include <boost/mpl/pair.hpp>

# include <boost/mpl/size.hpp>

# include <boost/mpl/push_back.hpp>

# include <boost/mpl/or.hpp>

# include <boost/mpl/count_if.hpp>

# include <boost/mpl/transform.hpp>

# include <boost/mpl/front.hpp>

# include <boost/mpl/iterator_range.hpp>

# include <boost/mpl/next.hpp>

# include <boost/mpl/begin_end.hpp>

# include <boost/mpl/not.hpp>

# include <boost/mpl/empty.hpp>

# include <boost/python/def.hpp>

# include <boost/python/make_constructor.hpp>

# include <boost/python/init.hpp>

# include <boost/python/to_python_converter.hpp>

# include <boost/parameter/aux_/maybe.hpp>

# include <boost/parameter/aux_/python/invoker.hpp>



namespace boost { namespace parameter { namespace python 

{

  namespace python_ = boost::python;

}}}



namespace boost { namespace parameter { namespace python { namespace aux 

{



  inline PyObject* unspecified_type()

  {

      static PyTypeObject unspecified = {

          PyObject_HEAD_INIT(NULL)

          0,                                /* ob_size        */

          "Boost.Parameter.Unspecified",    /* tp_name        */

          PyType_Type.tp_basicsize,         /* tp_basicsize   */

          0,                                /* tp_itemsize    */

          0,                                /* tp_dealloc     */

          0,                                /* tp_print       */

          0,                                /* tp_getattr     */

          0,                                /* tp_setattr     */

          0,                                /* tp_compare     */

          0,                                /* tp_repr        */

          0,                                /* tp_as_number   */

          0,                                /* tp_as_sequence */

          0,                                /* tp_as_mapping  */

          0,                                /* tp_hash        */

          0,                                /* tp_call        */

          0,                                /* tp_str         */

          0,                                /* tp_getattro    */

          0,                                /* tp_setattro    */

          0,                                /* tp_as_buffer   */

          Py_TPFLAGS_DEFAULT,               /* tp_flags       */

          0,                                /* tp_doc         */

      };



      if (unspecified.ob_type == 0)

      {

          unspecified.ob_type = &PyType_Type;

          PyType_Ready(&unspecified);

      }



      return (PyObject*)&unspecified;

  }



  struct empty_tag {};



  struct empty_tag_to_python

  {

      static PyObject* convert(empty_tag)

      {

          return python_::xincref(unspecified_type());

      }

  };



}}}} // namespace boost::parameter::python::aux



namespace boost { namespace python 

{



  // Converts a Python value to a maybe<T>

  template <class T>

  struct arg_from_python<parameter::aux::maybe<T> >

    : arg_from_python<T>

  {

      arg_from_python(PyObject* p)

        : arg_from_python<T>(p)

        , empty(parameter::python::aux::unspecified_type() == p)

      {}



      bool convertible() const

      {

          return empty || arg_from_python<T>::convertible();

      }



      parameter::aux::maybe<T> operator()()

      {

          if (empty)

          {

              return parameter::aux::maybe<T>();

          }

          else

          {

              return parameter::aux::maybe<T>(

                  arg_from_python<T>::operator()()

              );

          }

      }



      bool empty;

  };



}} // namespace boost::python



namespace boost { namespace parameter { namespace python {



namespace aux

{



  template <class K>

  struct is_optional

    : mpl::not_<

          mpl::or_<typename K::required, typename K::optimized_default>

      >

  {};



  template <class K, class Required, class Optimized, class T>

  struct arg_spec

  {

      typedef K keyword;

      typedef Required required;

      typedef T type;

      typedef Optimized optimized_default;

  };

  

  template <class K, class T, class Optimized = mpl::false_>

  struct make_arg_spec_impl

  {

      typedef arg_spec<

          typename K::first, typename K::second, Optimized, T

      > type;

  };



  template <class K, class T>

  struct make_arg_spec_impl<K, T, typename K::third>

  {

      typedef arg_spec<

          typename K::first, typename K::second, typename K::third, T

      > type;

  };



  template <class K, class T>

  struct make_arg_spec

    : make_arg_spec_impl<K, T>

  {

  };



  template <class Spec, class State>

  struct combinations_op

  {

      typedef typename State::second bits;

      typedef typename State::first result0;



      typedef typename mpl::if_<

          mpl::or_<

              typename Spec::required

            , typename Spec::optimized_default

            , mpl::bitand_<bits, mpl::long_<1> >

          >

        , typename mpl::push_back<result0, Spec>::type

        , result0

      >::type result;



      typedef typename mpl::if_<

          mpl::or_<

              typename Spec::required

            , typename Spec::optimized_default

          >

        , bits

        , typename mpl::shift_right<bits, mpl::long_<1> >::type

      >::type next_bits;



      typedef mpl::pair<

          result

        , next_bits

      > type;

  };



  // Used as start value in the recursive arg() composition below.

  struct no_keywords

  {

      template <class T>

      T const& operator,(T const& x) const

      {

          return x;

      }

  };



  template <class Def, class F, class Iter, class End, class Keywords>

  void def_combination_aux0(

      Def def, F f, Iter, End, Keywords const& keywords, mpl::false_)

  {

      typedef typename mpl::deref<Iter>::type spec;

      typedef typename spec::keyword kw;



      def_combination_aux(

          def, f, typename mpl::next<Iter>::type(), End()

        , (

              keywords, boost::python::arg(kw::keyword_name())

          )

      );

  }



  template <class Def, class F, class Iter, class End, class Keywords>

  void def_combination_aux0(

      Def def, F f, Iter, End, Keywords const& keywords, mpl::true_)

  {

      typedef typename mpl::deref<Iter>::type spec;

      typedef typename spec::keyword kw;



      def_combination_aux(

          def, f, typename mpl::next<Iter>::type(), End()

        , (

              keywords, boost::python::arg(kw::keyword_name()) = empty_tag()

          )

      );

  }



  inline void initialize_converter()

  {

      static python_::to_python_converter<empty_tag, empty_tag_to_python> x;

  }



  template <class Def, class F, class Iter, class End, class Keywords>

  void def_combination_aux(

      Def def, F f, Iter, End, Keywords const& keywords)

  {

      typedef typename mpl::deref<Iter>::type spec;



      typedef typename mpl::and_<

          typename spec::optimized_default

        , mpl::not_<typename spec::required>

      >::type optimized_default;

      

      def_combination_aux0(

          def, f, Iter(), End(), keywords, optimized_default()

      );

  }



  template <class Def, class F, class End, class Keywords>

  void def_combination_aux(

      Def def, F f, End, End, Keywords const& keywords)

  {

      def(f, keywords);

  } 



  template <class Def, class F, class End>

  void def_combination_aux(

      Def def, F f, End, End, no_keywords const&)

  {

      def(f);

  }



  template <

      class Def, class Specs, class Bits, class Invoker

  >

  void def_combination(

      Def def, Specs*, Bits, Invoker*)

  {

      typedef typename mpl::fold<

          Specs

        , mpl::pair<mpl::vector0<>, Bits>

        , combinations_op<mpl::_2, mpl::_1>

      >::type combination0;



      typedef typename combination0::first combination;



      typedef typename mpl::apply_wrap1<

          Invoker, combination

      >::type invoker;



      def_combination_aux(

          def

        , &invoker::execute

        , typename mpl::begin<combination>::type()

        , typename mpl::end<combination>::type()

        , no_keywords()

      );

  }



  template <

      class Def, class Specs, class Bits, class End, class Invoker

  >

  void def_combinations(

      Def def, Specs*, Bits, End, Invoker*)

  {

      initialize_converter();



      def_combination(def, (Specs*)0, Bits(), (Invoker*)0);



      def_combinations(

          def

        , (Specs*)0

        , mpl::long_<Bits::value + 1>()

        , End()

        , (Invoker*)0

      );

  }



  template <

      class Def, class Specs, class End, class Invoker

  >

  void def_combinations(

      Def, Specs*, End, End, Invoker*)

  {}



  struct not_specified {};



  template <class CallPolicies>

  struct call_policies_as_options

  {

      call_policies_as_options(CallPolicies const& call_policies)

        : call_policies(call_policies)

      {}



      CallPolicies const& policies() const

      {

          return call_policies;

      }



      char const* doc() const

      {

          return 0;

      }



      CallPolicies call_policies;

  };



  template <class Class, class Options = not_specified>

  struct def_class

  {

      def_class(Class& cl, char const* name, Options options = Options())

        : cl(cl)

        , name(name)

        , options(options)

      {}



      template <class F>

      void def(F f, not_specified const*) const

      {

          cl.def(name, f);

      }



      template <class F>

      void def(F f, void const*) const

      {

          cl.def(name, f, options.doc(), options.policies());

      }

      

      template <class F>

      void operator()(F f) const

      {

          this->def(f, &options);

      }



      template <class F, class Keywords>

      void def(F f, Keywords const& keywords, not_specified const*) const

      {

          cl.def(name, f, keywords);

      }



      template <class F, class Keywords>

      void def(F f, Keywords const& keywords, void const*) const

      {

          cl.def(name, f, keywords, options.doc(), options.policies());

      }



      template <class F, class Keywords>

      void operator()(F f, Keywords const& keywords) const

      {

          this->def(f, keywords, &options);

      }



      Class& cl;

      char const* name;

      Options options;

  };



  template <class Class, class CallPolicies = boost::python::default_call_policies>

  struct def_init

  {

      def_init(Class& cl, CallPolicies call_policies = CallPolicies())

        : cl(cl)

        , call_policies(call_policies)

      {}



      template <class F>

      void operator()(F f) const

      {

          cl.def(

              "__init__"

            , boost::python::make_constructor(f, call_policies)

          );

      }



      template <class F, class Keywords>

      void operator()(F f, Keywords const& keywords) const

      {

          cl.def(

              "__init__"

            , boost::python::make_constructor(f, call_policies, keywords)

          );

      }



      Class& cl;

      CallPolicies call_policies;

  };



  struct def_function

  {

      def_function(char const* name)

        : name(name)

      {}

      

      template <class F>

      void operator()(F f) const

      {

          boost::python::def(name, f);

      }



      template <class F, class Keywords>

      void operator()(F f, Keywords const& keywords) const

      {

          boost::python::def(name, f, keywords);

      }



      char const* name;

  };



} // namespace aux



template <class M, class Signature>

void def(char const* name, Signature)

{

    typedef mpl::iterator_range<

        typename mpl::next<

            typename mpl::begin<Signature>::type

        >::type

      , typename mpl::end<Signature>::type

    > arg_types;



    typedef typename mpl::transform<

        typename M::keywords

      , arg_types

      , aux::make_arg_spec<mpl::_1, mpl::_2>

      , mpl::back_inserter<mpl::vector0<> >

    >::type arg_specs;



    typedef typename mpl::count_if<

        arg_specs

      , aux::is_optional<mpl::_1>

    >::type optional_arity;

    

    typedef typename mpl::front<Signature>::type result_type;

    typedef typename mpl::shift_left<mpl::long_<1>, optional_arity>::type upper;



    aux::def_combinations(

        aux::def_function(name)

      , (arg_specs*)0

      , mpl::long_<0>()

      , mpl::long_<upper::value>()

      , (aux::make_invoker<M, result_type>*)0

    );

}



template <class M, class Class, class Signature>

void def(Class& cl, char const* name, Signature)

{

    typedef mpl::iterator_range<

        typename mpl::next<

            typename mpl::begin<Signature>::type

        >::type

      , typename mpl::end<Signature>::type

    > arg_types;



    typedef typename mpl::transform<

        typename M::keywords

      , arg_types

      , aux::make_arg_spec<mpl::_1, mpl::_2>

      , mpl::back_inserter<mpl::vector0<> >

    >::type arg_specs;



    typedef typename mpl::count_if<

        arg_specs

      , aux::is_optional<mpl::_1>

    >::type optional_arity;

    

    typedef typename mpl::front<Signature>::type result_type;

    typedef typename mpl::shift_left<mpl::long_<1>, optional_arity>::type upper;



    aux::def_combinations(

        aux::def_class<Class>(cl, name)

      , (arg_specs*)0

      , mpl::long_<0>()

      , mpl::long_<upper::value>()

      , (aux::make_invoker<M, result_type>*)0

    );

}



namespace aux

{



  template <class K>

  struct keyword

  {

      typedef K type;

  };



  template <class K>

  struct keyword<K*>

  {

      typedef K type;

  };



  template <class K>

  struct keyword<K**>

  {

      typedef K type;

  };



  template <class K>

  struct required

  {

      typedef mpl::true_ type;

  };



  template <class K>

  struct required<K*>

  {

      typedef mpl::false_ type;

  };



  template <class K>

  struct optimized

  {

      typedef mpl::true_ type;

  };



  template <class K>

  struct optimized<K**>

  {

      typedef mpl::false_ type;

  };



  template <class T>

  struct make_kw_spec;



  template <class K, class T>

  struct make_kw_spec<K(T)>

  {

      typedef arg_spec<

          typename keyword<K>::type

        , typename required<K>::type

        , typename optimized<K>::type

        , T

      > type;

  };



} // namespace aux



template <class ParameterSpecs, class CallPolicies = boost::python::default_call_policies>

struct init 

  : boost::python::def_visitor<init<ParameterSpecs, CallPolicies> >

{

    init(CallPolicies call_policies = CallPolicies())

      : call_policies(call_policies)

    {}



    template <class CallPolicies1>

    init<ParameterSpecs, CallPolicies1> 

    operator[](CallPolicies1 const& call_policies) const

    {

        return init<ParameterSpecs, CallPolicies1>(call_policies);

    }



    template <class Class>

    void visit_aux(Class& cl, mpl::true_) const

    {

        cl.def(boost::python::init<>()[call_policies]);

    }



    template <class Class>

    void visit_aux(Class& cl, mpl::false_) const

    {

        typedef typename mpl::transform<

            ParameterSpecs

          , aux::make_kw_spec<mpl::_>

          , mpl::back_inserter<mpl::vector0<> >

        >::type arg_specs;



        typedef typename mpl::count_if<

            arg_specs

          , aux::is_optional<mpl::_>

        >::type optional_arity;



        typedef typename mpl::shift_left<mpl::long_<1>, optional_arity>::type upper;



        aux::def_combinations(

            aux::def_init<Class, CallPolicies>(cl, call_policies)

          , (arg_specs*)0

          , mpl::long_<0>()

          , mpl::long_<upper::value>()

          , (aux::make_init_invoker<typename Class::wrapped_type>*)0

        );

    }



    template <class Class>

    void visit(Class& cl) const

    {

        visit_aux(cl, mpl::empty<ParameterSpecs>());

    }



    CallPolicies call_policies;

};



template <class ParameterSpecs, class CallPolicies = boost::python::default_call_policies>

struct call 

  : boost::python::def_visitor<call<ParameterSpecs, CallPolicies> >

{

    call(CallPolicies const& call_policies = CallPolicies())

      : call_policies(call_policies)

    {}



    template <class CallPolicies1>

    call<ParameterSpecs, CallPolicies1>

    operator[](CallPolicies1 const& call_policies) const

    {

        return call<ParameterSpecs, CallPolicies1>(call_policies);

    }



    template <class Class>

    void visit(Class& cl) const

    {

        typedef mpl::iterator_range<

            typename mpl::next<

                typename mpl::begin<ParameterSpecs>::type

            >::type

          , typename mpl::end<ParameterSpecs>::type

        > arg_types;



        typedef typename mpl::front<ParameterSpecs>::type result_type;



        typedef typename mpl::transform<

            arg_types

          , aux::make_kw_spec<mpl::_>

          , mpl::back_inserter<mpl::vector0<> >

        >::type arg_specs;



        typedef typename mpl::count_if<

            arg_specs

          , aux::is_optional<mpl::_>

        >::type optional_arity;



        typedef typename mpl::shift_left<mpl::long_<1>, optional_arity>::type upper;



        typedef aux::call_policies_as_options<CallPolicies> options;



        aux::def_combinations(

            aux::def_class<Class, options>(cl, "__call__", options(call_policies))

          , (arg_specs*)0

          , mpl::long_<0>()

          , mpl::long_<upper::value>()

          , (aux::make_call_invoker<typename Class::wrapped_type, result_type>*)0

        );

    }



    CallPolicies call_policies;

};



template <class Fwd, class ParameterSpecs>

struct function 

  : boost::python::def_visitor<function<Fwd, ParameterSpecs> >

{

    template <class Class, class Options>

    void visit(Class& cl, char const* name, Options const& options) const

    {

        typedef mpl::iterator_range<

            typename mpl::next<

                typename mpl::begin<ParameterSpecs>::type

            >::type

          , typename mpl::end<ParameterSpecs>::type

        > arg_types;



        typedef typename mpl::front<ParameterSpecs>::type result_type;



        typedef typename mpl::transform<

            arg_types

          , aux::make_kw_spec<mpl::_>

          , mpl::back_inserter<mpl::vector0<> >

        >::type arg_specs;



        typedef typename mpl::count_if<

            arg_specs

          , aux::is_optional<mpl::_>

        >::type optional_arity;



        typedef typename mpl::shift_left<mpl::long_<1>, optional_arity>::type upper;



        aux::def_combinations(

            aux::def_class<Class, Options>(cl, name, options)

          , (arg_specs*)0

          , mpl::long_<0>()

          , mpl::long_<upper::value>()

          , (aux::make_member_invoker<

                Fwd, result_type, typename Class::wrapped_type

            >*)0

        );

    }

};



}}} // namespace boost::parameter::python



#endif // BOOST_PARAMETER_PYTHON_060209_HPP



