// Copyright (c) 2019-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.
//
#pragma once

#include "iComputeGraph.h"
#include "INodeCategories.h"
#include "unstable/INodeTypeForwarding.h"
#include "IBundleFactory.h"
#include "IDirtyID.h"
#include "IBundleChanges.h"

#include <carb/Interface.h>
#include <carb/tasking/TaskingTypes.h>

#include <omni/core/IObject.h>

namespace rtx
{
namespace resourcemanager
{
class RpResource;
typedef uint32_t SyncScopeId;
}
}

namespace carb
{
namespace graphics
{
struct Semaphore;
}
}

namespace gpu
{
namespace rendergraph
{
//! Declare the IRenderGraph interface definition
OMNI_DECLARE_INTERFACE(IRenderGraph);
using RenderGraph = omni::core::ObjectPtr<gpu::rendergraph::IRenderGraph>;
}
enum class GfResult: int32_t;
}

namespace omni
{
namespace usd
{
class IUsdMutex;
using PathH = uint64_t;
namespace hydra
{
struct ViewportHydraRenderResults;
}
}

namespace kit
{
struct StageUpdateSettings;
}

namespace graph
{
namespace core
{

/** used only by kit - do not use this interface in plugins */
struct ComputeGraph
{
    CARB_PLUGIN_INTERFACE("omni::graph::core::ComputeGraph", 2, 8)

    /**
     * Returns the number of global orchestration graphs for all stages of the graph pipelines
     *
     * Note: from version 2.3 on, the semantics of this call has changed, even if the interface has not.
     * There is now a orchestration graph which has nodes that each wrap either a graph or an extension to be run
     * This function will now return the number of such orchestration graphs.  The non-orchestration global graphs
     * can be retrieved by iterating over the nodes of the orchestration graph and calling getWrappedGraph
     *
     * @return the number of graphs in the system, regardless of pipeline stage
     */
    size_t(CARB_ABI* getGraphCount)();

    /**
     * Fills the buffer with global orchestration graph objects that occupy all pipeline stages
     *
     * Note: From version 2.3 on, see note above about the global orchestration graph with nodes that
     *       wrap previous global graphs
     *
     * @param[out] graphObjs The buffer of graphs to fill
     * @param[in] bufSize The size of the buffer in terms of the number of GraphObj it has room for
     * @return true on success, false on failure
     */

    bool(CARB_ABI* getGraphs)(GraphObj* contextBuf, size_t bufSize);

    /**
     * Returns the number of graph contexts for all pipeline stages
     *
     * Note: From version 2.3 on, see note above about the global orchestration graph with nodes that
     * wrap previous global graphs
     *
     * @return the number of graph contexts in the whole system, regardless of pipeline stage
     */
    size_t(CARB_ABI* getGraphContextCount)();

    /**
     * Fills the buffer with graph context objects that occupy all pipeline stages
     *
     * Note: From version 2.3 on, see note above about the global orchestration graph with nodes
     *       that wrap previous global graphs
     *
     * @param[out] contextBuf The buffer of graph contexts to fill
     * @param[in] bufSize The size of the buffer in terms of the number of GraphContextObj it has room for
     * @return true on success, false on failure
     */
    bool(CARB_ABI* getGraphContexts)(GraphContextObj* contextBuf, size_t bufSize);

    /**
     * @brief Shut down all of the compute graph infrastructure
     *
     * The functionality in this method was originally introduced because we didn't have carbOnPluginShutdown working
     * properly (it's not being called for some reason), but later we decided to keep it because DS needs more control
     * over OG's startup and shutdown.
     */
    void(CARB_ABI* shutdownComputeGraph)();

    /**
     * @brief Start up all of the compute graph infrastructure
     *
     * Needed by DS to have more control over OG startup / shutdown mechanism
     */
    void(CARB_ABI* startupComputeGraph)();

    /**
     * @brief Attach OmniGraph to the given stage
     *
     * @param[in] stageId ID of the stage to which OmniGraph should attach
     * @param[in] metersPerUnit Length units of the stage
     * @param[in] userData Extra raw data to use for and identify the attachment
     *
     */
    void(CARB_ABI* attach)(long int stageId, double metersPerUnit, void* userData);

    /**
     * @brief Detach OmniGraph using the information in the @p userData
     *
     * @param[in] userData Extra raw data to use for and identify the detachment
     *
     */
    void(CARB_ABI* detach)(void* userData);

