// Copyright (c) 2022, 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 <OgnTestGracefulShutdownDatabase.h>
#include <omni/graph/core/ComputeGraph.h>

namespace omni {
namespace graph {
namespace test {

// Specialty node used to test the graceful shutdown process for extensions that ensures that node states have been
// destroyed before the shutdown begins (to avoid static object destruction order problems, where the state might
// might be accessing data within the extension).
//
// Exceptions are thrown instead of the standard error logging so that the test scripts can detect when there is a problem.
//
class OgnTestGracefulShutdown
{
public:
    // Static class member that will be set to false when the extension loads and true when the extension is
    // unloading in order to flag that information for the state object to check as part of the test.
    static bool sShutdownStarted;

    // The class member makes this class behave as internal per-node state. The state will use this node
    // type's registration as an indication that the shutdown is happening in the proper order.
    NodeTypeObj m_myNodeTypeObj{ nullptr, core::kInvalidNodeTypeHandle };
    void reportFailure(const char* message)
    {
        // The error logging may not work if the extension is shutting down but try anyway
        CARB_LOG_ERROR(message);
        auto interface = carb::getCachedInterface<core::ComputeGraph>();
        CARB_ASSERT(interface);
        interface->setTestFailure(true);
    }
    OgnTestGracefulShutdown()
    {
        auto iGraphRegistry = carb::getCachedInterface<core::IGraphRegistry>();
        if (iGraphRegistry)
        {
            m_myNodeTypeObj = iGraphRegistry->getRegisteredType("omni.graph.test.TestGracefulShutdown");
            if (! m_myNodeTypeObj.iNodeType or (m_myNodeTypeObj.nodeTypeHandle == core::kInvalidNodeTypeHandle))
            {
                reportFailure("TestGracefulShutdown node failed to find its node registration on initialization");
            }
        }
        else
        {
            reportFailure("TestGracefulShutdown node failed to acquire the IGraphRegistry interface on initialization");
        }
    }
    ~OgnTestGracefulShutdown()
    {
        auto iGraphRegistry = carb::getCachedInterface<core::IGraphRegistry>();
        if (iGraphRegistry)
        {
            // For the test the specialized registration process will cause the static flag on this node to be set
            // to false before the deregistration happens, simulating a potential problem with static object
            // destruction order. That means if this node's state is still alive during the extension shutdown the
            // flag will be false, otherwise it will be true.
            if (OgnTestGracefulShutdown::sShutdownStarted)
            {
                reportFailure("TestGracefulShutdown release did not happen until the extension was being shut down");
            }
        }
        else
        {
            reportFailure("TestGracefulShutdown node failed to acquire the IGraphRegistry interface on release");
        }
    };

    static bool compute(OgnTestGracefulShutdownDatabase& db)
    {
        // Accessing the state is required to create it since it creates on demand
        (void) db.internalState<OgnTestGracefulShutdown>();
        return true;
    }
};

bool OgnTestGracefulShutdown::sShutdownStarted = false;

// Specialized version of the code in REGISTER_OGN_NODE() that inserts a modification to the static flag that indicates
// when the extension shutdown has started.
namespace {
    class RegistrationWrapper : ogn::NodeTypeBootstrapImpl<OgnTestGracefulShutdown, OgnTestGracefulShutdownDatabase>
    {
    public:
        RegistrationWrapper()
        : ogn::NodeTypeBootstrapImpl<OgnTestGracefulShutdown, OgnTestGracefulShutdownDatabase>(
            "omni.graph.test.TestGracefulShutdown", 1, "omni.graph.test"
        )
        {
            OgnTestGracefulShutdown::sShutdownStarted = false;
        }
        ~RegistrationWrapper()
        {
            OgnTestGracefulShutdown::sShutdownStarted = true;
        }
    };
    RegistrationWrapper s_registration;
}

} // namespace test
} // namespace graph
} // namespace omni
