#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::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).
 */
struct PLS_CACHE_ALIGN task {
  task(char *stack_memory, size_t stack_size, unsigned depth, unsigned thread_id) :
      stack_memory_{stack_memory},
      stack_size_{stack_size},
      is_synchronized_{false},
      depth_{depth},
      thread_id_{thread_id},
      prev_{nullptr},
      next_{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.
  task(const task &other) = delete;
  task(task &&other) = delete;
  task &operator=(const task &other) = delete;
  task &operator=(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));
  }

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

  // TODO: Clean up responsibilities
  // 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}};

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

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

}

#endif //PLS_TASK_H
