
#ifndef PLS_LOCKING_DEQUE_IMPL_H_
#define PLS_LOCKING_DEQUE_IMPL_H_

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

template<typename Task>
template<typename T, typename ...ARGS>
T *locking_deque<Task>::push_task(ARGS &&...args) {
  static_assert(std::is_same<Task, T>::value || std::is_base_of<Task, T>::value,
                "Must only push types of <Item> onto work_stealing_deque<Item>");

  // Allocate object
  auto deque_item = stack_->push<locking_deque_container<Task, T>>(std::forward<ARGS>(args)...);
  deque_item->item_ = &deque_item->content_;

  // Keep for later publishing
  last_inserted_ = deque_item;

  // ...actual data reference
  return &deque_item->content_;
}

template<typename Task>
template<typename T, typename ...ARGS>
T *locking_deque<Task>::push_object(ARGS &&... args) {
  // Simply add data to the stack, do not publish it in any way
  return stack_->push<T>(std::forward(args)...);
}

template<typename Task>
void *locking_deque<Task>::push_bytes(size_t size) {
  // Simply add data to the stack, do not publish it in any way
  return stack_->push_bytes(size);
}

template<typename Task>
void locking_deque<Task>::publish_last_task() {
  std::lock_guard<base::spin_lock> lock{lock_};

  if (tail_ != nullptr) {
    tail_->next_ = last_inserted_;
  } else {
    head_ = last_inserted_;
  }
  last_inserted_->prev_ = tail_;
  last_inserted_->next_ = nullptr;
  tail_ = last_inserted_;
}

template<typename Task>
Task *locking_deque<Task>::pop_local_task() {
  bool cas_fail_out;
  return pop_local_task(cas_fail_out);
}

template<typename Task>
Task *locking_deque<Task>::pop_local_task(bool &cas_fail_out) {
  std::lock_guard<base::spin_lock> lock{lock_};
  cas_fail_out = false; // Can not fail CAS in locking implementation

  if (tail_ == nullptr) {
    return nullptr;
  }

  auto result = tail_;
  tail_ = tail_->prev_;
  if (tail_ == nullptr) {
    head_ = nullptr;
  } else {
    tail_->next_ = nullptr;
  }

  return result->item_;
}

template<typename Task>
Task *locking_deque<Task>::pop_external_task() {
  bool cas_fail_out;
  return pop_external_task(cas_fail_out);
}

template<typename Task>
Task *locking_deque<Task>::pop_external_task(bool &cas_fail_out) {
  std::lock_guard<base::spin_lock> lock{lock_};
  cas_fail_out = false; // Can not fail CAS in locking implementation

  if (head_ == nullptr) {
    return nullptr;
  }

  auto result = head_;
  head_ = head_->next_;
  if (head_ == nullptr) {
    tail_ = nullptr;
  } else {
    head_->prev_ = nullptr;
  }

  return result->item_;
}

template<typename Task>
void locking_deque<Task>::reset_offset(deque_offset state) {
  stack_->reset_offset(state);
}

template<typename Task>
deque_offset locking_deque<Task>::save_offset() {
  return stack_->save_offset();
}

}
}
}
}

#endif //PLS_LOCKING_DEQUE_IMPL_H_