    /**
     * @deprecated This version is deprecated and will be removed in a future version.
     * Use updateV2() instead.
     */
    void(CARB_ABI* update)(float currentTime,
                           float elapsedSecs,
                           const omni::kit::StageUpdateSettings* updateSettings,
                           void* userData);

    /**
     * @deprecated This version is deprecated and will be removed in a future version.
     * Use updateSimStepWithUsd() instead.
     */
    void(CARB_ABI* updateSimStep)(int64_t timeNS, carb::tasking::Counter*);

    /**
     * @brief Enable of disable the attachment of OmniGraph to the Kit update looop
     *
     * Currently there is a dependency in SimStageWithHistory on OG, so we cannot shutdown the graph
     * when it needs to be ticked from external process, such as DS2's ISimStep.
     * Using this method we can block (or unblock) OG from reacting to Kit's update loop.
     *
     * @param[in] state Whether the Kit update loop should be respected or not
     */
    void(CARB_ABI* considerKitUpdateLoop)(bool state);

    /**
     * @brief postRenderBegin is called after IHydraEngine::render() after the postRender starts
     * in order to tick graphs in the postRender pipeline stage.
     *
     * @param[in] syncScope ID for the resource manager's sync scope
     * @param[in] renderGraph Graph that will be processed in the postRender
     */
    void(CARB_ABI* postRenderBegin)(rtx::resourcemanager::SyncScopeId syncScope,
                                    gpu::rendergraph::RenderGraph renderGraph);

    /**
     * @brief postRenderBegin is called after IHydraEngine::render() during the postRender update
     * in order to tick graphs in the postRender pipeline stage.
     *
     * @param[in] syncScope ID for the resource manager's sync scope
     * @param[in] renderResults Return value from IHydraEngine::render()
     * @param[in] renderProductPrimPath Prim path of the RenderProduct for this view
     * @param[in] simTime Kit's simulation time that's passed to ComputeGraphImpl::updateV2()
     * @param[in] hydraTime The current time value of the USD Stage

     */
    void(CARB_ABI* postRenderUpdate)(rtx::resourcemanager::SyncScopeId syncScope,
                                     omni::usd::hydra::ViewportHydraRenderResults* renderResults,
                                     omni::usd::PathH renderProductPrimPath,
                                     double simTime,
                                     double hydraTime);

    /**
     * @brief postRenderBegin is called after IHydraEngine::render() after the postRender ends
     * in order to tick graphs in the postRender pipeline stage.
     *
     * @param[in] syncScope ID for the resource manager's sync scope
     * @return The status of the postRender
     */
    gpu::GfResult(CARB_ABI* postRenderEnd)(rtx::resourcemanager::SyncScopeId syncScope);

    /**
     * Returns the number of global orchestration graphs given a particular graph pipeline stage.
     *
     * @param[in] pipelineStage The stage of the pipeline (simulation, pre-render, post-render)
     * @return the number of graphs in that pipeline stage
     */
    size_t(CARB_ABI* getGraphCountInPipelineStage)(GraphPipelineStage pipelineStage);

    /**
     * Fills the buffer with global orchestration graph objects that occupy a particular pipeline stage
     *
     * @param[out] graphObjs The buffer of graphs to fill
     * @param[in] bufSize The size of the buffer in terms of the number of GraphObj it has room for
     * @param[in] pipelineStage The stage of the pipeline (simulation, pre-render, post-render)
     * @return true on success, false on failure
     */
    bool(CARB_ABI* getGraphsInPipelineStage)(GraphObj* graphObjs, size_t bufSize, GraphPipelineStage pipelineStage);

    /**
     * @brief This update function is used by DriveSim's ISimStep interface to tick the graph
     *
     * @param[in] timeNS Simulation time, in nanoseconds
     * @param[in] counter Tasking counter
     * @param[in] disableUsdUpdates Turn off USD updates while the simulation step is happening
     */
    void(CARB_ABI* updateSimStepUsd)(int64_t timeNS, carb::tasking::Counter* counter, bool disableUsdUpdates);

    /**
     * @brief Gets the interface object handling the node categories
     *
     * @return Raw object that implements the node category interface
     */
    INodeCategories*(CARB_ABI* getNodeCategoriesInterface)();

