// 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 QOHOSCOMMON_H
#define QOHOSCOMMON_H

//
//  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.
//

#include <QtCore/private/qohoslogger_p.h>
#include <QtCore/qglobal.h>
#include <QtCore/qlogging.h>
#include <QtCore/qdebug.h>
#include <array>
#include <cstdint>
#include <cstdlib>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#ifndef QT_NO_EXCEPTIONS
#include <stdexcept>
#endif
#include <type_traits>
#include <utility>

#define Q_OHOS_NAMED_FUNC(func) \
    ( \
        QT_PREPEND_NAMESPACE(makeQOhosNamedFunc)<decltype(&func)>( \
            [](auto &&...args) -> decltype(func(std::forward<decltype(args)>(args)...)) { \
                return func(std::forward<decltype(args)>(args)...); \
            }, \
            QT_STRINGIFY(func)) \
    )

#define qOhosReportFatalErrorAndAbort(...) \
    do { \
        QT_PREPEND_NAMESPACE(QMessageLogger)(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).fatal(__VA_ARGS__); \
        std::abort(); \
    } while (false)

QT_BEGIN_NAMESPACE

template<typename T>
class QOhosOptional;

namespace qohoscommon_h_detail {

template<typename A, typename B, typename = void>
struct HasEqualityComparatorT : std::false_type {};

template<typename A, typename B>
struct HasEqualityComparatorT<A, B, decltype(void(std::declval<A>() == std::declval<B>()))>
    : std::true_type {};

template<typename A, typename B>
constexpr bool hasEqualityComparator = HasEqualityComparatorT<A, B>::value;

template<typename>
struct IsQOhosOptional : std::false_type {};

template<typename T>
struct IsQOhosOptional<QOhosOptional<T>> : std::true_type {};

template<typename T>
constexpr bool isQOhosOptional = IsQOhosOptional<T>::value;

}

template<typename Func, typename... FuncArgs>
using QOhosInvokeResult = decltype(std::declval<Func>()(std::declval<FuncArgs>()...));

template<typename ...Ts>
using QOhosConsumer = std::function<void(Ts...)>;

template<typename T>
using QOhosSupplier = std::function<T()>;

template<typename... Args>
class QOhosTaskPromise
{
public:
    template<typename F, typename = std::enable_if_t<!std::is_same<std::decay_t<F>, QOhosTaskPromise>::value>>
    QOhosTaskPromise(
        F &&callable, std::function<void()> optOnDestroyedWithoutCall,
        std::string callerContextName = {});

    ~QOhosTaskPromise();

    QOhosTaskPromise(QOhosTaskPromise &&) = default;
    QOhosTaskPromise &operator=(QOhosTaskPromise &&) = default;

    QOhosTaskPromise(const QOhosTaskPromise &) = delete;
    QOhosTaskPromise &operator=(const QOhosTaskPromise &) = delete;

    void operator()(Args... args) const;

    QOhosTaskPromise makeChained(std::string callerContextName) &&;

    template<typename... ContextNames>
    std::array<QOhosTaskPromise, sizeof...(ContextNames)> makeBranched(ContextNames &&...branchContextNames) &&;

    std::pair<QOhosTaskPromise, QOhosTaskPromise> makeThenCatchBranches(std::string callerContextName) &&;

    operator std::function<void(Args...)>() &&;

private:
    class Callable
    {
    public:
        virtual ~Callable() = default;
        virtual void call(Args &&...args) = 0;
    };

    template<typename Func>
    class CallableImpl : public Callable
    {
    public:
        template<typename F>
        explicit CallableImpl(F &&f);

        void call(Args &&...args) override;

    private:
        Func m_func;
    };

    struct TrackingUsageState
    {
        TrackingUsageState(
            std::size_t activeCount,
            std::shared_ptr<std::function<void()>> onDestroyedWithoutCall);

