#ifndef PLS_HEAP_SCHEDULER_MEMORY_H
#define PLS_HEAP_SCHEDULER_MEMORY_H

#include <vector>

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

#include "pls/internal/scheduling/scheduler_memory.h"
#include "pls/internal/scheduling/thread_state.h"
#include "pls/internal/scheduling/thread_state_static.h"

namespace pls {
namespace internal {
namespace scheduling {

template<size_t NUM_TASKS, size_t STACK_SIZE>
class heap_scheduler_memory : public scheduler_memory {
 public:
  explicit heap_scheduler_memory(size_t max_threads) : max_threads_{max_threads},
                                                       thread_vector_{},
                                                       thread_state_vector_{},
                                                       thread_state_pointers_{} {
    thread_vector_.reserve(max_threads);
    thread_state_vector_.reserve(max_threads);

    for (size_t i = 0; i < max_threads; i++) {
      thread_vector_.emplace_back();
      thread_state_vector_.emplace_back();
      thread_state_pointers_.emplace_back(&thread_state_vector_[i].get_thread_state());
    }
    thread_states_array_ = thread_state_pointers_.data();
  }

  size_t max_threads() const override {
    return max_threads_;
  }

  base::thread &thread_for(size_t id) override {
    return thread_vector_[id];
  }
 private:
  using thread_state_type = thread_state_static<NUM_TASKS, STACK_SIZE>;
  // thread_state_type is aligned at the cache line and therefore overaligned (C++ 11 does not require
  // the new operator to obey alignments bigger than 16, cache lines are usually 64).
  // To allow this object to be allocated using 'new' (which the vector does internally),
  // we need to wrap it in an non aligned object.
  using thread_state_wrapper = base::alignment::cache_alignment_wrapper<thread_state_type>;

  size_t max_threads_;
  std::vector<base::thread> thread_vector_;
  std::vector<thread_state_wrapper> thread_state_vector_;
  std::vector<thread_state *> thread_state_pointers_;
};

}
}
}

#endif // PLS_HEOP_SCHEDULER_MEMORY_H
