
#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 {
namespace internal {
namespace scheduling {

/**
 * 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(task *tasks,
                        data_structures::aligned_stack static_stack_space,
                        size_t num_tasks,
                        size_t stack_size,
                        external_trading_deque &deque);

  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 &this_thread_tasks_[depth];
  }

  void set_thread_id(unsigned id) {
    for (size_t i = 0; i < num_tasks_; i++) {
      this_thread_tasks_[i].thread_id_ = id;
    }
    deque_.set_thread_id(id);
  }

  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();

  bool 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_;

  task *this_thread_tasks_;
  task *active_task_;

  external_trading_deque &deque_;
};

template<size_t NUM_TASKS, size_t STACK_SIZE>
class static_task_manager {
 public:
  static_task_manager()
      : tasks_{},
        static_stack_storage_{},
        static_external_trading_deque_{},
        task_manager_{tasks_.data(), static_stack_storage_.get_stack(), NUM_TASKS, STACK_SIZE,
                      static_external_trading_deque_.get_deque()} {};
  task_manager &get_task_manager() { return task_manager_; }

 private:
  std::array<task, NUM_TASKS> tasks_;
  data_structures::static_aligned_stack<NUM_TASKS * STACK_SIZE> static_stack_storage_;
  static_external_trading_deque<NUM_TASKS> static_external_trading_deque_;

  task_manager task_manager_;
};

}
}
}
#include "task_manager_impl.h"

#endif //PLS_TASK_MANAGER_H_
