#pragma once

#include <cstddef>
#include <utility>

namespace kuiper
{

/// Reference counted type
template<typename T>
class rc {
  public:
    /// Argument-forwarding constructor
    template<typename... Args>
    explicit rc(Args&&... args)
        : m_inner(std::forward<Args>(args)...) {}

    /// Default constructor (no args.)
    rc()
        : m_inner() {}

    /// Destructor
    ~rc() {
        if (m_ref_count > 0)
            return;
    }

    /// Copy constructor
    rc(const rc& other)
        : m_inner(other.m_inner), m_ref_count(other.m_ref_count + 1) {}

    /// Copy assignment constructor
    rc& operator=(const rc& other) {
        m_inner     = other.m_inner;
        m_ref_count = other.m_ref_count + 1;
        return *this;
    }

    /// Move constructor
    rc(rc&& other) noexcept
        : m_inner(std::move(other.m_inner)), m_ref_count(other.m_ref_count) {};

    /// Move assignment constructor
    rc& operator=(rc&& other) noexcept {
        *this = std::move(other);
        return *this;
    };

    // Inner type access

    /// Reference to the type contained in the `rc`
    T& inner() noexcept {
        return m_inner;
    }

    /// Read-only reference to the type contained in the `rc`
    const T& const_inner() const noexcept {
        return m_inner;
    }

    // Reference count

    std::size_t ref_count() const noexcept {
        return m_ref_count;
    }

  private:
    T m_inner;

    std::size_t m_ref_count = 0;
};

} // namespace kuiper
