
#ifndef PLS_SCHEDULER_IMPL_H
#define PLS_SCHEDULER_IMPL_H

namespace pls {
namespace internal {
namespace scheduling {

template<typename Function>
void scheduler::perform_work(Function work_section) {
  PROFILE_WORK_BLOCK("scheduler::perform_work")
  root_task<Function> master{work_section};

  // Push root task on stacks
  auto new_master = memory_->task_stack_for(0)->push(master);
  memory_->thread_state_for(0)->root_task_ = new_master;
  memory_->thread_state_for(0)->current_task_ = new_master;
  for (unsigned int i = 1; i < num_threads_; i++) {
    root_worker_task<Function> worker{new_master};
    auto new_worker = memory_->task_stack_for(0)->push(worker);
    memory_->thread_state_for(i)->root_task_ = new_worker;
    memory_->thread_state_for(i)->current_task_ = new_worker;
  }

  // Perform and wait for work
  sync_barrier_.wait(); // Trigger threads to wake up
  sync_barrier_.wait(); // Wait for threads to finish

  // Clean up stack
  memory_->task_stack_for(0)->pop<typeof(master)>();
  for (unsigned int i = 1; i < num_threads_; i++) {
    root_worker_task<Function> worker{new_master};
    memory_->task_stack_for(0)->pop<typeof(worker)>();
  }
}

template<typename Task>
void scheduler::execute_task(Task &task, int depth) {
  static_assert(std::is_base_of<abstract_task, Task>::value, "Only pass abstract_task subclasses!");

  auto my_state = base::this_thread::state<thread_state>();
  abstract_task *old_task;
  abstract_task *new_task;

  // Init Task
  old_task = my_state->current_task_;
  new_task = my_state->task_stack_->push(task);

  new_task->set_depth(depth >= 0 ? depth : old_task->depth() + 1);

  {
    my_state->lock_.writer_lock();
    my_state->current_task_ = new_task;
    old_task->set_child(new_task);
    my_state->lock_.writer_unlock();
  }

  // Run Task
  new_task->execute();

  // Teardown state back to before the task was executed
  my_state->task_stack_->pop<Task>();

  {
    my_state->lock_.writer_lock();
    old_task->set_child(nullptr);
    my_state->current_task_ = old_task;
    my_state->lock_.writer_unlock();
  }
}

}
}
}

#endif //PLS_SCHEDULER_IMPL_H