        std::shared_ptr<std::function<void()>> onDestroyedWithoutCall;
        bool used = false;
        std::size_t activeCount = 1;
    };

    struct TrackingNode
    {
        TrackingNode(
            std::string callerContext, std::shared_ptr<TrackingNode> optParent,
            std::shared_ptr<TrackingUsageState> usage);

        void markUsed(const char *callerContextString);
        std::string rootCallerContextOrUnknown() const;
        void logCallerContextChainAsErrors(const char *messagePrefix) const;

        std::string callerContext;
        std::shared_ptr<TrackingNode> optParent;
        std::shared_ptr<TrackingUsageState> usage;
    };

    QOhosTaskPromise(std::shared_ptr<Callable> callable, std::shared_ptr<TrackingNode> tracking);

    std::shared_ptr<Callable> m_callable;
    std::shared_ptr<TrackingNode> m_tracking;
};

template<typename FuncPtr, typename Invoker>
class QOhosNamedFunc
{
public:
    QOhosNamedFunc(Invoker invoker, const char *funcName);

    const char *name() const;

    template<typename... Args>
    auto operator()(Args &&...args) const -> decltype(std::declval<const Invoker &>()(std::forward<Args>(args)...));

private:
    static_assert(
        std::is_pointer<FuncPtr>::value && std::is_function<std::remove_pointer_t<FuncPtr>>::value,
        "FuncPtr must be a function pointer type");

    Invoker m_invoker;
    const char *m_funcName;
};

template<typename FuncPtr, typename Invoker>
QOhosNamedFunc<FuncPtr, Invoker> makeQOhosNamedFunc(Invoker invoker, const char *funcName)
{
    return {std::move(invoker), funcName};
}

class QOhosNoOpConsumer
{
public:
    template<typename ...Ts>
    void operator()(const Ts &...) const;
};

template<typename ...Ts>
QOhosConsumer<Ts...> makeQOhosNoOpConsumer();

QOhosNoOpConsumer makeQOhosNoOpConsumer();

template<typename T>
class QOhosOptional
{
    static_assert(
        std::is_copy_constructible<T>::value && std::is_copy_assignable<T>::value,
        "Only copyable types are supported");

public:
    explicit QOhosOptional(const T &value);
    QOhosOptional(const QOhosOptional<void> &empty);

    QOhosOptional() = default;
    QOhosOptional(const QOhosOptional<T> &other);
    QOhosOptional<T> &operator=(const QOhosOptional<T> &other);
    QOhosOptional<T> &operator=(const T &value);
    QOhosOptional<T> &operator=(const QOhosOptional<void> &empty);

    ~QOhosOptional();

    bool hasValue() const;
    T valueOr(const T &fallback) const;

    const T &value() const;
    T &value();

    void reset();

    template<typename... Args>
    T &emplace(Args &&...args);


    template<typename Func>
    std::enable_if_t<
        qohoscommon_h_detail::isQOhosOptional<QOhosInvokeResult<Func, T>>,
        QOhosInvokeResult<Func, T>>
    andThen(Func &&func) const;

    template<typename Func>
    QOhosOptional<QOhosInvokeResult<Func, T>> transform(Func &&func) const;

private:
    T &storedValueRef();
    const T &storedValueRef() const;
    template<typename... InitArgs>
    void initializeStoredValue(InitArgs &&...initArgs);

    std::aligned_storage_t<sizeof(T), alignof(T)> m_rawStoredValue;
    bool m_hasValue = false;
};

template<typename T, typename U>
std::enable_if_t<qohoscommon_h_detail::hasEqualityComparator<T, U>, bool>
operator==(const QOhosOptional<T> &lhs, const QOhosOptional<U> &rhs);

template<typename T, typename U>
std::enable_if_t<qohoscommon_h_detail::hasEqualityComparator<T, U>, bool>
operator!=(const QOhosOptional<T> &lhs, const QOhosOptional<U> &rhs);

