
#ifndef PLS_THREAD_STATE_H
#define PLS_THREAD_STATE_H

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

#include "context_switcher/continuation.h"

#include "pls/internal/base/system_details.h"

namespace pls::internal::scheduling {

class scheduler;
class task_manager;

/**
 * Proxy-Object for thread local state needed during scheduling.
 * The main use is to perform thread_state::get() as a thread local
 * memory to identify the current worker thread state.
 *
 * Holds only minimal data by itself and points to the appropriate scheduler
 * and task manager objects associated with this thread.
 */
struct PLS_CACHE_ALIGN thread_state {
 private:
  const unsigned thread_id_;
  scheduler &scheduler_;
  task_manager &task_manager_;

  PLS_CACHE_ALIGN context_switcher::continuation main_loop_continuation_;
  PLS_CACHE_ALIGN std::minstd_rand random_;

 public:
  explicit thread_state(scheduler &scheduler,
                        unsigned thread_id,
                        task_manager &task_manager) :
      thread_id_{thread_id},
      scheduler_{scheduler},
      task_manager_{task_manager},
      random_{static_cast<unsigned long>(std::chrono::steady_clock::now().time_since_epoch().count())} {};

  // Do not allow accidental copy/move operations.
  thread_state(const thread_state &) = delete;
  thread_state(thread_state &&) = delete;
  thread_state &operator=(const thread_state &) = delete;
  thread_state &operator=(thread_state &&) = delete;

  /**
   * 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.
   *
   * Each call is guaranteed to be a new lookup, i.e. it is not cached after fiber context switches.
   *
   * @return The thread_state of this thread.
   */
  [[nodiscard]] static thread_state &PLS_NOINLINE get();
  static void set(thread_state *);

  [[nodiscard]] unsigned get_thread_id() const { return thread_id_; }
  [[nodiscard]] task_manager &get_task_manager() { return task_manager_; }
  [[nodiscard]] scheduler &get_scheduler() { return scheduler_; }
  [[nodiscard]] long get_rand() {
    return random_();
  }

  [[nodiscard]] context_switcher::continuation &main_continuation() {
    return main_loop_continuation_;
  }
};

}

#endif //PLS_THREAD_STATE_H
