// dzn-runtime -- Dezyne runtime library
//
// Copyright © 2016, 2017, 2019, 2020, 2023 Janneke Nieuwenhuizen <janneke@gnu.org>
// Copyright © 2016 Henk Katerberg <hank@mudball.nl>
// Copyright © 2016-2019, 2021-2025 Rutger (regtur) van Beusekom <rutger@dezyne.org>
//
// This file is part of dzn-runtime.
//
// All rights reserved.
//
//
// Commentary:
//
// Code:

#ifndef DZN_PUMP_HH
#define DZN_PUMP_HH

#include <dzn/config.hh>
#include <dzn/coroutine.hh>
#include <dzn/meta.hh>
#include <dzn/runtime.hh>

#include <condition_variable>
#include <functional>
#include <future>
#include <list>
#include <map>
#include <mutex>
#include <queue>
#include <set>
#include <thread>
#include <vector>

namespace dzn
{
extern std::ostream debug;

struct runtime;

struct pump
{
  std::vector<void *> unblocked;
  bool running;
  bool paused;
  std::function<void ()> worker;
  std::list<coroutine> coroutines;
  std::list<coroutine> collateral_blocked;
  size_t current_coroutine;
  std::queue<std::function<void ()>> queue;
  std::vector<std::pair<std::function<bool ()>, std::function<void (size_t)>>> deferred;

  struct deadline
  {
    size_t id;
    std::chrono::steady_clock::time_point time;
    deadline (size_t identifier, size_t ms);
    bool expired (std::chrono::steady_clock::time_point const& now) const
    {return time <= now;}
    bool operator < (deadline const& that) const
    {return this->time < that.time || (this->time == that.time && this->id < that.id);}
  };

  std::map<deadline, std::function<void ()>> timers;
  std::vector<std::function<void ()>> switch_context;
  std::function<void ()> exit;
  std::thread::id thread_id;
  std::condition_variable condition;
  std::condition_variable idle;
  std::mutex mutex;
  std::future<void> task;
  pump ();
  ~pump ();
  size_t coroutine_id ();
  void stop ();
  void wait ();
  void pause ();
  void resume ();
  void flush ();
  void operator () ();

  bool port_blocked_p (void *);
  bool blocked_p (runtime&, dzn::component *);
  void block (runtime &, dzn::component *, void *);
  void collateral_block (runtime &, dzn::component *);
  void collateral_release (std::list<coroutine>::iterator);
  bool collateral_release_skip_block (dzn::component *);
  void create_context ();
  void context_switch ();
  void release (runtime &, dzn::component *, void *);
  void operator () (std::function<void ()> const&);
  void operator () (std::function<void ()> &&);
  void defer (std::function<bool ()> &&, std::function<void (size_t)> &&);
  void prune_deferred ();
  void handle (size_t, size_t, std::function<void ()> const&);
  void remove (size_t);
private:
  bool timers_expired (std::chrono::steady_clock::time_point const& now) const;
};

#if __cplusplus > 201402L
template <typename L, typename ... Args, typename = typename std::enable_if<std::is_void<typename std::invoke_result<L, Args ...>::type>::value>::type>
#else
template <typename L, typename ... Args, typename = typename std::enable_if<std::is_void<typename std::result_of<L (Args ...)>::type>::value>::type>
#endif
void shell (dzn::pump &pump, L && l, Args && ...args)
{
  std::promise<void> p;
  pump ([&] {l (std::forward<Args> (args)...); p.set_value ();});
  return p.get_future ().get ();
}
#if __cplusplus > 201402L
template < typename L, typename ... Args, typename = typename std::enable_if < !std::is_void<typename std::invoke_result<L,Args ...>::type>::value >::type >
#else
template < typename L, typename ... Args, typename = typename std::enable_if < !std::is_void<typename std::result_of<L (Args ...)>::type>::value >::type >
#endif
auto shell (dzn::pump &pump, L && l, Args && ...args) -> typename dzn::unwrap_data<decltype (l (std::forward<Args> (args)...))>::type
{
  typedef decltype (l (std::forward<Args> (args)...)) reply_t;
  std::promise<reply_t> p;
  pump ([&] {p.set_value (l (std::forward<Args> (args)...));});
  return static_cast<typename dzn::unwrap_data<reply_t>::type> (p.get_future ().get ());
}
}

#endif //DZN_PUMP_HH
//version: 2.19.2