template<typename T, typename U>
std::enable_if_t<qohoscommon_h_detail::hasEqualityComparator<T, U>, bool>
operator==(const QOhosOptional<T> &lhs, const U &rhs);

template<typename T, typename U>
std::enable_if_t<qohoscommon_h_detail::hasEqualityComparator<T, U>, bool>
operator!=(const QOhosOptional<T> &lhs, const U &rhs);

template<typename T, typename U>
std::enable_if_t<qohoscommon_h_detail::hasEqualityComparator<T, U>, bool>
operator==(const T &lhs, const QOhosOptional<U> &rhs);

template<typename T, typename U>
std::enable_if_t<qohoscommon_h_detail::hasEqualityComparator<T, U>, bool>
operator!=(const T &lhs, const QOhosOptional<U> &rhs);

template<>
class QOhosOptional<void>
{
};

template<typename T>
class QOhosSpan
{
public:
    QOhosSpan(T *data, std::size_t size) Q_DECL_NOEXCEPT;

    template<typename OtherT, typename = std::enable_if_t<std::is_convertible<OtherT (*)[], T (*)[]>::value>>
    QOhosSpan(const QOhosSpan<OtherT> &other) Q_DECL_NOEXCEPT;

    T *begin() const Q_DECL_NOEXCEPT;
    T *end() const Q_DECL_NOEXCEPT;

    T *data() const Q_DECL_NOEXCEPT;
    std::size_t size() const Q_DECL_NOEXCEPT;

    T &operator[](std::size_t index) const;

    QOhosSpan<T> subspan(std::size_t offset) const;
    QOhosSpan<T> subspan(std::size_t offset, std::size_t length) const;

private:
    T *m_data;
    std::size_t m_size;
};

template<typename T>
QOhosSpan<const std::uint8_t> as_bytes(QOhosSpan<T> span) Q_DECL_NOEXCEPT;

template<typename T>
QOhosSpan<T> makeQOhosSpan(T *data, std::size_t count);

template<typename T>
QOhosOptional<T> makeQOhosOptional(const T &value);

QOhosOptional<void> makeEmptyQOhosOptional();

template<typename T>
class QOhosMutexProtectedValue
{
public:
    QOhosMutexProtectedValue();

    QOhosMutexProtectedValue(const QOhosMutexProtectedValue<T> &other) = delete;
    QOhosMutexProtectedValue &operator=(const QOhosMutexProtectedValue<T> &other) = delete;

    QOhosMutexProtectedValue(QOhosMutexProtectedValue<T> &&other) = delete;
    QOhosMutexProtectedValue &operator=(QOhosMutexProtectedValue<T> &&other) = delete;

    template<typename ProcessFunc>
    void processValue(ProcessFunc &&processFunc);

    template<typename EvalFunc>
    auto evalWithValue(EvalFunc &&evalFunc) const -> decltype(evalFunc(std::declval<const T &>()));

private:
    mutable std::mutex m_valueMutex;
    T m_value{};
};

namespace QtOhos {

template<typename T>
std::shared_ptr<T> moveToSharedPtr(T &&obj);

template<typename T>
std::shared_ptr<T> makeSharedPtrWithAttachedExtraData(
    std::shared_ptr<T> baseSharedPtr, std::shared_ptr<void> extraData);

template<typename T>
std::weak_ptr<T> makeWeakPtr(const std::shared_ptr<T> &obj);

std::shared_ptr<void> makeDestroyNotifier(std::function<void()> callOnDestroy);

}

template<typename... Args>
template<typename F, typename>
QOhosTaskPromise<Args...>::QOhosTaskPromise(
    F &&callable, std::function<void()> optOnDestroyedWithoutCall, std::string callerContextName)
    : m_callable(std::make_shared<CallableImpl<std::decay_t<F>>>(std::forward<F>(callable)))
    , m_tracking(
        std::make_shared<TrackingNode>(
            std::move(callerContextName), nullptr,
            std::make_shared<TrackingUsageState>(
                1,
                QtOhos::moveToSharedPtr(
                    optOnDestroyedWithoutCall
                        ? std::move(optOnDestroyedWithoutCall)
                        : makeQOhosNoOpConsumer<>()))))
{
}

