// 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 <OgnTutorialBundlesDatabase.h>

using omni::graph::core::BaseDataType;
using omni::graph::core::NameToken;
using omni::graph::core::Type;

namespace
{
// Tokens to use for checking filter names
static NameToken s_filterBigArrays;
static NameToken s_filterNameX;
static NameToken s_filterTypeInt;
}

class OgnTutorialBundles
{
public:
    // Overriding the initialize method allows caching of the name tokens which will avoid string comparisons
    // at evaluation time.
    static void initialize(const GraphContextObj& contextObj, const NodeObj&)
    {
        s_filterBigArrays = contextObj.iToken->getHandle("big");
        s_filterNameX = contextObj.iToken->getHandle("x");
        s_filterTypeInt = contextObj.iToken->getHandle("int");
    }

    static bool compute(OgnTutorialBundlesDatabase& db)
    {
        // Bundle attributes are extracted from the database in the same way as any other attribute.
        // The only difference is that a different interface is provided, suited to bundle manipulation.
        const auto& fullBundle = db.inputs.fullBundle();
        const auto& filteredBundle = db.inputs.filteredBundle();
        const auto& filters = db.inputs.filters();
        auto& outputBundle = db.outputs.combinedBundle();

        // The first thing this node does is to copy the contents of the fullBundle to the output bundle.
        // operator=() has been defined on bundles to make this a one-step operation. Note that this completely
        // replaces any previous bundle contents. If you wish to append another bundle then you would use:
        //    outputBundle.insertBundle(fullBundle);
        outputBundle = fullBundle;

        // Set some booleans that determine which filters to apply
        bool filterBigArrays{ false };
        bool filterNameX{ false };
        bool filterTypeInt{ false };
        for (const auto& filterToken : filters)
        {
            if (filterToken == s_filterBigArrays)
            {
                filterBigArrays = true;
            }
            else if (filterToken == s_filterNameX)
            {
                filterNameX = true;
            }
            else if (filterToken == s_filterTypeInt)
            {
                filterTypeInt = true;
            }
            else
            {
                db.logWarning("Unrecognized filter name '%s'", db.tokenToString(filterToken));
            }
        }

        // The bundle object has an iterator for looping over the attributes within in
        for (const auto& bundledAttribute : filteredBundle)
        {
            // The two main accessors for the bundled attribute provide the name and type information
            NameToken name = bundledAttribute.name();
            Type type = bundledAttribute.type();

            // Check each of the filters to see which attributes are to be skipped
            if (filterTypeInt)
            {
                if ((type.baseType == BaseDataType::eInt) || (type.baseType == BaseDataType::eUInt) ||
                    (type.baseType == BaseDataType::eInt64) || (type.baseType == BaseDataType::eUInt64))
                {
                    continue;
                }
            }
            if (filterNameX)
            {
                std::string nameString(db.tokenToString(name));
                if (nameString.find('x') != std::string::npos)
                {
                    continue;
                }
            }
            if (filterBigArrays)
            {
                // A simple utility method on the bundled attribute provides access to array size
                if (bundledAttribute.size() > 10)
                {
                    continue;
                }
            }

            // All filters have been passed so the attribute is eligible to be copied onto the output.
            outputBundle.insertAttribute(bundledAttribute);
        }
        return true;
    }
};

REGISTER_OGN_NODE()
