// 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 NodeDef.h
//!
//! @brief Declares @ref omni::graph::core::unstable::IGenericNodeGraphDef.
#pragma once

#include <carb/profiler/Profile.h>

#include <omni/String.h>
#include <omni/graph/core/IInternal.h>
#include <omni/graph/core/iComputeGraph.h>
#include <omni/graph/core/unstable/IGenericNodeGraphDef.h>
#include <omni/graph/exec/unstable/NodeGraphDef.h>
#include <omni/inspect/IInspectJsonSerializer.h>

namespace omni
{
namespace graph
{
namespace core
{
namespace unstable
{

//! @copydoc omni::graph::exec::unstable::IGenericNodeGraphDef
class GenericNodeGraphDef : public exec::unstable::NodeGraphDefT<IGenericNodeGraphDef, exec::unstable::INodeGraphDefDebug>
{
public:
    //! Construct generic OmniGraph definition.
    //!
    //! This class is a base class for all OmniGraph node graph definitions and will use provided executor.
    //!
    //! @param  builder  Builder constructing the graph
    //! @param  executorFactory  Executor factory to use for this instance of the graph
    //! @param  definitionName  Definition name is considered as a token that transformation passes can register against
    //! @param  graphObj  Authoring graph associated with this definition
    //! @param  isInstanced  Is this an instanced OmniGraph
    //!
    //! May throw.
    static omni::core::ObjectPtr<GenericNodeGraphDef> create(exec::unstable::IGraphBuilder* builder,
                                                             exec::unstable::ExecutorFactory executorFactory,
                                                             const char* definitionName,
                                                             const GraphObj& graphObj,
                                                             bool isInstanced)
    {
        OMNI_THROW_IF_ARG_NULL(builder);
        OMNI_THROW_IF_ARG_NULL(definitionName);
        return omni::core::steal(new GenericNodeGraphDef(builder, executorFactory, definitionName, graphObj, isInstanced));
    }

    void build_abi(exec::unstable::IGraphBuilder* builder) noexcept override
    {
        // make sure the topology of the graph is valid...this may not be the case if graph is empty
        // because if happens lazily when requesting root node
        getRoot()->validateOrResetTopology();

        // attach to authoring graph
        m_privateDef->attachToAuthoring(this);
    }

    bool inspect_abi(inspect::IInspector* inspector) noexcept override
    {
        return true;
    }

    static omni::string getDefinitionName(const GraphObj& graphObj)
    {
        static const omni::string sDefNamePrefix = "og.def.graph_";

        return sDefNamePrefix + graphObj.iGraph->getEvaluatorName(graphObj);
    }

protected:
    using BaseType = exec::unstable::NodeGraphDefT<IGenericNodeGraphDef, exec::unstable::INodeGraphDefDebug>;

    GraphObj getAuthoringGraph_abi() noexcept override
    {
        return m_privateDef->getAuthoringGraph();
    }

    bool isInstanced_abi() noexcept override
    {
        return m_privateDef->isInstanced();
    }

    exec::unstable::INode* getExecutionNode_abi(const NodeObj* nodeObj) noexcept override
    {
        return nullptr;
    }

    exec::unstable::Status preExecute_abi(exec::unstable::ExecutionTask* info) noexcept override
    {
        return m_privateDef->preExecute(*info);
    }

    exec::unstable::Status postExecute_abi(exec::unstable::ExecutionTask* info) noexcept override
    {
        return m_privateDef->postExecute(*info);
    }

    void getInstanceInfo_abi(omni::graph::exec::unstable::ExecutionTask const* info, InstanceIndex* retBaseInstanceIndex, size_t* retNumberOfInstances) noexcept override
    {
        m_privateDef->getInstanceInfo(*info, *retBaseInstanceIndex, *retNumberOfInstances);
    }

    GenericNodeGraphDef(exec::unstable::IGraphBuilder* builder,
                        exec::unstable::ExecutorFactory executorFactory,
                        const char* definitionName,
                        const GraphObj& graphObj,
                        bool isInstanced)
        : BaseType(builder->getGraph(), executorFactory, definitionName) // may throw
          ,
          m_privateDef(
              carb::getCachedInterface<omni::graph::core::IInternal>()->createPrivateGraphDef(graphObj, isInstanced),
              omni::core::kSteal)
    {
    }

private:
    omni::core::ObjectPtr<IPrivateNodeGraphDef> m_privateDef; //!< Internal object, hiding implementation details
                                                              //!< relying on OG internals
};

} // namespace unstable
} // namespace core
} // namespace graph
} // namespace omni