template<typename... Args>
QOhosTaskPromise<Args...>::~QOhosTaskPromise()
{
    if (!m_tracking || m_tracking->usage->used) {
        return;
    }

    --m_tracking->usage->activeCount;

    if (m_tracking->usage->activeCount == 0) {
        m_tracking->logCallerContextChainAsErrors(Q_FUNC_INFO);
        qOhosPrintfError(
            "%s: promise destroyed without notifying the caller: %s",
            Q_FUNC_INFO, m_tracking->rootCallerContextOrUnknown().c_str());
        (*m_tracking->usage->onDestroyedWithoutCall)();
    }
}

template<typename... Args>
QOhosTaskPromise<Args...>::QOhosTaskPromise(
    std::shared_ptr<Callable> callable, std::shared_ptr<TrackingNode> tracking)
    : m_callable(std::move(callable))
    , m_tracking(std::move(tracking))
{
}

template<typename... Args>
void QOhosTaskPromise<Args...>::operator()(Args... args) const
{
    if (!m_tracking) {
        qOhosReportFatalErrorAndAbort("%s: called on moved-from instance", Q_FUNC_INFO);
    }

    if (!m_callable) {
        qOhosReportFatalErrorAndAbort("%s: called on empty QOhosTaskPromise", Q_FUNC_INFO);
    }

    m_tracking->markUsed(Q_FUNC_INFO);

    m_callable->call(std::forward<Args>(args)...);
}

template<typename... Args>
QOhosTaskPromise<Args...>::operator std::function<void(Args...)>() &&
{
    const auto funcInfo = Q_FUNC_INFO;

    if (!m_tracking) {
        qOhosReportFatalErrorAndAbort("%s: called on moved-from instance", funcInfo);
    }

    if (!m_callable) {
        return {};
    }

    m_tracking->markUsed(funcInfo);

    auto callable = std::move(m_callable);
    auto tracking = std::move(m_tracking);

    auto sharedCalledFlag = std::make_shared<bool>(false);
    auto destroyNotifier = QtOhos::makeDestroyNotifier(
        [sharedCalledFlag, tracking, funcInfo]() {
            if (!*sharedCalledFlag) {
                tracking->logCallerContextChainAsErrors(funcInfo);
                qOhosPrintfError(
                    "%s: promise (as std::function) destroyed without notifying the caller: %s",
                    funcInfo, tracking->rootCallerContextOrUnknown().c_str());
                (*tracking->usage->onDestroyedWithoutCall)();
            }
        });

    return [callable, sharedCalledFlag, tracking, funcInfo, destroyNotifier](Args... args) {
        if (*sharedCalledFlag) {
            qOhosReportFatalErrorAndAbort(
                "%s: promise (as std::function) called more than once for: %s",
                funcInfo, tracking->rootCallerContextOrUnknown().c_str());
        }
        *sharedCalledFlag = true;
        callable->call(std::forward<Args>(args)...);
    };
}

template<typename... Args>
QOhosTaskPromise<Args...> QOhosTaskPromise<Args...>::makeChained(std::string callerContextName) &&
{
    if (!m_tracking) {
        qOhosReportFatalErrorAndAbort("%s: called on moved-from instance", Q_FUNC_INFO);
    }

    m_tracking->markUsed(Q_FUNC_INFO);

    auto newUsage = std::make_shared<TrackingUsageState>(1, m_tracking->usage->onDestroyedWithoutCall);

    return QOhosTaskPromise(
        std::move(m_callable),
        std::make_shared<TrackingNode>(
            std::move(callerContextName), std::move(m_tracking), newUsage));
}

