///////////////////////////////////////////////////////////////////////////////
//
// Copyright 2024 Pixar
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://openusd.org/license.
//
// 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 PXR_EXTERNAL_BOOST_PYTHON_DETAIL_DEFAULTS_GEN_HPP
#define PXR_EXTERNAL_BOOST_PYTHON_DETAIL_DEFAULTS_GEN_HPP

#include "pxr/pxr.h"
#include "pxr/external/boost/python/common.hpp"

#include "pxr/external/boost/python/detail/preprocessor.hpp"
#include "pxr/external/boost/python/detail/mpl2/at.hpp"
#include "pxr/external/boost/python/detail/mpl2/size.hpp"
#include <cstddef>

namespace PXR_BOOST_NAMESPACE { namespace python {

namespace detail
{
  // overloads_base is used as a base class for all function
  // stubs. This class holds the doc_string of the stubs.
  struct overloads_base
  {
      overloads_base(char const* doc_)
          : m_doc(doc_) {}

      overloads_base(char const* doc_, detail::keyword_range const& kw)
          : m_doc(doc_), m_keywords(kw) {}

      char const* doc_string() const
      {
          return m_doc;
      }

      detail::keyword_range const& keywords() const
      {
          return m_keywords;
      }

   private:
      char const* m_doc;
      detail::keyword_range m_keywords;
  };

  // overloads_proxy is generated by the overloads_common operator[] (see
  // below). This class holds a user defined call policies of the stubs.
  template <class CallPoliciesT, class OverloadsT>
  struct overloads_proxy
      : public overloads_base
  {
      typedef typename OverloadsT::non_void_return_type   non_void_return_type;
      typedef typename OverloadsT::void_return_type       void_return_type;

      overloads_proxy(
          CallPoliciesT const& policies_
          , char const* doc
          , keyword_range const& kw
          )
          : overloads_base(doc, kw)
            , policies(policies_)
      {}

      CallPoliciesT
      call_policies() const
      {
          return policies;
      }

      CallPoliciesT policies;
  };

  // overloads_common is our default function stubs base class. This
  // class returns the default_call_policies in its call_policies()
  // member function.  It can generate a overloads_proxy however through
  // its operator[]
  template <class DerivedT>
  struct overloads_common
      : public overloads_base
  {
      overloads_common(char const* doc)
          : overloads_base(doc) {}

      overloads_common(char const* doc, keyword_range const& kw)
          : overloads_base(doc, kw) {}

      default_call_policies
      call_policies() const
      {
          return default_call_policies();
      }

      template <class CallPoliciesT>
      overloads_proxy<CallPoliciesT, DerivedT>
      operator[](CallPoliciesT const& policies) const
      {
          return overloads_proxy<CallPoliciesT, DerivedT>(
              policies, this->doc_string(), this->keywords());
      }
  };

}}} // namespace PXR_BOOST_NAMESPACE::python::detail


#define PXR_BOOST_PYTHON_GEN_FUNCTION(fname, fstubs_name, n_args, n_dflts, ret)     \
struct fstubs_name                                                      \
{                                                                       \
    static constexpr int n_funcs = n_dflts + 1;                         \
    static constexpr int n_req = n_args - n_dflts;                      \
    static constexpr int max_args = n_funcs;                            \
                                                                        \
    template <typename SigT>                                            \
    struct gen                                                          \
    {                                                                   \
        template <class Idxs> struct get_args;                          \
        template <size_t I0, size_t... I>                               \
        struct get_args<std::index_sequence<I0, I...>>                  \
        {                                                               \
            using type = std::tuple<                                    \
                typename ::PXR_BOOST_NAMESPACE::python::detail::mpl2::at_c<SigT, I>::type...           \
            >;                                                          \
        };                                                              \
                                                                        \
        using RetT = typename ::PXR_BOOST_NAMESPACE::python::detail::mpl2::at_c<SigT, 0>::type;        \
        using ArgT = typename get_args<                                 \
            std::make_index_sequence<PXR_BOOST_NAMESPACE::python::detail::mpl2::size<SigT>::value>   \
        >::type;                                                        \
                                                                        \
        template <class Idxs> struct func_with_arity;                   \
        template <size_t... I>                                          \
        struct func_with_arity<std::index_sequence<I...>>               \
        {                                                               \
            /* XXX: Infinite recursion if name of this function and */  \
            /* fname are the same */                                    \
            static RetT theFn(                                          \
                typename std::tuple_element<I, ArgT>::type... t)        \
            { return fname(t...); }                                     \
        };                                                              \
                                                                        \
        template <size_t N>                                             \
        struct func :                                                   \
            func_with_arity<std::make_index_sequence<N+n_req>>          \
        {                                                               \
        };                                                              \
    };                                                                  \
};

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

