
#ifndef PLS_LOCKING_DEQUE_H
#define PLS_LOCKING_DEQUE_H

#include <mutex>

#include "pls/internal/base/spin_lock.h"
#include "aligned_stack.h"

namespace pls {
namespace internal {
namespace scheduling {
namespace data_structures {

using deque_offset = aligned_stack::stack_offset;

/**
 * Wraps any object into a deque Task.
 */
template<typename Task>
struct locking_deque_task {
  Task *item_;

  locking_deque_task *prev_;
  locking_deque_task *next_;

};

template<typename Task, typename Content>
struct locking_deque_container : public locking_deque_task<Task> {
  Content content_;

 public:
  template<typename ...ARGS>
  explicit locking_deque_container(ARGS &&... args) : content_{std::forward<ARGS>(args)...} {}
};

/**
 * A double linked list based deque.
 * Storage is therefore only needed for the individual items.
 *
 * @tparam Task The type of Tasks stored in this deque
 */
template<typename Task>
class locking_deque {
  aligned_stack *stack_;

  locking_deque_task<Task> *head_;
  locking_deque_task<Task> *tail_;

  locking_deque_task<Task> *last_inserted_;

  base::spin_lock lock_;

 public:
  explicit locking_deque(aligned_stack *stack)
      : stack_{stack}, head_{nullptr}, tail_{nullptr}, lock_{} {}

  template<typename T, typename ...ARGS>
  T *push_task(ARGS &&... args);
  template<typename T, typename ...ARGS>
  T *push_object(ARGS &&... args);
  void *push_bytes(size_t size);
  void publish_last_task();

  Task *pop_local_task(bool &cas_fail_out);
  Task *pop_local_task();
  Task *pop_external_task(bool &cas_fail_out);
  Task *pop_external_task();

  void reset_offset(deque_offset state);
  deque_offset save_offset();
};

}
}
}
}
#include "locking_deque_impl.h"

#endif //PLS_LOCKING_DEQUE_H