template<typename... Args>
template<typename... ContextNames>
std::array<QOhosTaskPromise<Args...>, sizeof...(ContextNames)>
QOhosTaskPromise<Args...>::makeBranched(ContextNames &&...branchContextNames) &&
{
    if (!m_tracking) {
        qOhosReportFatalErrorAndAbort("%s: called on moved-from instance", Q_FUNC_INFO);
    }

    m_tracking->markUsed(Q_FUNC_INFO);

    auto sharedUsage = std::make_shared<TrackingUsageState>(
        sizeof...(ContextNames), m_tracking->usage->onDestroyedWithoutCall);
    return {{
        QOhosTaskPromise(
            m_callable,
            std::make_shared<TrackingNode>(
                std::forward<ContextNames>(branchContextNames),
                m_tracking, sharedUsage))...
    }};
}

template<typename... Args>
std::pair<QOhosTaskPromise<Args...>, QOhosTaskPromise<Args...>>
QOhosTaskPromise<Args...>::makeThenCatchBranches(std::string callerContextName) &&
{
    auto branches = std::move(*this).makeChained(std::move(callerContextName)).makeBranched("then", "catch");
    return {std::move(branches[0]), std::move(branches[1])};
}

template<typename... Args>
template<typename Func>
template<typename F>
QOhosTaskPromise<Args...>::CallableImpl<Func>::CallableImpl(F &&f)
    : m_func(std::forward<F>(f))
{
}

template<typename... Args>
template<typename Func>
void QOhosTaskPromise<Args...>::CallableImpl<Func>::call(Args &&...args)
{
    m_func(std::forward<Args>(args)...);
}

template<typename... Args>
QOhosTaskPromise<Args...>::TrackingUsageState::TrackingUsageState(
    std::size_t activeCount, std::shared_ptr<std::function<void()>> onDestroyedWithoutCall)
    : onDestroyedWithoutCall(onDestroyedWithoutCall)
    , activeCount(activeCount)
{
}

template<typename... Args>
QOhosTaskPromise<Args...>::TrackingNode::TrackingNode(
    std::string callerContext, std::shared_ptr<TrackingNode> optParent,
    std::shared_ptr<TrackingUsageState> usage)
    : callerContext(std::move(callerContext))
    , optParent(std::move(optParent))
    , usage(std::move(usage))
{
}

template<typename... Args>
void QOhosTaskPromise<Args...>::TrackingNode::markUsed(const char *callerContextString)
{
    if (usage->used) {
        logCallerContextChainAsErrors(callerContextString);
        qOhosReportFatalErrorAndAbort(
            "%s: using a dead promise branch (%s) from this caller: %s",
            callerContextString, callerContext.c_str(), rootCallerContextOrUnknown().c_str());
    }
    usage->used = true;
}

template<typename... Args>
std::string QOhosTaskPromise<Args...>::TrackingNode::rootCallerContextOrUnknown() const
{
    const TrackingNode *rootContextNode = nullptr;
    for (auto currentNode = this; currentNode != nullptr; currentNode = currentNode->optParent.get()) {
        if (!currentNode->callerContext.empty()) {
            rootContextNode = currentNode;
        }
    }
    return rootContextNode != nullptr ? rootContextNode->callerContext : "<unknown>";
}

template<typename... Args>
void QOhosTaskPromise<Args...>::TrackingNode::logCallerContextChainAsErrors(const char *messagePrefix) const
{
    int index = 0;
    for (auto currentNode = this; currentNode; currentNode = currentNode->optParent.get()) {
        if (!currentNode->callerContext.empty()) {
            qOhosPrintfError(
                "%s [caller context #%d]: %s",
                messagePrefix, index, currentNode->callerContext.c_str());
            ++index;
        }
    }
}

