
#ifndef PLS_TASK_H
#define PLS_TASK_H

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

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

#include "pls/internal/scheduling/thread_state.h"

namespace pls {
namespace internal {
namespace scheduling {

class task {
  friend class scheduler;

  // Coordinate finishing of sub_tasks
  std::atomic<unsigned int> ref_count_;
  task *parent_;

  // Stack Management (reset stack pointer after wait_for_all() calls)
  data_structures::work_stealing_deque<task>::state deque_state_;

 protected:
  // TODO: Double Check with copy and move constructors, try to minimize overhead while keeping a clean API.
  explicit task();
  task(const task &other);

  /**
   * Overwrite this with the actual behaviour of concrete tasks.
   */
  virtual void execute_internal() = 0;

  template<typename T>
  void spawn_child(T &&sub_task);
  void wait_for_all();

 private:
  void execute();
};

template<typename T>
void task::spawn_child(T &&sub_task) {
  PROFILE_FORK_JOIN_STEALING("spawn_child")
  static_assert(std::is_base_of<task, typename std::remove_reference<T>::type>::value, "Only pass task subclasses!");

  // Keep our refcount up to date
  ref_count_++;

  // Assign forced values (for stack and parent management)
  sub_task.parent_ = this;
  sub_task.deque_state_ = thread_state::get()->deque_.save_state();

  // Push on our deque
  const T const_task = sub_task;
  thread_state::get()->deque_.push_tail(const_task);
}

}
}
}

#endif //PLS_TASK_H
