// 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 IExecutionContext.h
//!
//! @brief Defines @ref omni::kit::exec::core::unstable::IExecutionContext.
#pragma once

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

namespace omni
{
namespace kit
{

struct StageUpdateSettings;

namespace exec
{
namespace core
{
namespace unstable
{

//! ABI-safe struct containing time information for graph execution.
struct ExecutionContextTime
{
    double currentTime; //!< Current execution time (within application timeline)
    double absoluteSimTime; //!< Absolute simulation time (from start of simulation)
    float elapsedSecs; //!< Time elapsed since last execution
    bool timeChanged; //!< Is this execution triggered by a time change
    char _padding; //!< Padding
    char _padding1; //!< Padding
    char _padding2; //!< Padding
};

static_assert(std::is_standard_layout<ExecutionContextTime>::value, "ExecutionContextTime is not ABI-safe");
static_assert(offsetof(ExecutionContextTime, currentTime) == 0, "unexpected offset");
static_assert(offsetof(ExecutionContextTime, absoluteSimTime) == 8, "unexpected offset");
static_assert(offsetof(ExecutionContextTime, elapsedSecs) == 16, "unexpected offset");
static_assert(offsetof(ExecutionContextTime, timeChanged) == 20, "unexpected offset");
static_assert(24 == sizeof(ExecutionContextTime), "unexpected size");

// forward declarations needed by interface declaration
class IExecutionContext;
class IExecutionContext_abi;
class IScheduleFunction;

//! @ref omni::kit::exec::core::unstable::IExecutionContext inherits all of the functionality of @ref
//! omni::graph::exec::unstable::IExecutionContext but adds information related to Kit.
class IExecutionContext_abi : public omni::core::Inherits<omni::graph::exec::unstable::IExecutionContext,
                                                          OMNI_TYPE_ID("omni.kit.exec.core.unstable.IExecutionContext")>
{
protected:
    //! Returns Kit's timing parameters for the current execution.
    virtual const ExecutionContextTime* getTime_abi() noexcept = 0;

    //! Returns Kit's update settings for the current execution.
    virtual const omni::kit::StageUpdateSettings* getUpdateSettings_abi() noexcept = 0;

    //! Schedule given function to execute at the end of current execution.
    virtual void schedulePostTask_abi(OMNI_ATTR("not_null") graph::exec::unstable::IScheduleFunction* fn) noexcept = 0;
};

} // namespace unstable
} // namespace core
} // namespace exec
} // namespace kit
} // namespace omni

// generated API declaration
#define OMNI_BIND_INCLUDE_INTERFACE_DECL
#include "IExecutionContext.gen.h"

//! @copydoc omni::kit::exec::core::unstable::IExecutionContext_abi
class omni::kit::exec::core::unstable::IExecutionContext
    : public omni::core::Generated<omni::kit::exec::core::unstable::IExecutionContext_abi>
{
public:
    //! Schedule given function to execute at the end of current execution.
    //!
    //! This inline implementation wraps lambda into IScheduleFunction.
    //!
    //! The supplied function should have the signature of `void()`.
    //!
    //! Returns true if function was scheduled. False when context is currently not executing and function won't be
    //! scheduled.
    template <typename FN>
    inline bool schedulePostTask(FN&& callback);
};

// additional headers needed for API implementation
#include <omni/graph/exec/unstable/IScheduleFunction.h>

template <typename FN>
inline bool omni::kit::exec::core::unstable::IExecutionContext::schedulePostTask(FN&& callback)
{
    class Forwarder : public graph::exec::unstable::Implements<graph::exec::unstable::IScheduleFunction>
    {
    public:
        Forwarder(FN&& fn) : m_fn(std::move(fn))
        {
        }

    protected:
        graph::exec::unstable::Status invoke_abi() noexcept override
        {
            m_fn();
            return graph::exec::unstable::Status::eSuccess;
        }

        FN m_fn;
    };

    if (inExecute())
    {
        schedulePostTask_abi(omni::core::steal(new Forwarder(std::forward<FN>(callback))).get());
        return true;
    }
    else
    {
        return false;
    }
}

// generated API implementation
#define OMNI_BIND_INCLUDE_INTERFACE_IMPL
#include "IExecutionContext.gen.h"