template<typename FuncPtr, typename Invoker>
QOhosNamedFunc<FuncPtr, Invoker>::QOhosNamedFunc(Invoker invoker, const char *funcName)
    : m_invoker(std::move(invoker))
    , m_funcName(funcName)
{
}

template<typename FuncPtr, typename Invoker>
const char *QOhosNamedFunc<FuncPtr, Invoker>::name() const
{
    return m_funcName;
}

template<typename FuncPtr, typename Invoker>
template<typename... Args>
auto QOhosNamedFunc<FuncPtr, Invoker>::operator()(Args &&...args) const -> decltype(std::declval<const Invoker &>()(std::forward<Args>(args)...))
{
    return m_invoker(std::forward<Args>(args)...);
}

template<typename ...Ts>
void QOhosNoOpConsumer::operator()(const Ts &...) const
{
}

template<typename ...Ts>
QOhosConsumer<Ts...> makeQOhosNoOpConsumer()
{
    return [](const Ts &...) {
    };
}

inline QOhosNoOpConsumer makeQOhosNoOpConsumer()
{
    return QOhosNoOpConsumer();
}

template<typename T>
QOhosOptional<T>::QOhosOptional(const T &value)
{
    initializeStoredValue(value);
}

template<typename T>
QOhosOptional<T>::QOhosOptional(const QOhosOptional<void> &)
{
}

template<typename T>
QOhosOptional<T>::QOhosOptional(const QOhosOptional<T> &other)
{
    if (other.m_hasValue) {
        initializeStoredValue(other.storedValueRef());
    }
}

template<typename T>
QOhosOptional<T> &QOhosOptional<T>::operator=(const QOhosOptional<T> &other)
{
    if (&other == this) {
        return *this;
    }

    if (other.m_hasValue) {
        if (m_hasValue) {
            storedValueRef() = other.storedValueRef();
        } else {
            initializeStoredValue(other.storedValueRef());
        }
    } else {
        reset();
    }

    return *this;
}

template<typename T>
QOhosOptional<T> &QOhosOptional<T>::operator=(const T &value)
{
    *this = QOhosOptional<T>(value);
    return *this;
}

template<typename T>
QOhosOptional<T> &QOhosOptional<T>::operator=(const QOhosOptional<void> &)
{
    reset();
    return *this;
}

template<typename T>
QOhosOptional<T>::~QOhosOptional()
{
    reset();
}

template<typename T>
void QOhosOptional<T>::reset()
{
    if (m_hasValue) {
        m_hasValue = false;
        storedValueRef().~T();
    }
}

template<typename T>
T &QOhosOptional<T>::storedValueRef()
{
    return *reinterpret_cast<T *>(&m_rawStoredValue);
}

template<typename T>
const T &QOhosOptional<T>::storedValueRef() const
{
    return *reinterpret_cast<const T *>(&m_rawStoredValue);
}

template<typename T>
template<typename... InitArgs>
void QOhosOptional<T>::initializeStoredValue(InitArgs &&...initArgs)
{
    new (&m_rawStoredValue) T(std::forward<InitArgs...>(initArgs...));
    m_hasValue = true;
}

template<typename T>
bool QOhosOptional<T>::hasValue() const
{
    return m_hasValue;
}

template<typename T>
T QOhosOptional<T>::valueOr(const T &fallback) const
{
    return hasValue() ? storedValueRef() : fallback;
}

template<typename T>
const T &QOhosOptional<T>::value() const
{
    if (!m_hasValue) {
#ifndef QT_NO_EXCEPTIONS
        throw std::runtime_error("Can't access value inside empty QOhosOptional<>");
#else
        qFatal("Can't access value inside empty QOhosOptional<>");
        std::abort();
#endif
    }

    return storedValueRef();
}

template<typename T>
T &QOhosOptional<T>::value()
{
    if (!m_hasValue) {
#ifndef QT_NO_EXCEPTIONS
        throw std::runtime_error("Can't access value inside empty QOhosOptional<>");
#else
        qFatal("Can't access value inside empty QOhosOptional<>");
        std::abort();
#endif
    }

    return storedValueRef();
}

