#ifndef PLS_BASE_TASK_H
#define PLS_BASE_TASK_H

#include <utility>
#include <atomic>

#include "context_switcher/continuation.h"
#include "context_switcher/context_switcher.h"

#include "pls/internal/profiling/profiler.h"
#include "pls/internal/profiling/dag_node.h"

namespace pls::internal::scheduling {
/**
 * A task is the smallest unit of execution seen by the runtime system.
 *
 * Tasks represent a action dispatched by a potentially parallel call.
 * Tasks have their own execution context (stack and register state), making them stackefull coroutines.
 * Tasks can be suspended and resumed (stealing happens by resuming a task).
 *
 * Being coroutines tasks go through a very deliberate state machine:
 * - initialized (no execution state)
 * - running (currently executing user code)
 * - suspended (suspended by switching to a different task).
 *
 * This base_task can be extended by different trading/stealing implementations,
 * to add for example additional flags. The scheduler itself always works solely with this base version.
 *
 * Currently, only the 'lock_free' stealing implementation is present and extends the task in this package.
 * The scheduler only uses/operates on this base_task, as the staling/resource trading is not its domain.
 */
struct strain_resource;
struct base_task {
  base_task(char *stack_memory, size_t stack_size, unsigned depth, unsigned thread_id) :
      depth_{depth},
      thread_id_{thread_id},
      stack_memory_{stack_memory},
      stack_size_{stack_size},
      is_synchronized_{false},
      is_serial_section_{false},
      prev_{nullptr},
      next_{nullptr},
      attached_resources_{nullptr} {}

  // Do not allow accidental copy/move operations.
  // The whole runtime relies on tasks never changing memory positions during execution.
  // Create tasks ONCE and use them until the runtime is shut down.
  base_task(const base_task &other) = delete;
  base_task(base_task &&other) = delete;
  base_task &operator=(const base_task &other) = delete;
  base_task &operator=(base_task &&other) = delete;

  template<typename F>
  context_switcher::continuation run_as_task(F &&lambda) {
    return context_switcher::enter_context(stack_memory_, stack_size_, std::forward<F>(lambda));
  }

  // General task information
  const unsigned depth_;
  const unsigned thread_id_;

  // Stack/continuation management
  char * const stack_memory_;
  const size_t stack_size_;

  context_switcher::continuation continuation_;
  bool is_synchronized_;
  bool is_serial_section_;

  // Linked list for trading/memory management
  base_task *prev_;
  base_task *next_;

  std::atomic<strain_resource *> attached_resources_;

#if PLS_PROFILING_ENABLED
  profiling::dag_node *profiling_node_;
#endif
};

}

#endif //PLS_BASE_TASK_H
