#pragma once

#include <thread>
#include <vector>
#include <mutex>

template<typename T>
class safe_vector {
public:
    void push_back(const T& value) {
        std::lock_guard lock(mutex);
        data.push_back(value);
    }

    void push_back(T&& in_value) {
        std::lock_guard lock(mutex);
        data.push_back(std::move(in_value));
    }

    [[nodiscard]] std::optional<T> pop_back(bool shrink = false) {
        std::lock_guard lock(mutex);
        if (data.empty()) {
            return std::nullopt;
        }
        T value = std::move(data.back());
        data.pop_back();
        if (shrink) {
            data.shrink_to_fit();
        }
        return value;
    }

    [[nodiscard]] std::optional<T> pop_front() {
        std::lock_guard lock(mutex);
        if (data.empty()) {
            return std::nullopt;
        }
        T value = std::move(data.front());
        data.erase(data.begin());
        return value;
    }

    [[nodiscard]] auto size() const {
        std::lock_guard lock(mutex);
        return data.size();
    }

    [[nodiscard]] auto empty() const {
        std::lock_guard lock(mutex);
        return data.empty();
    }

    [[nodiscard]] auto begin() {
        std::lock_guard lock(mutex);
        return data.begin();
    }

    [[nodiscard]] auto end() {
        std::lock_guard lock(mutex);
        return data.end();
    }

    [[nodiscard]] auto begin() const {
        std::lock_guard lock(mutex);
        return data.begin();
    }

    [[nodiscard]] auto end() const {
        std::lock_guard lock(mutex);
        return data.end();
    }

    [[nodiscard]] auto rbegin() {
        std::lock_guard lock(mutex);
        return data.rbegin();
    }

    [[nodiscard]] auto rend() {
        std::lock_guard lock(mutex);
        return data.rend();
    }

    [[nodiscard]] auto rbegin() const {
        std::lock_guard lock(mutex);
        return data.rbegin();
    }

    [[nodiscard]] auto rend() const {
        std::lock_guard lock(mutex);
        return data.rend();
    }

private:
    mutable std::mutex mutex;
    std::vector<T> data;
};
