#pragma once
#include <luna/Atomic.h>
#include <luna/Types.h>

template <typename T, usize Size> class CircularQueue
{
    enum
    {
        Capacity = Size + 1
    };

  public:
    CircularQueue()
    {
    }

    bool try_push(const T& value)
    {
        usize current_tail = m_tail.load(MemoryOrder::Relaxed);
        const usize new_tail = (current_tail + 1) % Capacity;
        if (new_tail == m_head.load(MemoryOrder::Acquire))
        {
            // Queue is full
            return false;
        }
        m_data[current_tail] = value;
        if (!m_tail.compare_exchange_strong(current_tail, new_tail, MemoryOrder::Release, MemoryOrder::Relaxed))
        {
            // Someone else updated the tail
            return false;
        }
        return true;
    }

    bool try_pop(T& value)
    {
        usize current_head = m_head.load(MemoryOrder::Relaxed);
        if (current_head == m_tail.load(MemoryOrder::Acquire))
        {
            // Queue is empty
            return false;
        }
        value = m_data[current_head];
        const usize new_head = (current_head + 1) % Capacity;
        if (!m_head.compare_exchange_strong(current_head, new_head, MemoryOrder::Release, MemoryOrder::Relaxed))
        {
            // Someone else updated the head
            return false;
        }
        return true;
    }

  private:
    T m_data[Capacity];
    Atomic<usize> m_head = 0;
    Atomic<usize> m_tail = 0;
};