// 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 <OgnBreakVector2Database.h>
#include <omni/graph/core/ogn/ComputeHelpers.h>
#include <omni/graph/core/ogn/UsdTypes.h>
#include <fstream>
#include <iomanip>

namespace omni
{
namespace graph
{
namespace nodes
{

namespace
{
template <typename Type>
bool tryBreakVector(OgnBreakVector2Database& db)
{
    const auto vector = db.inputs.tuple().template get<Type[2]>();
    auto x = db.outputs.x().template get<Type>();
    auto y = db.outputs.y().template get<Type>();

    if (!vector || !x || !y){
        return false;
    }

    *x = (*vector)[0];
    *y = (*vector)[1];

    return true;
}
} // namespace
// Node to break a 2-vector into it's component scalers
class OgnBreakVector2
{
public:
    static bool compute(OgnBreakVector2Database& db)
    {
        // Compute the components, if the types are all resolved.
        try
        {
            if (tryBreakVector<double>(db))
                return true;
            else if (tryBreakVector<float>(db))
                return true;
            else if (tryBreakVector<pxr::GfHalf>(db))
                return true;
            else if (tryBreakVector<int32_t>(db))
                return true;
            else
            {
                db.logWarning("Failed to resolve input types");
            }
        }
        catch (const std::exception& e)
        {
            db.logError("Vector could not be broken: %s", e.what());
            return false;
        }

        return true;
    }

    static void onConnectionTypeResolve(const NodeObj& nodeObj)
    {
        auto vector = nodeObj.iNode->getAttributeByToken(nodeObj, inputs::tuple.token());
        auto x = nodeObj.iNode->getAttributeByToken(nodeObj, outputs::x.token());
        auto y = nodeObj.iNode->getAttributeByToken(nodeObj, outputs::y.token());

        auto vectorType = vector.iAttribute->getResolvedType(vector);

        // Require inputs to be resolved before determining outputs' type
        if (vectorType.baseType != BaseDataType::eUnknown)
        {
            std::array<AttributeObj, 3> attrs{ vector, x, y };
            std::array<uint8_t, 3> tuples{ 2, 1, 1 };
            std::array<uint8_t, 3> arrays{ 0, 0, 0 };
            std::array<AttributeRole, 3> roles{ vectorType.role, AttributeRole::eNone, AttributeRole::eNone };
            nodeObj.iNode->resolvePartiallyCoupledAttributes(nodeObj, attrs.data(), tuples.data(), arrays.data(), roles.data(), attrs.size());
        }
    }
};

REGISTER_OGN_NODE();

}
}
}
