
#ifndef PLS_SCHEDULER_H
#define PLS_SCHEDULER_H

#include <array>
#include <iostream>

#include "pls/internal/helpers/profiler.h"

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

#include "pls/internal/base/thread.h"
#include "pls/internal/base/barrier.h"

#include "thread_state.h"
#include "root_task.h"
#include "scheduler_memory.h"

namespace pls {
namespace internal {
namespace scheduling {

void worker_routine();
using scheduler_thread = base::thread<decltype(&worker_routine), thread_state>;

class scheduler {
  friend void worker_routine();

  const unsigned int num_threads_;
  scheduler_memory *memory_;

  base::barrier sync_barrier_;
  bool terminated_;
 public:
  explicit scheduler(scheduler_memory *memory, unsigned int num_threads);
  ~scheduler();

  /**
   * Wakes up the thread pool.
   * Code inside the Function lambda can invoke all parallel APIs.
   *
   * @param work_section generic function or lambda to be executed in the scheduler's context.
   */
  template<typename Function>
  void perform_work(Function work_section);

  /**
   * Executes a top-level-task (children of abstract_task) on this thread.
   *
   * @param task The task to be executed.
   * @param depth Optional: depth of the new task, otherwise set implicitly.
   */
  template<typename Task>
  static void execute_task(Task &task, int depth = -1);

  static abstract_task *current_task() { return base::this_thread::state<thread_state>()->current_task_; }

  void terminate(bool wait_for_workers = true);

  unsigned int num_threads() const { return num_threads_; }
  thread_state *thread_state_for(size_t id) { return memory_->thread_state_for(id); }
};

}
}
}
#include "scheduler_impl.h"

#endif //PLS_SCHEDULER_H