    /**
     * @brief Gets an ONI object for the interface handling the node categories
     *
     * @return Shared object that implements the node category interface
     */
    inline omni::core::ObjectPtr<INodeCategories> getNodeCategoriesInterfacePtr() const
    {
        return omni::core::steal(getNodeCategoriesInterface());
    }

    /**
     * @brief Sets the test failure state. This is for test failures that cannot be caught by conventional means.
     *
     * @param[in] hasFailed If true then increment the test failure count, otherwise reset it to 0.
     */
    void (CARB_ABI* setTestFailure)(bool hasFailed);

    /**
     * @return Returns the current test failure count since it was last cleared.
     */
    size_t (CARB_ABI* testFailureCount)();

    /**
     * @brief Gets an ONI object for the interface handling the bundle factory
     *
     * @return Shared object that implements the bundle factory interface
     */
    IBundleFactory*(CARB_ABI* getBundleFactoryInterface)();

    /**
     * @brief Gets an ONI object for the interface handling the bundle factory
     *
     * @return Shared object that implements the bundle factory interface
     */
    inline omni::core::ObjectPtr<IBundleFactory> getBundleFactoryInterfacePtr() const
    {
        return omni::core::steal(getBundleFactoryInterface());
    }

    /**
     * @brief Updates the graphs
     *
     * @param[in] currentTime Time at which the graphs are being updated
     * @param[in] elapsedSecs Amount of time elapsed in overall graph updates
     * @param[in] absoluteSimTime Time at which the simulation graph updates
     * @param[in] updateSettings Any settings required by the update
     * @param[in] userData Raw user data to pass to the update
     */
    void(CARB_ABI* updateV2)(double currentTime,
                             float elapsedSecs,
                             double absoluteSimTime,
                             const omni::kit::StageUpdateSettings* updateSettings,
                             void* userData);

    /**
     * @brief preRenderBegin is called before IHydraEngine::render() before the preRender starts
     * in order to tick graphs in the prerender pipeline stage.
     *
     * @param[in] renderGraph Render graph that is about to be ticked
     */
    void(CARB_ABI* preRenderBegin)(gpu::rendergraph::RenderGraph renderGraph);

    /**
     * @brief preRenderUpdate is called before IHydraEngine::render() when the preRender updates
     * in order to tick graphs in the prerender pipeline stage.
     *
     * @param[in] simTime Time at which the render graph is being updated according to the simulation graph
     * @param[in] hydraTime Time at which the render graph is being updated according to Hydra
     * @param[in] mutex Mutex for locking USD while updating
     */
    void(CARB_ABI* preRenderUpdate)(double simTime,
                                    double hydraTime,
                                    omni::usd::IUsdMutex& mutex);

    /**
     * @brief preRenderEnd is called before IHydraEngine::render() after the preRender is done
     * in order to tick graphs in the prerender pipeline stage.
     */
    void(CARB_ABI* preRenderEnd)();


    /**
     * Flushes any pending USD changes from the fabric scene delegate.
     */
    void(CARB_ABI* flushUsd)();

    /**
     * @brief Gets the interface object handling the node type forwarding
     *
     * @return Shared object that implements the node type forwarding interface
     */
    unstable::INodeTypeForwarding*(CARB_ABI* getNodeTypeForwardingInterface)();
    inline omni::core::ObjectPtr<unstable::INodeTypeForwarding> getNodeTypeForwardingInterfacePtr() const
    {
        return omni::core::steal(getNodeTypeForwardingInterface());
    }

    unstable::IDirtyID2*(CARB_ABI* getDirtyIDInterface)(GraphContextObj const& context);
    omni::core::ObjectPtr<unstable::IDirtyID2> getDirtyIDInterfacePtr(GraphContextObj const& context) const
    {
        return omni::core::steal(getDirtyIDInterface(context));
    }

    IBundleChanges*(CARB_ABI* getBundleChangesInterface)(GraphContextObj const& context);
    omni::core::ObjectPtr<IBundleChanges> getBundleChangesInterfacePtr(GraphContextObj const& context) const
    {
        return omni::core::steal(getBundleChangesInterface(context));
    }
};
// Update this every time a new ABI function is added, to ensure one isn't accidentally added in the middle
STRUCT_INTEGRITY_CHECK(ComputeGraph, getBundleChangesInterface, 28)

}
}
}
