// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#ifndef QSHAREDHANDLE_P_H
#define QSHAREDHANDLE_P_H

#include <QtCore/private/quniquehandle_p.h>
#include <QtCore/qtconfigmacros.h>
#include <QtCore/qcompare.h>

#if __cpp_lib_concepts
#  include <concepts>
#endif

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

QT_BEGIN_NAMESPACE

namespace QtPrivate {

#if __cpp_lib_concepts

// clang-format off

// Define a concept for the traits
template <typename T>
concept QSharedHandleTraitsConcept = requires
{
    typename T::Type;

    { T::invalidValue() } noexcept -> std::same_as<typename T::Type>;
    { T::ref(std::declval<typename T::Type>()) } -> std::same_as<typename T::Type>;
    { T::unref(std::declval<typename T::Type>()) } -> std::same_as<bool>;
};

// clang-format on

#endif

#if __cpp_lib_concepts
template <QSharedHandleTraitsConcept SharedHandleTraits>
#else
template <typename SharedHandleTraits>
#endif
struct UniqueHandleTraitsFromSharedHandleTraits
{
    using Type = typename SharedHandleTraits::Type;

    [[nodiscard]] static Type invalidValue() noexcept(noexcept(SharedHandleTraits::invalidValue()))
    {
        return SharedHandleTraits::invalidValue();
    }

    [[nodiscard]] static bool
    close(Type handle) noexcept(noexcept(SharedHandleTraits::unref(handle)))
    {
        return SharedHandleTraits::unref(handle);
    }
};

#if __cpp_lib_concepts
template <QSharedHandleTraitsConcept HandleTraits>
#else
template <typename HandleTraits>
#endif
struct QSharedHandle : private QUniqueHandle<UniqueHandleTraitsFromSharedHandleTraits<HandleTraits>>
{
private:
    using BaseClass = QUniqueHandle<UniqueHandleTraitsFromSharedHandleTraits<HandleTraits>>;

    static constexpr bool BaseResetIsNoexcept =
            noexcept(std::declval<BaseClass>().reset(std::declval<typename HandleTraits::Type>()));

    static constexpr bool RefIsNoexcept =
            noexcept(HandleTraits::ref(std::declval<typename HandleTraits::Type>()));

    static constexpr bool BaseMoveIsNoexcept =
            noexcept(std::declval<BaseClass>().operator=(std::move(std::declval<BaseClass>())));

public:
    using typename BaseClass::Type;

    enum RefMode : uint8_t
    {
        HasRef,
        NeedsRef,

        // syntactic sugar
        AddRef = NeedsRef,
        NoAddRef = HasRef,
    };

    QSharedHandle() = default;

    explicit QSharedHandle(typename HandleTraits::Type object, RefMode mode)
        : BaseClass{ mode == NeedsRef ? HandleTraits::ref(object) : object }
    {
    }

    QSharedHandle(const QSharedHandle &o)
        : BaseClass{
              HandleTraits::ref(o.get()),
          }
    {
    }

    QSharedHandle(QSharedHandle &&) noexcept = default;

    // NOLINTNEXTLINE: bugprone-unhandled-self-assign
    QSharedHandle &operator=(const QSharedHandle &o) noexcept(RefIsNoexcept && BaseResetIsNoexcept)
    {
        if (BaseClass::get() != o.get())
            BaseClass::reset(HandleTraits::ref(o.get()));
        return *this;
    };

    QSharedHandle &operator=(QSharedHandle &&o) noexcept(BaseMoveIsNoexcept)
    {
        BaseClass::operator=(std::forward<QSharedHandle>(o));
        return *this;
    }

    void reset(typename HandleTraits::Type o,
               RefMode mode) noexcept(RefIsNoexcept && BaseResetIsNoexcept)
    {
        if (mode == NeedsRef)
            BaseClass::reset(HandleTraits::ref(o));
        else
            BaseClass::reset(o);
    }

    void reset() noexcept(BaseResetIsNoexcept) { BaseClass::reset(); }

    [[nodiscard]] friend bool operator==(const QSharedHandle &lhs,
                                         const QSharedHandle &rhs) noexcept
    {
        return lhs.get() == rhs.get();
    }

    [[nodiscard]] friend bool operator!=(const QSharedHandle &lhs,
                                         const QSharedHandle &rhs) noexcept
    {
        return lhs.get() != rhs.get();
    }

    [[nodiscard]] friend bool operator<(const QSharedHandle &lhs, const QSharedHandle &rhs) noexcept
    {
        return lhs.get() < rhs.get();
    }

    [[nodiscard]] friend bool operator<=(const QSharedHandle &lhs,
                                         const QSharedHandle &rhs) noexcept
    {
        return lhs.get() <= rhs.get();
    }

    [[nodiscard]] friend bool operator>(const QSharedHandle &lhs, const QSharedHandle &rhs) noexcept
    {
        return lhs.get() > rhs.get();
    }

    [[nodiscard]] friend bool operator>=(const QSharedHandle &lhs,
                                         const QSharedHandle &rhs) noexcept
    {
        return lhs.get() >= rhs.get();
    }

    using BaseClass::get;
    using BaseClass::isValid;
    using BaseClass::operator bool;
    using BaseClass::release;
    using BaseClass::operator&;

    void swap(QSharedHandle &other) noexcept(noexcept(std::declval<BaseClass>().swap(other)))
    {
        BaseClass::swap(other);
    }
};

template <typename Trait>
void swap(QSharedHandle<Trait> &lhs, QSharedHandle<Trait> &rhs) noexcept(noexcept(lhs.swap(rhs)))
{
    lhs.swap(rhs);
}

} // namespace QtPrivate

QT_END_NAMESPACE

#endif // QSHAREDHANDLE_P_H
