#ifndef PLS_TASK_H
#define PLS_TASK_H

#include <utility>
#include <atomic>

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

#include "pls/internal/base/system_details.h"
#include "pls/internal/data_structures/stamped_integer.h"
#include "pls/internal/scheduling/traded_cas_field.h"

namespace pls {
namespace internal {
namespace 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).
 */
struct alignas(base::system_details::CACHE_LINE_SIZE) task {
  void init(char *stack_memory, size_t stack_size, unsigned depth, unsigned thread_id) {
    stack_memory_ = stack_memory;
    stack_size_ = stack_size;

    depth_ = depth;
    thread_id_ = thread_id;
  }

  context_switcher::continuation get_continuation() {
    return std::move(continuation_);
  }
  void set_continuation(context_switcher::continuation &&continuation) {
    continuation_ = std::move(continuation);
  }

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

  // TODO: Proper access control and split it up into responsibilities
  // Stack/Continuation Management
  char *stack_memory_;
  size_t stack_size_;
  context_switcher::continuation continuation_;

  // Work-Stealing
  std::atomic<traded_cas_field> external_trading_deque_cas_{};
  std::atomic<task *> resource_stack_next_{};
  std::atomic<data_structures::stamped_integer> resource_stack_root_{{0, 0}};
  bool clean_;

  // Task Tree (we have a parent that we want to continue when we finish)
  task *parent_task_;
  unsigned depth_;
  unsigned thread_id_;

  // Memory Linked List
  task *prev_;
  task *next_;
};

}
}
}

#endif //PLS_TASK_H
