
#ifndef PLS_THREAD_STATE_H
#define PLS_THREAD_STATE_H

#include <random>
#include <chrono>
#include <utility>

#include "context_switcher/continuation.h"

namespace pls {
namespace internal {
namespace scheduling {

class scheduler;
class task_manager;
struct task;

struct alignas(base::system_details::CACHE_LINE_SIZE) thread_state {
 private:
  scheduler *scheduler_;
  unsigned id_;
  task_manager &task_manager_;

  alignas(base::system_details::CACHE_LINE_SIZE) task *current_task_;
  alignas(base::system_details::CACHE_LINE_SIZE) context_switcher::continuation main_loop_continuation_;
  alignas(base::system_details::CACHE_LINE_SIZE) std::minstd_rand random_;

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

  /**
   * 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>(); }

  unsigned get_id() { return id_; }
  void set_id(unsigned id) {
    id_ = id;
  }
  task_manager &get_task_manager() { return task_manager_; }
  scheduler &get_scheduler() { return *scheduler_; }
  void set_scheduler(scheduler *scheduler) {
    scheduler_ = scheduler;
  }
  long get_rand() {
    return random_();
  }

  void set_main_continuation(context_switcher::continuation &&continuation) {
    main_loop_continuation_ = std::move(continuation);
  }
  context_switcher::continuation get_main_continuation() {
    return std::move(main_loop_continuation_);
  }

  // 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
