
#ifndef PLS_THREAD_STATE_H
#define PLS_THREAD_STATE_H

#include <random>
#include <memory>
#include <array>
#include <chrono>

namespace pls {
namespace internal {
namespace scheduling {

// forward declaration
class task_manager;
class cont_manager;
class scheduler;
class base_task;
class base_cont;

struct alignas(base::system_details::CACHE_LINE_SIZE) thread_state {
  scheduler *scheduler_;
  size_t id_;

  // Keep track of the last spawn state (needed to chain tasks/conts correctly)
  bool right_spawn_;
  base_cont *parent_cont_;
  // TODO: Set this when spawning!
  //       See if we should move this to the cont manager...seems like a better fit!

  task_manager &task_manager_;
  cont_manager &cont_manager_;

  alignas(base::system_details::CACHE_LINE_SIZE) base_task *current_task_;
  alignas(base::system_details::CACHE_LINE_SIZE) std::minstd_rand random_;

 public:
  thread_state(task_manager &task_manager,
               cont_manager &cont_manager) :
      scheduler_{nullptr},
      id_{0},
      right_spawn_{false},
      parent_cont_{nullptr},
      task_manager_{task_manager},
      cont_manager_{cont_manager},
      current_task_{nullptr},
      random_{static_cast<unsigned long>(std::chrono::steady_clock::now().time_since_epoch().count())} {};

  void reset() {
    right_spawn_ = false;
    parent_cont_ = nullptr;
  }

  /**
   * Convenience helper to get the thread_state instance associated with this thread.
   * Must only be called on threads that are associated with a thread_state,
   * this will most likely be threads created by the scheduler.
   *
   * @return The thread_state of this thread.
   */
  static thread_state &get() { return *base::this_thread::state<thread_state>(); }

  size_t get_id() { return id_; }
  task_manager &get_task_manager() { return task_manager_; }
  cont_manager &get_cont_manager() { return cont_manager_; }
  scheduler &get_scheduler() { return *scheduler_; }

  // Do not allow move/copy operations.
  // State is a pure memory container with references/pointers into it from all over the code.
  // It should be allocated, used and de-allocated, nothing more.
  thread_state(thread_state &&) = delete;
  thread_state &operator=(thread_state &&) = delete;

  thread_state(const thread_state &) = delete;
  thread_state &operator=(const thread_state &) = delete;
};

}
}
}

#endif //PLS_THREAD_STATE_H
