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

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

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

// forward declarations needed by interface declaration
class IGraphBuilderNode;
class IGraphBuilderNode_abi;
class INode;
class INodeDef;
class INodeGraphDef;
class ITopology;

//! Describes a node @ref omni::graph::exec::unstable::IGraphBuilder can manipulate.
//!
//! Only @ref omni::graph::exec::unstable::IGraphBuilder should use @ref omni::graph::exec::unstable::IGraphBuilderNode.
//! One way to think about this interface is that it is a private interface used by
//! @ref omni::graph::exec::unstable::IGraphBuilder to connect instances of @ref omni::graph::exec::unstable::INode.
class IGraphBuilderNode_abi : public omni::core::Inherits<omni::graph::exec::unstable::IBase,
                                                          OMNI_TYPE_ID("omni.graph.exec.unstable.IGraphBuilderNode")>
{
protected:
    //! Adds the given node as a parent (i.e. upstream) of this node.
    //!
    //! @ref omni::core::IObject::acquire() is not called on the given node.  It is up to the calling code to ensure the
    //! node persists while in use by this interface.
    //!
    //! @p parent must not be @c nullptr.
    //!
    //! It is undefined behavior to add a parent multiple times to a node.
    //!
    //! This method is not thread safe.
    //!
    //! May throw.
    virtual OMNI_ATTR("throw_result") omni::core::Result
        _addParent_abi(OMNI_ATTR("not_null, throw_if_null") IGraphBuilderNode* parent) noexcept = 0;

    //! Removes the given node as a parent.
    //!
    //! If given node is not a parent, this method returns success.
    //!
    //! This method is not thread safe.
    //!
    //! May throw.
    virtual OMNI_ATTR("throw_result") omni::core::Result
        _removeParent_abi(OMNI_ATTR("not_null, throw_if_null") IGraphBuilderNode* parent) noexcept = 0;

    //! Adds the given node as a child (i.e. downstream) of this node.
    //!
    //! @ref omni::core::IObject::acquire() is not called on the given node.  It is up to the calling code to ensure the
    //! node persists while in use by this interface.
    //!
    //! @p child must not be @c nullptr.
    //!
    //! It is undefined behavior to add a child multiple times to a node.
    //!
    //! This method is not thread safe.
    //!
    //! May throw.
    virtual OMNI_ATTR("throw_result") omni::core::Result
        _addChild_abi(OMNI_ATTR("not_null, throw_if_null") IGraphBuilderNode* child) noexcept = 0;

    //! Removes the given node as a child.
    //!
    //! If given node is not a child, this method returns success.
    //!
    //! This method is not thread safe.
    //!
    //! May throw.
    virtual OMNI_ATTR("throw_result") omni::core::Result
        _removeChild_abi(OMNI_ATTR("not_null, throw_if_null") IGraphBuilderNode* child) noexcept = 0;

    //! Remove from the container parent nodes that no longer exist in current topology, i.e are invalid.
    //!
    //! @ref omni::core::IObject::release() is not called on the invalid nodes.
    //!
    //! This method is not thread safe.
    virtual void _removeInvalidParents_abi() noexcept = 0;

    //! Remove from the container child nodes that no longer exist in current topology, i.e are invalid.
    //!
    //! @ref omni::core::IObject::release() is not called on the invalid nodes.
    //!
    //! This method is not thread safe.
    virtual void _removeInvalidChildren_abi() noexcept = 0;

    //! Invalidate all children and parents connections by invalidating the topology this node is sync with.
    //!
    //! This method is thread safe.
    virtual void _invalidateConnections_abi() noexcept = 0;


    //! Sets the number of parents who are a part of cycle.
    //!
    //! This method is not thread safe.
    virtual void setCycleParentCount_abi(uint32_t count) noexcept = 0;

    //! Sets the definition for this 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 pointer.
    //!
    //! See also  @ref omni::graph::exec::unstable::IGraphBuilderNode::_setNodeGraphDef().
    //!
    //! This method is not thread safe.
    virtual void _setNodeDef_abi(INodeDef* nodeDef) noexcept = 0;

    //! Sets the definition for this 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 pointer.
    //!
    //! See also  @ref omni::graph::exec::unstable::IGraphBuilderNode::_setNodeDef().
    //!
    //! This method is not thread safe.
    virtual void _setNodeGraphDef_abi(INodeGraphDef* nodeGraphDef) noexcept = 0;

    //! Unsets this node's definition.
    //!
    //! If the definition is already @c nullptr, this method does nothing.
    //!
    //! This method is not thread safe.
    virtual void _clearDef_abi() noexcept = 0;

    //! Access the topology owning this node.
    //!
    //! The returned @ref omni::graph::exec::unstable::ITopology will *not* have
    //! @ref omni::core::IObject::acquire() called before being returned.
    //!
    //! This method is not thread safe.
    virtual OMNI_ATTR("no_acquire") ITopology* getTopology_abi() noexcept = 0;

    //! Make topology valid for current topology version. Drop all the connections if topology changed.
    //!
    //! This method is not thread safe.
    virtual void validateOrResetTopology_abi() noexcept = 0;

    //! Access parent at the given index.
    //!
    //! If the given index is greater than the parent count, an error is returned.
    //!
    //! This method is not thread safe.
    //!
    //! May throw due to internal casting.
    //!
    //! See @ref omni::graph::exec::unstable::IGraphBuilderNode::getParentCount().
    //!
    //! Consider using @ref omni::graph::exec::unstable::IGraphBuilderNode::getParents()
    //! for a modern C++ wrapper to this method.
    //!
    //! The returned @ref omni::graph::exec::unstable::IGraphBuilderNode will *not* have
    //! @ref omni::core::IObject::acquire() called before being returned.
    virtual OMNI_ATTR("throw_result") omni::core::Result
        getParentAt_abi(uint64_t index,
                        OMNI_ATTR("not_null, throw_if_null, out, *no_acquire, *return")
                            IGraphBuilderNode** out) noexcept = 0;

    //! Returns the number of parents.
    //!
    //! This method is not thread safe.
    virtual uint64_t getParentCount_abi() noexcept = 0;

    //! Access child at the given index.
    //!
    //! If the given index is greater than the parent count, an error is returned.
    //!
    //! This method is not thread safe.
    //!
    //! May throw due to internal casting.
    //!
    //! See @ref omni::graph::exec::unstable::IGraphBuilderNode::getChildCount().
    //!
    //! Consider using @ref omni::graph::exec::unstable::IGraphBuilderNode::getChildren()
    //! for a modern C++ wrapper to this method.
    //!
    //! The returned @ref omni::graph::exec::unstable::IGraphBuilderNode will *not* have
    //! @ref omni::core::IObject::acquire() called before being returned.
    virtual OMNI_ATTR("throw_result") omni::core::Result
        getChildAt_abi(uint64_t index,
                       OMNI_ATTR("not_null, throw_if_null, out, *no_acquire, *return")
                           IGraphBuilderNode** out) noexcept = 0;

    //! Returns the number of children.
    //!
    //! This method is not thread safe.
    virtual uint64_t getChildCount_abi() noexcept = 0;

    //! Returns @c true if the given node is an immediate child of this node.
    //!
    //! @p node may be @c nullptr.
    //!
    //! This method is not thread safe.
    virtual bool hasChild_abi(IGraphBuilderNode* node) noexcept = 0;

    //! Returns @c true if this node is the root of the topology.
    //!
    //! This method is not thread safe.
    virtual bool isRoot_abi() noexcept = 0;

    //! Returns the root node of the topology of which this node is a part.
    //!
    //! This method is not thread safe.
    //!
    //! May throw due to internal casting.
    //!
    //! The returned @ref omni::graph::exec::unstable::IGraphBuilderNode will *not* have
    //! @ref omni::core::IObject::acquire() called before being returned.
    virtual omni::core::Result getRoot_abi(OMNI_ATTR("not_null, throw_if_null, out, *no_acquire")
                                               IGraphBuilderNode** out) noexcept = 0;
};

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

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

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

