
#ifndef PLS_TASK_MANAGER_H_
#define PLS_TASK_MANAGER_H_

#include <memory>
#include <utility>
#include <array>

#include "pls/internal/scheduling/task.h"
#include "pls/internal/scheduling/external_trading_deque.h"

#include "pls/internal/data_structures/aligned_stack.h"

namespace pls::internal::scheduling {

class stack_allocator {
 public:
  virtual char *allocate_stack(size_t size) = 0;
  virtual void free_stack(size_t size, char *stack) = 0;
};

class heap_stack_allocator : public stack_allocator {
 public:
  char *allocate_stack(size_t size) override { return new char[size]; }
  void free_stack(size_t, char *stack) override { delete[] stack; }
};

/**
 * Handles management of tasks in the system. Each thread has a local task manager,
 * responsible for allocating, freeing and publishing tasks for stealing.
 */
class task_manager {
 public:
  explicit task_manager(unsigned thread_id,
                        size_t num_tasks,
                        size_t stack_size,
                        stack_allocator &stack_allocator);
  ~task_manager();

  void push_resource_on_task(task *target_task, task *spare_task_chain);
  task *pop_resource_from_task(task *target_task);

  task &get_this_thread_task(size_t depth) {
    return *tasks_[depth];
  }

  task &get_active_task() {
    return *active_task_;
  }
  void set_active_task(task *active_task) {
    active_task_ = active_task;
  }

  template<typename F>
  void spawn_child(F &&lambda);
  void sync();

  task *steal_task(task_manager &stealing_task_manager);

  bool try_clean_return(context_switcher::continuation &result_cont);

  bool check_task_chain_forward(task *start_task);
  bool check_task_chain_backward(task *start_task);
  bool check_task_chain();

 private:
  size_t num_tasks_;

  stack_allocator &stack_allocator_;
  std::vector<std::unique_ptr<task>> tasks_;
  task *active_task_;

  external_trading_deque deque_;
};

}
#include "task_manager_impl.h"

#endif //PLS_TASK_MANAGER_H_