template<typename T>
template<typename... Args>
T &QOhosOptional<T>::emplace(Args &&...args)
{
    reset();
    initializeStoredValue(std::forward<Args...>(args)...);
    return storedValueRef();
}

template<typename T, typename U>
std::enable_if_t<qohoscommon_h_detail::hasEqualityComparator<T, U>, bool>
operator==(const QOhosOptional<T> &lhs, const QOhosOptional<U> &rhs)
{
    return (lhs.hasValue() && rhs.hasValue())
        ? lhs.value() == rhs.value()
        : (!lhs.hasValue() && !rhs.hasValue());
}

template<typename T, typename U>
std::enable_if_t<qohoscommon_h_detail::hasEqualityComparator<T, U>, bool>
operator!=(const QOhosOptional<T> &lhs, const QOhosOptional<U> &rhs)
{
    return !(lhs == rhs);
}

template<typename T, typename U>
std::enable_if_t<qohoscommon_h_detail::hasEqualityComparator<T, U>, bool>
operator==(const QOhosOptional<T> &lhs, const U &rhs)
{
    return lhs.hasValue() && lhs.value() == rhs;
}

template<typename T, typename U>
std::enable_if_t<qohoscommon_h_detail::hasEqualityComparator<T, U>, bool>
operator!=(const QOhosOptional<T> &lhs, const U &rhs)
{
    return !(lhs == rhs);
}

template<typename T, typename U>
std::enable_if_t<qohoscommon_h_detail::hasEqualityComparator<T, U>, bool>
operator==(const T &lhs, const QOhosOptional<U> &rhs)
{
    return rhs.hasValue() && lhs == rhs.value();
}

template<typename T, typename U>
std::enable_if_t<qohoscommon_h_detail::hasEqualityComparator<T, U>, bool>
operator!=(const T &lhs, const QOhosOptional<U> &rhs)
{
    return !(lhs == rhs);
}

template<typename T>
template<typename Func>
std::enable_if_t<
    qohoscommon_h_detail::isQOhosOptional<QOhosInvokeResult<Func, T>>,
    QOhosInvokeResult<Func, T>>
QOhosOptional<T>::andThen(Func &&func) const
{
    return hasValue() ? func(value()) : QOhosInvokeResult<Func, T>();
}

template<typename T>
template<typename Func>
QOhosOptional<QOhosInvokeResult<Func, T>> QOhosOptional<T>::transform(Func &&func) const
{
    using TransformedT = QOhosInvokeResult<Func, T>;
    return hasValue()
        ? QOhosOptional<TransformedT>(func(value()))
        : QOhosOptional<TransformedT>();
}

template<typename T>
QOhosSpan<T>::QOhosSpan(T *data, std::size_t size) Q_DECL_NOEXCEPT
    : m_data(data)
    , m_size(size)
{
}

template<typename T>
template<typename OtherT, typename>
QOhosSpan<T>::QOhosSpan(const QOhosSpan<OtherT> &other) Q_DECL_NOEXCEPT
    : m_data(other.data())
    , m_size(other.size())
{
}

template<typename T>
T *QOhosSpan<T>::begin() const Q_DECL_NOEXCEPT
{
    return m_data;
}

template<typename T>
T *QOhosSpan<T>::end() const Q_DECL_NOEXCEPT
{
    return m_data + m_size;
}

template<typename T>
T *QOhosSpan<T>::data() const Q_DECL_NOEXCEPT
{
    return m_data;
}

template<typename T>
std::size_t QOhosSpan<T>::size() const Q_DECL_NOEXCEPT
{
    return m_size;
}

template<typename T>
T &QOhosSpan<T>::operator[](std::size_t index) const
{
    return m_data[index];
}