#define PXR_BOOST_PYTHON_GEN_MEM_FUNCTION(fname, fstubs_name, n_args, n_dflts, ret) \
struct fstubs_name                                                      \
{                                                                       \
    static constexpr int n_funcs = n_dflts + 1;                         \
    static constexpr int n_req = n_args - n_dflts;                      \
    static constexpr int max_args = n_funcs + 1;                        \
                                                                        \
    template <typename SigT>                                            \
    struct gen                                                          \
    {                                                                   \
        template <class Idxs> struct get_args;                          \
        template <size_t I0, size_t I1, size_t... I>                    \
        struct get_args<std::index_sequence<I0, I1, I...>>              \
        {                                                               \
            using type = std::tuple<                                    \
                typename ::PXR_BOOST_NAMESPACE::python::detail::mpl2::at_c<SigT, I>::type...           \
            >;                                                          \
        };                                                              \
                                                                        \
        using RetT = typename ::PXR_BOOST_NAMESPACE::python::detail::mpl2::at_c<SigT, 0>::type;        \
        using ClassT = typename ::PXR_BOOST_NAMESPACE::python::detail::mpl2::at_c<SigT, 1>::type;      \
        using ArgT = typename get_args<                                 \
            std::make_index_sequence<PXR_BOOST_NAMESPACE::python::detail::mpl2::size<SigT>::value>   \
        >::type;                                                        \
                                                                        \
        template <class Idxs> struct func_with_arity;                   \
        template <size_t... I>                                          \
        struct func_with_arity<std::index_sequence<I...>>               \
        {                                                               \
            static RetT theFn(                                          \
                ClassT obj, typename std::tuple_element<I, ArgT>::type... t) \
            { return obj.fname(t...); }                                 \
        };                                                              \
                                                                        \
        template <size_t N>                                             \
        struct func :                                                   \
            func_with_arity<std::make_index_sequence<N+n_req>>          \
        {                                                               \
        };                                                              \
    };                                                                  \
};

#define PXR_BOOST_PYTHON_OVERLOAD_CONSTRUCTORS(fstubs_name, n_args, n_dflts)                    \
    fstubs_name(char const* doc = 0)                                                        \
        : ::PXR_BOOST_NAMESPACE::python::detail::overloads_common<fstubs_name>(doc) {}                    \
    template <std::size_t N>                                                                \
    fstubs_name(char const* doc, ::PXR_BOOST_NAMESPACE::python::detail::keywords<N> const& keywords)      \
        : ::PXR_BOOST_NAMESPACE::python::detail::overloads_common<fstubs_name>(                           \
            doc, keywords.range())                                                          \
    {                                                                                       \
        [[maybe_unused]] typedef typename ::PXR_BOOST_NAMESPACE::python::detail::           \
            error::more_keywords_than_function_arguments<                                   \
                N,n_args>::too_many_keywords assertion;                                     \
    }                                                                                       \
    template <std::size_t N>                                                                \
    fstubs_name(::PXR_BOOST_NAMESPACE::python::detail::keywords<N> const& keywords, char const* doc = 0)  \
        : ::PXR_BOOST_NAMESPACE::python::detail::overloads_common<fstubs_name>(                           \
            doc, keywords.range())                                                          \
    {                                                                                       \
        [[maybe_unused]] typedef typename ::PXR_BOOST_NAMESPACE::python::detail::           \
            error::more_keywords_than_function_arguments<                                   \
                N,n_args>::too_many_keywords assertion;                                     \
    }

#  define PXR_BOOST_PYTHON_GEN_FUNCTION_STUB(fname, fstubs_name, n_args, n_dflts)   \
    struct fstubs_name                                                          \
        : public ::PXR_BOOST_NAMESPACE::python::detail::overloads_common<fstubs_name>         \
    {                                                                           \
        PXR_BOOST_PYTHON_GEN_FUNCTION(                                              \
            fname, non_void_return_type, n_args, n_dflts, return)               \
                                                                                \
        typedef non_void_return_type void_return_type;                          \
        PXR_BOOST_PYTHON_OVERLOAD_CONSTRUCTORS(fstubs_name, n_args, n_dflts)        \
    };


#  define PXR_BOOST_PYTHON_GEN_MEM_FUNCTION_STUB(fname, fstubs_name, n_args, n_dflts)       \
    struct fstubs_name                                                                  \
        : public ::PXR_BOOST_NAMESPACE::python::detail::overloads_common<fstubs_name>                 \
    {                                                                                   \
        PXR_BOOST_PYTHON_GEN_MEM_FUNCTION(                                                  \
            fname, non_void_return_type, n_args, n_dflts, return)                       \
                                                                                        \
        typedef non_void_return_type void_return_type;                                  \
        PXR_BOOST_PYTHON_OVERLOAD_CONSTRUCTORS(fstubs_name, n_args + 1, n_dflts)            \
    };

///////////////////////////////////////////////////////////////////////////////
//
//  MAIN MACROS
//
//      Given generator_name, fname, min_args and max_args, These macros
//      generate function stubs that forward to a function or member function
//      named fname. max_args is the arity of the function or member function
//      fname. fname can have default arguments. min_args is the minimum
//      arity that fname can accept.
//
//      There are two versions:
//
//          1. PXR_BOOST_PYTHON_FUNCTION_OVERLOADS for free functions
//          2. PXR_BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS for member functions.
//
///////////////////////////////////////////////////////////////////////////////
#define PXR_BOOST_PYTHON_FUNCTION_OVERLOADS(generator_name, fname, min_args, max_args)          \
    PXR_BOOST_PYTHON_GEN_FUNCTION_STUB(                                                         \
        fname,                                                                              \
        generator_name,                                                                     \
        max_args,                                                                           \
        (max_args - min_args))

#define PXR_BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(generator_name, fname, min_args, max_args)   \
    PXR_BOOST_PYTHON_GEN_MEM_FUNCTION_STUB(                                                     \
        fname,                                                                              \
        generator_name,                                                                     \
        max_args,                                                                           \
        (max_args - min_args))

// deprecated macro names (to be removed)
#define PXR_BOOST_PYTHON_FUNCTION_GENERATOR PXR_BOOST_PYTHON_FUNCTION_OVERLOADS
#define PXR_BOOST_PYTHON_MEM_FUN_GENERATOR PXR_BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS

///////////////////////////////////////////////////////////////////////////////
#endif // PXR_EXTERNAL_BOOST_PYTHON_DETAIL_DEFAULTS_GEN_HPP