//! @copydoc omni::graph::exec::unstable::IGraphBuilderNode_abi
//!
//! @ingroup groupOmniGraphExecInterfaces
class omni::graph::exec::unstable::IGraphBuilderNode
    : public omni::core::Generated<omni::graph::exec::unstable::IGraphBuilderNode_abi>
{
public:
    //! Implementation detail to access parent ABI.
    struct GetParent
    {
        //! Access element at a given index
        static void getAt(IGraphBuilderNode* owner, uint64_t index, IGraphBuilderNode** out)
        {
            *out = owner->getParentAt(index);
        }

        //! Returns element count
        static uint64_t getCount(IGraphBuilderNode* owner)
        {
            return owner->getParentCount();
        }
    };

    //! Implementation detail to access children ABI.
    struct GetChild
    {
        //! Access element at a given index
        static void getAt(IGraphBuilderNode* owner, uint64_t index, IGraphBuilderNode** out)
        {
            *out = owner->getChildAt(index);
        }

        //! Returns element count
        static uint64_t getCount(IGraphBuilderNode* owner)
        {
            return owner->getChildCount();
        }
    };

    //! Implementation details that wraps index-based node access with iterators.
    using Parents = detail::ElementAt<IGraphBuilderNode, IGraphBuilderNode*, GetParent>;

    //! Implementation details that wraps index-based node access with iterators.
    using Children = detail::ElementAt<IGraphBuilderNode, IGraphBuilderNode*, GetChild>;

    //! Returns an object that allows the list of parents to be iterated over (i.e. using range-based for loops).
    Parents getParents() noexcept
    {
        return Parents(this);
    }

    //! Returns an object that allows the list of children to be iterated over (i.e. using range-based for loops).
    Children getChildren() noexcept
    {
        return Children(this);
    }

    //! Returns the root node of the topology of which this node is a part.
    //!
    //! May throw.
    inline IGraphBuilderNode* getRoot();
};

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

inline omni::graph::exec::unstable::IGraphBuilderNode* omni::graph::exec::unstable::IGraphBuilderNode::getRoot()
{
    IGraphBuilderNode* out;
    OMNI_THROW_IF_FAILED(getRoot_abi(&out));
    return out;
}

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