// 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 <OgnSinDatabase.h>
#include <omni/graph/core/ogn/ComputeHelpers.h>
#include <carb/logging/Log.h>
#include <omni/math/linalg/math.h>
#include <math.h>

namespace omni {
namespace graph {
namespace nodes {
    
// unnamed namespace to avoid multiple declaration when linking
namespace {

/**
 * Used when input type is resolved as non-int numeric type other than Half
 */
template <typename T>
bool tryComputeAssumingType(OgnSinDatabase& db)
{
    auto functor = [](auto const& a, auto& result)
    {
        result = static_cast<T>(std::sin(pxr::GfDegreesToRadians(a)));
    };
    return ogn::compute::tryComputeWithArrayBroadcasting<T>(db.inputs.value(), db.outputs.value(), functor);
}

template <>
bool tryComputeAssumingType<pxr::GfHalf>(OgnSinDatabase& db)
{
    auto functor = [](auto const& a, auto& result)
    {
        result = static_cast<pxr::GfHalf>(static_cast<float>(std::sin(pxr::GfDegreesToRadians(a))));
    };
    return ogn::compute::tryComputeWithArrayBroadcasting<pxr::GfHalf>(db.inputs.value(), db.outputs.value(), functor);
}
} // namespace

class OgnSin
{
public:
    static bool compute(OgnSinDatabase& db)
    {
        try
        {
            // All possible types excluding ogn::string and bool
            // scalers
            if (tryComputeAssumingType<double>(db)) return true;
            else if (tryComputeAssumingType<pxr::GfHalf>(db)) return true; // Specifically for pxr::GfHalf
            else if (tryComputeAssumingType<float>(db)) return true;
            else
            {
                db.logWarning("Failed to resolve input types");
            }
        }
        catch (std::exception &error)
        {
            db.logError("Could not perform Sine funtion : %s", error.what());
        }
        return false;
    }

    static void onConnectionTypeResolve(const NodeObj& node){
        auto value = node.iNode->getAttributeByToken(node, inputs::value.token());
        auto result = node.iNode->getAttributeByToken(node, outputs::value.token());

        auto valueType = value.iAttribute->getResolvedType(value);

        // Require inputs to be resolved before determining output's type
        if (valueType.baseType != BaseDataType::eUnknown)
        {
            std::array<AttributeObj, 2> attrs { value, result };
            node.iNode->resolveCoupledAttributes(node, attrs.data(), attrs.size());

        }
    }
};

REGISTER_OGN_NODE()

} // namespace nodes
} // namespace graph
} // namespace omni
