#pragma once

#include <atomic>
#include <cstddef>
#include <vector>

namespace softbus::core::memory
{

template <typename T>
class LockFreeQueue
{
public:
    // SPSC ring queue. Producer and consumer must be single-threaded.
    explicit LockFreeQueue(std::size_t capacity)
        : m_capacity(capacity + 1), m_data(m_capacity)
    {
    }

    bool push(const T& value)
    {
        const auto head = m_head.load(std::memory_order_relaxed);
        const auto next = increment(head);
        if (next == m_tail.load(std::memory_order_acquire)) {
            return false; // queue full
        }
        m_data[head] = value;
        m_head.store(next, std::memory_order_release);
        return true;
    }

    bool push(T&& value)
    {
        const auto head = m_head.load(std::memory_order_relaxed);
        const auto next = increment(head);
        if (next == m_tail.load(std::memory_order_acquire)) {
            return false; // queue full
        }
        m_data[head] = std::move(value);
        m_head.store(next, std::memory_order_release);
        return true;
    }

    bool pop(T& out)
    {
        const auto tail = m_tail.load(std::memory_order_relaxed);
        if (tail == m_head.load(std::memory_order_acquire)) {
            return false; // queue empty
        }
        out = std::move(m_data[tail]);
        m_tail.store(increment(tail), std::memory_order_release);
        return true;
    }

    bool empty() const
    {
        return m_head.load(std::memory_order_acquire) == m_tail.load(std::memory_order_acquire);
    }

    std::size_t capacity() const
    {
        return m_capacity - 1;
    }

private:
    std::size_t increment(std::size_t idx) const
    {
        return (idx + 1) % m_capacity;
    }

private:
    const std::size_t m_capacity;
    std::vector<T> m_data;
    alignas(64) std::atomic<std::size_t> m_head{0};
    alignas(64) std::atomic<std::size_t> m_tail{0};
};

} // namespace softbus::core::memory
