//
// continuation
// ~~~~~~~~~~~~
// Polymorphic continuation wrapper and generic continuation utility functions.
//
// Copyright (c) 2014 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// 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 EXECUTORS_EXPERIMENTAL_CONTINUATION_HEADER
#define EXECUTORS_EXPERIMENTAL_CONTINUATION_HEADER

#include <experimental/executor>
#include <memory>

namespace std {
namespace experimental {
inline namespace concurrency_v1 {

class __continuation;
template <class> class __continuation_impl_base;
template <class, class...> struct __continuation_chain;
template <class> struct __continuation_handler;

class bad_continuation;

// Polymorphic continuation wrapper.

template <class...> class continuation; // not defined

template <class _R, class... _Args> class continuation<_R(_Args...)>
{
public:
  typedef executor executor_type;

  // construct / copy / destroy:

  continuation(const continuation&) = delete;
  continuation& operator=(const continuation&) = delete;

  continuation() noexcept;
  continuation(nullptr_t) noexcept;
  continuation(continuation&& __c) noexcept;
  continuation(continuation<>&&) = delete;
  template <class _Continuation> continuation(_Continuation __c);
  template <class _Alloc> continuation(allocator_arg_t, const _Alloc&) noexcept;
  template <class _Alloc> continuation(allocator_arg_t, const _Alloc&, nullptr_t) noexcept;
  template <class _Alloc> continuation(allocator_arg_t, const _Alloc&, continuation&& __c) noexcept;
  template <class _Alloc> continuation(allocator_arg_t, const _Alloc&, continuation<>&&) = delete;
  template <class _Continuation, class _Alloc>
    continuation(allocator_arg_t, const _Alloc& __a, _Continuation __c);

  continuation& operator=(continuation&& __c);
  continuation& operator=(nullptr_t);
  template <class _Continuation> continuation& operator=(_Continuation&& __c);

  ~continuation();

  // continuation capacity:

  explicit operator bool() const noexcept;

  // continuation invocation:

  executor_type get_executor() const noexcept;
  void operator()(_Args... __args);

  // continuation target access:

  const type_info& target_type() const noexcept;
  template <class _Continuation> _Continuation* target() noexcept;
  template <class _Continuation> const _Continuation* target() const noexcept;

private:
  friend class continuation<>;
  template <class, class...> friend struct __continuation_chain;
  template <class> friend struct __continuation_handler;
  template <class _Signature>
    friend continuation<_Signature> static_continuation_cast(continuation<>&& __c);
  template <class _Signature>
    friend continuation<_Signature> dynamic_continuation_cast(continuation<>&& __c);
  explicit continuation(__continuation_impl_base<_R(_Args...)>* __i) : _M_impl(__i) {}
  unique_ptr<__continuation_impl_base<_R(_Args...)>> _M_impl;
};

template <class _R, class... _Args, class... _Args2>
  struct continuation_of<continuation<_R(_Args...)>(_Args2...)>;

template <class _R, class... _Args>
  bool operator==(const continuation<_R(_Args...)>& __c, nullptr_t) noexcept;
template <class _R, class... _Args>
  bool operator==(nullptr_t, const continuation<_R(_Args...)>& __c) noexcept;
template <class _R, class... _Args>
  bool operator!=(const continuation<_R(_Args...)>& __c, nullptr_t) noexcept;
template <class _R, class... _Args>
  bool operator!=(nullptr_t, const continuation<_R(_Args...)>& __c) noexcept;

template <> class continuation<>
{
public:
  typedef executor executor_type;

  // construct / copy / destroy:

  continuation(const continuation&) = delete;
  continuation& operator=(const continuation& __c);

  continuation() noexcept;
  continuation(nullptr_t) noexcept;
  continuation(continuation&& __c) noexcept;
  template <class _R, class... _Args> continuation(continuation<_R(_Args...)>&& __c) noexcept;
  template <class _Alloc> continuation(allocator_arg_t, const _Alloc&) noexcept;
  template <class _Alloc> continuation(allocator_arg_t, const _Alloc&, nullptr_t) noexcept;
  template <class _Alloc> continuation(allocator_arg_t, const _Alloc&, continuation&& __c) noexcept;
  template <class _R, class... _Args, class _Alloc>
    continuation(allocator_arg_t, const _Alloc& __a, continuation<_R(_Args...)>&& __c) noexcept;

  continuation& operator=(continuation&& __c);
  continuation& operator=(nullptr_t);
  template <class _R, class... _Args>
    continuation& operator=(continuation<_R(_Args...)>&& __c);

  ~continuation();

  // continuation capacity:

  explicit operator bool() const noexcept;

  // continuation invocation:

  executor_type get_executor() const noexcept;

  // continuation target access:

  const type_info& target_type() const noexcept;
  template <class _Continuation> _Continuation* target() noexcept;
  template <class _Continuation> const _Continuation* target() const noexcept;

private:
  template <class _Signature>
    friend continuation<_Signature> static_continuation_cast(continuation<>&& __c);
  template <class _Signature>
    friend continuation<_Signature> dynamic_continuation_cast(continuation<>&& __c);
  unique_ptr<__continuation> _M_impl;
};

bool operator==(const continuation<>& __c, nullptr_t) noexcept;
bool operator==(nullptr_t, const continuation<>& __c) noexcept;
bool operator!=(const continuation<>& __c, nullptr_t) noexcept;
bool operator!=(nullptr_t, const continuation<>& __c) noexcept;

template <class _R, class... _Args> struct handler_type<continuation<>, _R(_Args...)>;
template <class _Signature> struct continuation_result {};

// continuation casts:

template <class _Signature>
  continuation<_Signature> static_continuation_cast(continuation<>&& __c);
template <class _Signature>
  continuation<_Signature> dynamic_continuation_cast(continuation<>&& __c);

} // inline namespace concurrency_v1
} // namespace experimental

template<class _R, class... _Args, class _Alloc>
  struct uses_allocator<std::experimental::continuation<_R(_Args...)>, _Alloc>
    : true_type {};

template<class _Alloc>
  struct uses_allocator<std::experimental::continuation<>, _Alloc>
    : true_type {};

} // namespace std

#include <experimental/bits/continuation.h>

#endif
