/**
 * Abstraction for threading to allow porting.
 * Currently using either pthread or C++ 11 threads.
 */

#ifndef PLS_THREAD_H
#define PLS_THREAD_H

#include <functional>
#include <pthread.h>
#include <atomic>
#include <ctime>

#include "system_details.h"

namespace pls {
namespace internal {
namespace base {

using thread_entrypoint = void();

/**
 * Static methods than can be performed on the current thread.
 *
 * usage:
 * this_thread::yield();
 * T* state = this_thread::state<T>();
 *
 * PORTABILITY:
 * Current implementation is based on pthreads.
 */
class this_thread {
  friend
  class thread;
#ifdef PLS_THREAD_SPECIFIC_PTHREAD
  static pthread_key_t local_storage_key_;
  static bool local_storage_key_initialized_;
#endif
#ifdef PLS_THREAD_SPECIFIC_COMPILER
  static __thread void *local_state_;
#endif
 public:
  static void yield() {
    pthread_yield();
  }

  static void sleep(long microseconds) {
    timespec time{0, 1000 * microseconds};
    nanosleep(&time, nullptr);
  }

  /**
   * Retrieves the local state pointer.
   *
   * @tparam T The type of the state that is stored.
   * @return The state pointer hold for this thread.
   */
  template<typename T>
  static T *state();

  /**
   * Stores a pointer to the thread local state object.
   * The memory management for this has to be done by the user,
   * we only keep the pointer.
   *
   * @tparam T The type of the state that is stored.
   * @param state_pointer A pointer to the threads state object.
   */
  template<typename T>
  static void set_state(T *state_pointer);
};

/**
 * Abstraction for starting a function in a separate thread.
 * Offers only threading functionality needed in this project,
 * underlying implementation can be changed.
 * Uses NO heap memory allocation.
 *
 * PORTABILITY:
 * Current implementation is based on pthreads.
 */
class thread {
  friend class this_thread;
  // Keep handle to native implementation
  pthread_t pthread_thread_;
  bool running_;

  template<typename Function, typename State>
  static void *start_pthread_internal(void *thread_pointer);

 public:
  template<typename Function, typename State>
  explicit thread(const Function &function, State *state_pointer);

  template<typename Function>
  explicit thread(const Function &function);

  explicit thread();
  ~thread();

  void join();

  // make object move only
  thread(thread &&) noexcept;
  thread &operator=(thread &&) noexcept;

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

}
}
}
#include "thread_impl.h"

#endif //PLS_THREAD_H
