
#ifndef PLS_LOCKING_DEQUE_H
#define PLS_LOCKING_DEQUE_H

#include <mutex>

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

namespace pls {
namespace internal {
namespace data_structures {

/**
 * Wraps any object into a deque item.
 */
template<typename Item>
struct locking_deque_item {
  Item *item_;

  locking_deque_item *prev_;
  locking_deque_item *next_;

};

template<typename Item, typename Content>
struct locking_deque_container : public locking_deque_item<Item> {
  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 Item The type of items stored in this deque
 */
template<typename Item>
class locking_deque {
  aligned_stack *stack_;

  locking_deque_item<Item> *head_;
  locking_deque_item<Item> *tail_;

  base::spin_lock lock_;

 public:
  using state = aligned_stack::state;

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

  template<typename T, typename ...ARGS>
  T *push_tail(ARGS &&... args);
  template<typename T, typename Function, typename ...ARGS>
  T *push_tail_cb(const Function &after_creation, ARGS &&... args);
  Item *pop_tail();
  Item *pop_head();

  void release_memory_until(state state);
  state save_state();
};

}
}
}
#include "locking_deque_impl.h"

#endif //PLS_LOCKING_DEQUE_H
