//
// Copyright (c) 2012-2016 Krzysztof Jusiak (krzysztof at jusiak dot net)
//
// 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 BOOST_DI_CORE_POLICY_HPP
#define BOOST_DI_CORE_POLICY_HPP

#include "boost/di/aux_/compiler.hpp"
#include "boost/di/aux_/utility.hpp"
#include "boost/di/aux_/type_traits.hpp"
#include "boost/di/core/binder.hpp"
#include "boost/di/core/pool.hpp"

namespace core {

template <class...>
struct arg_wrapper;

template <class T, class TName, class TIsRoot, template <class...> class TList, class... TCtor, class TDependency, class TDeps>
struct arg_wrapper<T, TName, TIsRoot, TList<TCtor...>, TDependency, TDeps> {
  using type BOOST_DI_UNUSED = T;
  using expected BOOST_DI_UNUSED = typename TDependency::expected;
  using given BOOST_DI_UNUSED = typename TDependency::given;
  using name BOOST_DI_UNUSED = TName;
  using arity BOOST_DI_UNUSED = aux::integral_constant<int, sizeof...(TCtor)>;
  using scope BOOST_DI_UNUSED = typename TDependency::scope;
  using is_root BOOST_DI_UNUSED = TIsRoot;

  template <class T_, class TName_, class TDefault_>
  using resolve = decltype(core::binder::resolve<T_, TName_, TDefault_>((TDeps*)0));
};

template <class T>
struct allow_void : T {};

template <>
struct allow_void<void> : aux::true_type {};

class policy {
  template <class TArg, class TPolicy, class TPolicies>
  static void call_impl(const TPolicies& policies) noexcept {
    static_cast<const TPolicy&>(policies)(TArg{});
  }

  template <class TArg, class TPolicy>
  struct try_call_impl : allow_void<decltype((aux::declval<TPolicy>())(aux::declval<TArg>()))> {};

 public:
  template <class, class>
  struct try_call;

  template <class TArg, class... TPolicies>
  struct try_call<TArg, pool_t<TPolicies...>> : aux::is_same<aux::bool_list<aux::always<TPolicies>::value...>,
                                                             aux::bool_list<try_call_impl<TArg, TPolicies>::value...>> {};

  template <class TArg, class... TPolicies>
  static void call(BOOST_DI_UNUSED const pool_t<TPolicies...>& policies) noexcept {
    int _[]{0, (call_impl<TArg, TPolicies>(policies), 0)...};
    (void)_;
  }
};

}  // core

#endif
