// 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 <OgnModuloDatabase.h>
#include <cmath>
#include <algorithm>
#include <array>

template <typename T, typename AttrInType, typename AttrOutType>
void modulo(AttrInType& a, AttrInType& b, AttrOutType& result)
{
    T bval = *b.template get<T>();
    if (bval == 0)
    {
        *result.template get<T>() = 0;
        return;
    }
    *result.template get<T>() = *a.template get<T>() % bval;
}

class OgnModulo
{
public:
    static bool compute(OgnModuloDatabase& db)
    {
        const auto& a = db.inputs.a();
        const auto& b = db.inputs.b();
        auto& result = db.outputs.result();

        if (!a.resolved())
            return true;

        switch(a.type().baseType)
        {
        case BaseDataType::eInt:
            modulo<int>(a, b, result);
            break;
        case BaseDataType::eUInt:
            modulo<uint32_t>(a, b, result);
            break;
        case BaseDataType::eInt64:
            modulo<int64_t>(a, b, result);
            break;
        case BaseDataType::eUInt64:
            modulo<uint64_t>(a, b, result);
            break;
        default:
            break;
        }
        
        return true;
    }
    
    static void onConnectionTypeResolve(const NodeObj& node){
        // Resolve fully-coupled types for the 3 attributes
        std::array<AttributeObj, 3> attrs {
            node.iNode->getAttribute(node, OgnModuloAttributes::inputs::a.m_name),
            node.iNode->getAttribute(node, OgnModuloAttributes::inputs::b.m_name),
            node.iNode->getAttribute(node, OgnModuloAttributes::outputs::result.m_name)
        };
        node.iNode->resolveCoupledAttributes(node, attrs.data(), attrs.size());
    }
};

REGISTER_OGN_NODE()
