// Copyright (c) 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 <OgnTutorialDynamicAttributesDatabase.h>
#include <omni/graph/core/CppWrappers.h>

namespace omni {
namespace graph {

using core::AttributeRole;

namespace tutorials {

class OgnTutorialDynamicAttributes
{
public:
    static bool compute(OgnTutorialDynamicAttributesDatabase& db)
    {
        auto iNode = db.abi_node().iNode;

        // Get a copy of the input so that it can be modified in place
        uint32_t rawOutput = db.inputs.value();

        // Only run this section of code if the dynamic attribute is present
        if (iNode->getAttributeExists(db.abi_node(), db.tokenToString(db.tokens.firstBit)))
        {
            AttributeObj firstBit = iNode->getAttributeByToken(db.abi_node(), db.tokens.firstBit);
            // firstBit will invert the bit with its number, if present.
            const auto firstBitPtr = getDataR<uint32_t>(
                db.abi_context(), firstBit.iAttribute->getConstAttributeDataHandle(firstBit, db.getInstanceIndex()));
            if (firstBitPtr)
            {
                if( 0 <= *firstBitPtr && *firstBitPtr <= 31)
                {
                    rawOutput ^= 1 << *firstBitPtr;
                }
                else
                {
                    db.logWarning("Could not xor bit %ud. Must be in [0, 31]", *firstBitPtr);
                }
            }
            else
            {
                db.logError("Could not retrieve the data for firstBit");
            }
        }

        if (iNode->getAttributeExists(db.abi_node(), db.tokenToString(db.tokens.secondBit)))
        {
            AttributeObj secondBit = iNode->getAttributeByToken(db.abi_node(), db.tokens.secondBit);
            // secondBit will invert the bit with its number, if present
            const auto secondBitPtr = getDataR<uint32_t>(
                db.abi_context(), secondBit.iAttribute->getConstAttributeDataHandle(secondBit, db.getInstanceIndex()));
            if (secondBitPtr)
            {
                if( 0 <= *secondBitPtr && *secondBitPtr <= 31)
                {
                    rawOutput ^= 1 << *secondBitPtr;
                }
                else
                {
                    db.logWarning("Could not xor bit %ud. Must be in [0, 31]", *secondBitPtr);
                }
            }
            else
            {
                db.logError("Could not retrieve the data for secondBit");
            }
        }

        if (iNode->getAttributeExists(db.abi_node(), db.tokenToString(db.tokens.invert)))
        {
            AttributeObj invert = iNode->getAttributeByToken(db.abi_node(), db.tokens.invert);
            // invert will invert the bits, if the role is set and the attribute access is correct
            const auto invertPtr = getDataR<double>(
                db.abi_context(), invert.iAttribute->getConstAttributeDataHandle(invert, db.getInstanceIndex()));
            if (invertPtr)
            {
                // Verify that the invert attribute has the (random) correct role before applying it
                if (invert.iAttribute->getResolvedType(invert).role == AttributeRole::eTimeCode)
                {
                    rawOutput ^= 0xffffffff;
                }
            }
            else
            {
                db.logError("Could not retrieve the data for invert");
            }
        }

        // Set the modified result onto the output as usual
        db.outputs.result() = rawOutput;

        return true;
    }
};

REGISTER_OGN_NODE()

} // namespace tutorials
} // namespace graph
} // namespace omni
