#ifndef RINGBUFFER_H
#define RINGBUFFER_H

#include <cstddef>
#include <iostream>
#include <vector>
#include <atomic>
#include <algorithm>
#include <cstdint>
#include <type_traits>

template<typename T>
class RingBuffer {

public:
    explicit RingBuffer(size_t capacity, T initValue = T{})
        : m_capacity(capacity)
        , m_data(capacity, initValue) {}

    inline void push(const T& v) {
        const uint64_t w = m_write.fetch_add(1, std::memory_order_relaxed);
        m_data[w % m_capacity] = v;
        const uint64_t newSize = std::min<uint64_t>(w + 1, m_capacity);
        m_size.store(newSize, std::memory_order_release);
    }

    inline uint64_t size() const {
        return m_size.load(std::memory_order_acquire);
    }

    inline uint64_t capacity() const {
        return m_capacity;
    }

    inline uint64_t oldestGlobalIndex() const {
        const uint64_t w = m_write.load(std::memory_order_acquire);
        const uint64_t s = size();
        return (w >= s) ? (w - s) : 0;
    }

    inline uint64_t newestGlobalIndex() const {
        const uint64_t w = m_write.load(std::memory_order_acquire);
        return (w > 0) ? (w - 1) : 0;
    }

    inline bool readByGlobalIndex(uint64_t gidx, T& out) const {
        const uint64_t oldest = oldestGlobalIndex();
        const uint64_t newest = newestGlobalIndex();
        if (gidx < oldest || gidx > newest)
            return false;

        out = m_data[gidx % m_capacity];
        return true;
    }

    inline void reset() {
        m_write.store(0, std::memory_order_release);
        m_size.store(0, std::memory_order_release);
    }

    inline void readRange(uint64_t gidx, uint64_t count, std::vector<T>& out) const {
        out.clear();
        out.reserve(count);
        for (size_t i = 0; i < count; i++) {
            T v{};
            if (readByGlobalIndex(gidx, v)) {
                out.push_back(v);
            }
            else {

            }
        }
    }

private:
    uint64_t m_capacity;
    std::vector<T> m_data;
    std::atomic<uint64_t> m_write{0};
    std::atomic<uint64_t> m_size{0};
};

#endif