
// Copyright (c) 2020-2021, 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.
//
#include <OgnTutorialStateDatabase.h>
#include <atomic>

// Implementation of a C++ OmniGraph node that uses internal state information to compute outputs.
class OgnTutorialState
{
    // ------------------------------------------------------------
    // The presence of these members will be detected when a node of this type is being instantiated, at which
    // time an instance of this node will be attached to the OmniGraph node and be made available in the
    // database class as the member "db.internalState<OgnTutorialState>()".
    //

    // Start all nodes with a monotinic increment value of 0
    size_t mIncrementValue{ 0 };

    // ------------------------------------------------------------
    // You can also define node-type static data, although you are responsible for dealing with any
    // thread-safety issues that may arise from accessing it from multiple threads (or multiple hardware)
    // at the same time. In this case it is a single value that is read and incremented so an atomic
    // variable is sufficient. In real applications this would be a complex structure, potentially keyed off
    // of combinations of inputs or real time information, requiring more stringent locking.
    //
    // This value increases for each node and indicates the value from which a node's own internal state value
    // increments. e.g. the first instance of this node type will start its state value at 1, the second instance at 2,
    // and so on...
    static std::atomic<size_t> sStartingValue;
public:
    // Operation of the state mechanism relies on the presence of a default constructor. If you don't have to do
    // any initialization you can let the compiler provide the default implementation.
    OgnTutorialState()
    {
        mIncrementValue = sStartingValue;
        // Update the per-class state information for the next node. The values go up by 100 to make it easier for
        // tests to compare the state information on different nodes.
        sStartingValue += 100;
    }

    // Helper function to update the node's internal state based on the previous values and the per-class state.
    // You could also do this in-place in the compute() function; pulling it out here makes the state manipulation
    // more explicit.
    void updateState()
    {
        mIncrementValue += 1;
    }

public:
    // Compute the output based on inputs and internal state
    static bool compute(OgnTutorialStateDatabase& db)
    {

        // This illustrates how internal state and inputs can be used in conjunction. The inputs can be used
        // to divert to a different computation path.
        if (db.inputs.override())
        {
            db.outputs.monotonic() = db.inputs.overrideValue();
        }
        else
        {
            // OmniGraph ensures that the database contains the correct internal state information for the node
            // being evaluated. Beyond that it has no knowledge of the data within that state.
            auto& state = db.internalState<OgnTutorialState>();
            db.outputs.monotonic() = state.mIncrementValue;

            // Update the node's internal state data for the next evaluation.
            state.updateState();
        }

        return true;
    }
};

std::atomic<size_t> OgnTutorialState::sStartingValue{ 0 };

REGISTER_OGN_NODE()
