// Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

//! @file ScheduleFunction.h
//!
//! @brief Helpers for @ref omni::graph::exec::unstable::IScheduleFunction.
#pragma once

#include <omni/graph/exec/unstable/IScheduleFunction.h>

namespace omni
{
namespace graph
{
namespace exec
{
namespace unstable
{
namespace detail
{

#ifndef DOXYGEN_BUILD

template <typename Fn>
struct ScheduleFunctionHelper
{
    static omni::graph::exec::unstable::Status invoke(Fn&& fn)
    {
        return fn();
    }

    static auto capture(Fn&& fn)
    {
        return std::move(fn);
    }
};

template <>
struct ScheduleFunctionHelper<IScheduleFunction*&>
{
    static omni::graph::exec::unstable::Status invoke(IScheduleFunction* fn)
    {
        return fn->invoke();
    }

    static auto capture(IScheduleFunction* fn)
    {
        return omni::core::borrow(fn);
    }
};

template <>
struct ScheduleFunctionHelper<omni::core::ObjectPtr<IScheduleFunction>&>
{
    static omni::graph::exec::unstable::Status invoke(omni::core::ObjectPtr<IScheduleFunction>& fn)
    {
        return fn->invoke();
    }

    static omni::core::ObjectPtr<IScheduleFunction> capture(omni::core::ObjectPtr<IScheduleFunction>& fn)
    {
        return std::move(fn);
    }
};

#endif

} // namespace detail

//! Helper function to efficiently call an invocable object (i.e. std::function, function ptr, IScheduleFunction*).
template <typename Fn>
omni::graph::exec::unstable::Status invokeScheduleFunction(Fn&& fn)
{
    return detail::ScheduleFunctionHelper<Fn>::invoke(std::forward<Fn>(fn));
}

//! Helper function to efficiently capture an invocable object (i.e. std::function, function ptr, IScheduleFunction*).
//!
//! Suitable when capturing the invocable object in a lambda to be passed to a scheduler.
//!
//! Use this function when an @ref IScheduleFunction will be invoked at a later time by a scheduler.  This function will
//! call @ref omni::core::IObject::acquire() on the @ref IScheduleFunction.
//!
//! If an invocable object that is not a @ref IScheduleFunction is passed to this function, @c std::move() will be
//! called.
template <typename Fn>
auto captureScheduleFunction(Fn&& fn) -> decltype(detail::ScheduleFunctionHelper<Fn>::capture(std::forward<Fn>(fn)))
{
    return detail::ScheduleFunctionHelper<Fn>::capture(std::forward<Fn>(fn));
}

} // namespace unstable
} // namespace exec
} // namespace graph
} // namespace omni
