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

#include <omni/core/ResultError.h>
#include <omni/graph/exec/unstable/IBase.h>
#include <omni/graph/exec/unstable/NodePartition.h>
#include <omni/graph/exec/unstable/Span.h>

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

// forward declarations needed by interface declaration
class IGraph;
class IGraphBuilder_abi;
class IGraphBuilder;
class IGraphBuilderContext;
class IGraphBuilderNode;
class INode;
class IDef;
class INodeDef;
class INodeGraphDef;
class ITopology;

//! Graph builder is the only class that has the ability to modify topology of a graph.
//!
//! Topological edits of the graph are only allowed during graph transformation and should never
//! be performed during execution of the graph. Construction of the builder will automatically drop
//! all the connections between nodes.
//!
//! Methods on this class mutating a graph topology are not thread-safe (unless documented otherwise)
class IGraphBuilder_abi
    : public omni::core::Inherits<omni::graph::exec::unstable::IBase, OMNI_TYPE_ID("omni.graph.exec.unstable.IGraphBuilder")>
{
protected:
    //! Return owner of all graphs this builder touches
    //!
    //! The returned @ref omni::graph::exec::unstable::IGraph will *not* have
    //! @ref omni::core::IObject::acquire() called before being returned.
    virtual OMNI_ATTR("no_acquire") IGraph* getGraph_abi() noexcept = 0;

    //! Returns the topology this builder can modify.
    //!
    //! The returned @ref omni::graph::exec::unstable::ITopology will *not* have
    //! @ref omni::core::IObject::acquire() called before being returned.
    virtual OMNI_ATTR("no_acquire") ITopology* getTopology_abi() noexcept = 0;

    //! Returns the context in which this builder works.
    //!
    //! The returned @ref omni::graph::exec::unstable::IGraphBuilderContext will *not* have
    //! @ref omni::core::IObject::acquire() called before being returned.
    virtual OMNI_ATTR("no_acquire") IGraphBuilderContext* getContext_abi() noexcept = 0;

    //! Returns @ref omni::graph::exec::unstable::INodeGraphDef this builder can modify.
    //!
    //! The returned @ref omni::graph::exec::unstable::INodeGraphDef will *not* have
    //! @ref omni::core::IObject::acquire() called before being returned.
    virtual OMNI_ATTR("no_acquire") INodeGraphDef* getNodeGraphDef_abi() noexcept = 0;

    //! Connect two given nodes.
    //!
    //! It is an error if the two nodes are not in the same topology.
    //!
    //! Neither given node should be @c nullptr.
    //!
    //! Neither @ref omni::graph::exec::unstable::INode have @ref omni::core::IObject::acquire() called
    //! during the connection process.
    //!
    //! May throw.
    virtual OMNI_ATTR("throw_result") omni::core::Result
        connect_abi(OMNI_ATTR("not_null, throw_if_null") INode* upstreamNode,
                    OMNI_ATTR("not_null, throw_if_null") INode* downstreamNode) noexcept = 0;

    //! Disconnect two given nodes.
    //!
    //! It is an error if the two nodes are not in the same topology.
    //!
    //! Neither given node should be @c nullptr.
    //!
    //! Neither @ref omni::graph::exec::unstable::INode have @ref omni::core::IObject::acquire() called
    //! during the disconnection process.
    //!
    //! May throw.
    virtual OMNI_ATTR("throw_result") omni::core::Result
        disconnect_abi(OMNI_ATTR("not_null, throw_if_null") INode* upstreamNode,
                       OMNI_ATTR("not_null, throw_if_null") INode* downstreamNode) noexcept = 0;

    //! Remove a node from topology.
    //!
    //! The given node must not be @c nullptr.
    //!
    //! May throw.
    virtual OMNI_ATTR("throw_result") omni::core::Result
        remove_abi(OMNI_ATTR("not_null, throw_if_null") INode* node) noexcept = 0;

    //! Sets the definition for given node.
    //!
    //! If a definition is already set, it will be replaced by the given definition.
    //!
    //! The given definition may be @c nullptr.
    //!
    //! @ref omni::core::IObject::acquire() is called on the given definition pointer.
    //!
    //! See also  @ref omni::graph::exec::unstable::IGraphBuilder::setNodeGraphDef().
    //!
    //! This method is NOT thread safe.
    virtual void setNodeDef_abi(OMNI_ATTR("not_null") INode* node, INodeDef* nodeDef) noexcept = 0;

    //! Sets the definition for give node.
    //!
    //! If a definition is already set, it will be replaced by the given definition.
    //!
    //! The given definition may be @c nullptr.
    //!
    //! @ref omni::core::IObject::acquire() is called on the given definition pointer.
    //!
    //! See also  @ref omni::graph::exec::unstable::IGraphBuilder::setNodeDef().
    //!
    //! This method is NOT thread safe.
    virtual void setNodeGraphDef_abi(OMNI_ATTR("not_null") INode* node, INodeGraphDef* nodeGraphDef) noexcept = 0;

    //! Unsets given node's definition.
    //!
    //! If the definition is already @c nullptr, this method does nothing.
    //!
    //! This method is NOT thread safe.
    virtual void clearDef_abi(OMNI_ATTR("not_null") INode* node) noexcept = 0;

    //! Replace well formed cluster of nodes with a single node and the given definition.
    //!
    //! All nodes must exist in the same and current topology, otherwise the entire operation is aborted.
    //!
    //! @ref omni::core::IObject::acquire() is called on the given definition pointer.
    //!
    //! This method is NOT thread safe.
    virtual void replacePartition_abi(OMNI_ATTR("in, not_null, throw_if_null, ref") const NodePartition* partition,
                                      OMNI_ATTR("not_null, throw_if_null") IDef* definition) noexcept = 0;

    //! Create a new node in current node graph def.
    //!
    //! The given node name must not be @c nullptr.
    //!
    //! The given node def can be @c nullptr.
    //!
    //! Node creation can return @c nullptr when current node graph def doesn't allow node construction outside
    //! of the pass that created it.
    //!
    //! The returned @ref omni::graph::exec::unstable::INode will have @ref omni::core::IObject::acquire() called on it.
    virtual INode* createNode_abi(OMNI_ATTR("in, not_null, throw_if_null, c_str") const char* name,
                                  IDef* def) noexcept = 0;

    //! Access created nodes by this builder.
    //!
    //! Span is no longer valid when topology of the graph changes. You need to query it again.
    //!
    //! In case a node once created gets removed by another pass, returned list will continue to have it.
    //! It is safe to do, because we do not delete underlying nodes until the next graph population.
    //! Checking if node is valid in current topology allows to filter out these cases.
    //!
    //! The pointers in the span are non owning, i.e. @ref omni::graph::exec::unstable::INode will not have
    //! @ref omni::core::IObject::acquire() called on it.
    virtual Span<INode* const> getCreatedNodes_abi() noexcept = 0;
};

//! Smart pointer managing an instance of @ref IGraphBuilder.
using GraphBuilderPtr = omni::core::ObjectPtr<IGraphBuilder>;

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

// generated API declaration
#define OMNI_BIND_INCLUDE_INTERFACE_DECL
#include <omni/graph/exec/unstable/IGraphBuilder.gen.h>

//! @copydoc omni::graph::exec::unstable::IGraphBuilder_abi
//!
//! @ingroup groupOmniGraphExecInterfaces
class omni::graph::exec::unstable::IGraphBuilder
    : public omni::core::Generated<omni::graph::exec::unstable::IGraphBuilder_abi>
{
};

// additional headers needed for API implementation
#include <omni/graph/exec/unstable/IGraphBuilderNode.h>
#include <omni/graph/exec/unstable/INode.h>
#include <omni/graph/exec/unstable/INodeDef.h>
#include <omni/graph/exec/unstable/INodeGraphDef.h>

// generated API implementation
#define OMNI_BIND_INCLUDE_INTERFACE_IMPL
#include <omni/graph/exec/unstable/IGraphBuilder.gen.h>
