// 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 <OgnTutorialBundleAddAttributesDatabase.h>
#include <omni/graph/core/IAttributeType.h>

using omni::graph::core::Type;


class OgnTutorialBundleAddAttributes
{
public:
    static bool compute(OgnTutorialBundleAddAttributesDatabase& db)
    {
        const auto& attributeTypeNames = db.inputs.typesToAdd();
        const auto& attributeNames = db.inputs.addedAttributeNames();
        const auto& useBatchedAPI = db.inputs.useBatchedAPI();
        auto& outputBundle = db.outputs.bundle();

        // The usual error checking. Being diligent about checking the data ensures you will have an easier time
        // debugging the graph if anything goes wrong.
        if (attributeTypeNames.size() != attributeNames.size())
        {
            db.logWarning(
                "Number of attribute types (%zu) does not match number of attribute names (%zu)",
                attributeTypeNames.size(), attributeNames.size()
            );
            return false;
        }

        // Make sure the bundle is starting from empty
        outputBundle.clear();

        if (useBatchedAPI)
        {
            //
            // unfortunately we need to build a vector of the types
            //
            auto typeNameIt = std::begin(attributeTypeNames);
            std::vector<Type> types;
            types.reserve(attributeTypeNames.size());
            for (; typeNameIt != std::end(attributeTypeNames); ++typeNameIt)
            {
                auto typeName = *typeNameIt;
                types.emplace_back(db.typeFromName(typeName));
            }
            outputBundle.addAttributes(attributeTypeNames.size(), attributeNames.data(), types.data());

            // Remove attributes from the bundle that were already added. This is a somewhat contrived operation that
            // allows testing of both adding and removal within a simple environment.
            if (db.inputs.removedAttributeNames().size()) {
                outputBundle.removeAttributes(db.inputs.removedAttributeNames().size(), db.inputs.removedAttributeNames().data());
            }
        }
        else
        {
            // Since the two arrays are the same size a dual loop can be used to walk them in pairs
            auto typeNameIt = std::begin(attributeTypeNames);
            auto attributeNameIt = std::begin(attributeNames);
            for (; typeNameIt != std::end(attributeTypeNames) && attributeNameIt != std::end(attributeNames); ++typeNameIt, ++attributeNameIt)
            {
                auto typeName = *typeNameIt;
                auto attributeName = *attributeNameIt;
                auto typeNameString = db.tokenToString(typeName);
                Type newType = db.typeFromName(typeName);
                // Ignore the output since for this example there will not be any values set on the new attribute
                (void)outputBundle.addAttribute(attributeName, newType);
            }

            // Remove attributes from the bundle that were already added. This is a somewhat contrived operation that
            // allows testing of both adding and removal within a simple environment.
            for (const auto& toRemove : db.inputs.removedAttributeNames())
            {
                outputBundle.removeAttribute(toRemove);
            }
        }

        return true;
    }
};

REGISTER_OGN_NODE()
