#pragma once

#include <condition_variable>
#include <mutex>
#include <optional>
#include <queue>

namespace th_safe {
template <typename T> class queue {
public:
  void push(const T &value) {
    {
      std::lock_guard lock(mutex_);
      queue_.push(value);
    }
    cond_var_.notify_one();
  }

  std::optional<T> pop() {
    std::unique_lock lock(mutex_);
    cond_var_.wait(lock, [this] { return !queue_.empty() || done_; });

    if (queue_.empty())
      return std::nullopt;

    T value = queue_.front();
    queue_.pop();
    return value;
  }

  void done() {
    {
      std::lock_guard lock(mutex_);
      done_ = true;
    }
    cond_var_.notify_all();
  }

private:
  std::queue<T> queue_;
  std::mutex mutex_;
  std::condition_variable cond_var_;
  bool done_ = false;
};
} // namespace th_safe
