#pragma once
#include <array>
#include <atomic>
#include <cstddef>
#include <optional>

namespace lumi {
template <typename T, std::size_t Capacity> class bounded_spsc_queue {
    static_assert(Capacity > 1);
    std::array<T, Capacity> values_{};
    alignas(64) std::atomic<std::size_t> head_{0};
    alignas(64) std::atomic<std::size_t> tail_{0};
    std::atomic<std::uint64_t> dropped_{0};
public:
    bool try_push(T value) noexcept {
        const auto head = head_.load(std::memory_order_relaxed);
        const auto next = (head + 1) % Capacity;
        if (next == tail_.load(std::memory_order_acquire)) { dropped_.fetch_add(1, std::memory_order_relaxed); return false; }
        values_[head] = std::move(value);
        head_.store(next, std::memory_order_release);
        return true;
    }
    std::optional<T> try_pop() noexcept {
        const auto tail = tail_.load(std::memory_order_relaxed);
        if (tail == head_.load(std::memory_order_acquire)) return std::nullopt;
        T value = std::move(values_[tail]);
        tail_.store((tail + 1) % Capacity, std::memory_order_release);
        return value;
    }
    std::uint64_t dropped() const noexcept { return dropped_.load(std::memory_order_relaxed); }
};
}
