
#ifndef PLS_LOCK_FREE_TASK_MANAGER_H_
#define PLS_LOCK_FREE_TASK_MANAGER_H_

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

#include "pls/internal/base/stack_allocator.h"

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

namespace pls::internal::scheduling {
struct thread_state;
}

namespace pls::internal::scheduling::lock_free {

/**
 * Handles management of tasks in the system. Each thread has a local task manager,
 * responsible for allocating, freeing and publishing tasks for stealing.
 *
 * The task manager handles steal/sync details, the scheduler handles all other aspects.
 * By doing this the task manager can be exchanged for a different implementation for testing purposes.
 */
class task_manager {
  using stack_allocator = pls::internal::base::stack_allocator;

 public:
  explicit task_manager(unsigned thread_id,
                        size_t num_tasks,
                        size_t stack_size,
                        std::shared_ptr<stack_allocator> &stack_allocator);
  ~task_manager();

  task *get_task(size_t index) { return tasks_[index].get(); }

  // Local scheduling
  void push_local_task(base_task *pushed_task);
  base_task *pop_local_task();

  // Stealing work, automatically trades in another task
  // Return: stolen_task, traded_task, cas_success
  std::tuple<base_task *, base_task *, bool> steal_task(thread_state &stealing_state);

  // Sync/memory management
  base_task *pop_clean_task_chain(base_task *task);
 private:
  std::shared_ptr<stack_allocator> stack_allocator_;
  std::vector<std::unique_ptr<task>> tasks_;

  external_trading_deque deque_;
};
}

#endif //PLS_LOCK_FREE_TASK_MANAGER_H_
