// 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 <OgnPerturbBundlePointsDatabase.h>
#include <cstdlib>

namespace omni {
namespace graph {
namespace test {

// This is the implementation of the OGN node defined in OgnPerturbBundlePoints.ogn
class OgnPerturbBundlePoints
{
    static auto& copyBundle(OgnPerturbBundlePointsDatabase& db)
    {
        CARB_PROFILE_ZONE(carb::profiler::kCaptureMaskDefault, "Acquiring Data");
        db.outputs.bundle = db.inputs.bundle;
        return db.outputs.bundle();
    }
public:
    // Perturb a bundles of arrays of points by random amounts within the limits of the bounding cube formed by the
    // diagonal corners "minimum" and "maximum".
    static bool compute(OgnPerturbBundlePointsDatabase& db)
    {
        const auto& minimum = db.inputs.minimum();
        GfVec3f rangeSize = db.inputs.maximum() - minimum;

        // No bundle members mean nothing to perturb
        const auto& inputBundle = db.inputs.bundle();
        if (inputBundle.attributeCount() == 0)
        {
            db.outputs.bundle().clear();
            return true;
        }

        // How much of the surface should be perturbed
        auto percentModified = db.inputs.percentModified();
        percentModified = (percentModified > 100.0f ? 100.0f : (percentModified < 0.0f ? 0.0f : percentModified));

        // Steal the bundle contents, then walk all of the members and perturb any points arrays found
        auto& outputBundle = copyBundle(db);

        CARB_PROFILE_ZONE(carb::profiler::kCaptureMaskDefault, "Perturbing Data");
        for (auto& bundledAttribute : outputBundle)
        {
            auto pointArrayData = bundledAttribute.get<float[][3]>();
            if (pointArrayData)
            {
                auto& pointArray = *pointArrayData;
                size_t pointsToModify = (size_t)((percentModified * (float)pointArray.size()) / 100.0f);
                size_t index{ 0 };
                while (index < pointsToModify)
                {
                    auto& point = pointArray[index++];
                    point[0] += minimum[0] + static_cast<float>(rand()) / (static_cast<float>(RAND_MAX/(rangeSize[0])));
                    point[1] += minimum[1] + static_cast<float>(rand()) / (static_cast<float>(RAND_MAX/(rangeSize[1])));
                    point[2] += minimum[2] + static_cast<float>(rand()) / (static_cast<float>(RAND_MAX/(rangeSize[2])));
                }
            }
        }

        return true;
    }
};

REGISTER_OGN_NODE()

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