template<typename T>
QOhosSpan<T> QOhosSpan<T>::subspan(std::size_t offset) const
{
    if (offset >= m_size) {
#ifndef QT_NO_EXCEPTIONS
        throw std::out_of_range("offset >= span size");
#else
        qFatal("offset >= span size");
        std::abort();
#endif
    }
    return QOhosSpan<T>(m_data + offset, m_size - offset);
}

template<typename T>
QOhosSpan<T> QOhosSpan<T>::subspan(std::size_t offset, std::size_t length) const
{
    if (offset >= m_size) {
#ifndef QT_NO_EXCEPTIONS
        throw std::out_of_range("offset >= span size");
#else
        qFatal("offset >= span size");
        std::abort();
#endif
    }
    return QOhosSpan<T>(m_data + offset, std::min(m_size - offset, length));
}

template<typename T>
QOhosSpan<const std::uint8_t> as_bytes(QOhosSpan<T> span) Q_DECL_NOEXCEPT
{
    return QOhosSpan<const std::uint8_t>(
        reinterpret_cast<const std::uint8_t *>(span.data()), span.size() * sizeof(T));
}

template<typename T>
QOhosSpan<T> makeQOhosSpan(T *data, std::size_t count)
{
    return QOhosSpan<T>(data, count);
}

template<typename T>
QOhosOptional<T> makeQOhosOptional(const T &value)
{
    return QOhosOptional<T>(value);
}

inline QOhosOptional<void> makeEmptyQOhosOptional()
{
    return {};
}

template<typename T>
QOhosMutexProtectedValue<T>::QOhosMutexProtectedValue() = default;

template<typename T>
template<typename ProcessFunc>
void QOhosMutexProtectedValue<T>::processValue(ProcessFunc &&processFunc)
{
    std::lock_guard<std::mutex> valueLock(m_valueMutex);
    std::forward<ProcessFunc>(processFunc)(m_value);
}

template<typename T>
template<typename EvalFunc>
auto QOhosMutexProtectedValue<T>::evalWithValue(EvalFunc &&evalFunc) const -> decltype(evalFunc(std::declval<const T &>()))
{
    std::lock_guard<std::mutex> valueLock(m_valueMutex);
    return std::forward<EvalFunc>(evalFunc)(m_value);
}

namespace QtOhos {

template<typename T>
std::shared_ptr<T> moveToSharedPtr(T &&obj)
{
    return std::make_shared<T>(std::forward<T>(obj));
}

template<typename T>
std::shared_ptr<T> makeSharedPtrWithAttachedExtraData(
    std::shared_ptr<T> baseSharedPtr, std::shared_ptr<void> extraData)
{
    auto *baseRawPtr = baseSharedPtr.get();
    return std::shared_ptr<T>(
        moveToSharedPtr(
            std::make_pair(
                std::move(baseSharedPtr),
                std::move(extraData))),
        baseRawPtr);
}

template<typename T>
std::weak_ptr<T> makeWeakPtr(const std::shared_ptr<T> &obj)
{
    return obj;
}

inline std::shared_ptr<void> makeDestroyNotifier(std::function<void()> callOnDestroy)
{
    class DestroyNotifier
    {
    public:
        explicit DestroyNotifier(std::function<void()> callOnDestroy)
            : callOnDestroy(std::move(callOnDestroy))
        {
        }

        DestroyNotifier(const DestroyNotifier &) = delete;
        DestroyNotifier(DestroyNotifier &&) = delete;
        DestroyNotifier &operator=(const DestroyNotifier &) = delete;
        DestroyNotifier &operator=(DestroyNotifier &&) = delete;

        ~DestroyNotifier()
        {
            callOnDestroy();
        };

    private:
        std::function<void()> callOnDestroy;
    };

    return std::make_shared<DestroyNotifier>(std::move(callOnDestroy));
}

}

QT_END_NAMESPACE

#endif
