// Copyright (c) 2020, 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 <OgnTutorialRoleDataDatabase.h>

// This class exercises access to the DataModel through the generated database class for all role-based data types

namespace
{
// Helper values to make it easy to add 1 to values of different lengths
GfHalf h1{ 1.0f };
GfVec2d increment2d{ 1.0, 1.0 };
GfVec2f increment2f{ 1.0f, 1.0f };
GfVec2h increment2h{ h1, h1 };
GfVec3d increment3d{ 1.0, 1.0, 1.0 };
GfVec3f increment3f{ 1.0f, 1.0f, 1.0f };
GfVec3h increment3h{ h1, h1, h1 };
GfVec4d increment4d{ 1.0, 1.0, 1.0, 1.0 };
GfVec4f increment4f{ 1.0f, 1.0f, 1.0f, 1.0f };
GfVec4h increment4h{ h1, h1, h1, h1 };
GfQuatd incrementQd{ 1.0, 1.0, 1.0, 1.0 };
GfQuatf incrementQf{ 1.0f, 1.0f, 1.0f, 1.0f };
GfQuath incrementQh{ h1, h1, h1, h1 };
GfMatrix4d incrementM4d{ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
GfMatrix3d incrementM3d{ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
GfMatrix2d incrementM2d{ 1.0, 1.0, 1.0, 1.0 };
}

// Helper macro to simplify the code but include all of the error checking
#define ComputeOne(ATTRIBUTE_NAME, INCREMENT_VARIABLE, ROLE_EXPECTED)                                                  \
    foundError = false;                                                                                                \
    if (db.inputs.ATTRIBUTE_NAME.role() != ROLE_EXPECTED)                                                              \
    {                                                                                                                  \
        db.logWarning("Input role type %d != %d", (int)db.inputs.ATTRIBUTE_NAME.role(), (int)ROLE_EXPECTED);           \
        foundError = true;                                                                                             \
        foundAnyErrors = true;                                                                                         \
    }                                                                                                                  \
    if (db.outputs.ATTRIBUTE_NAME.role() != ROLE_EXPECTED)                                                             \
    {                                                                                                                  \
        db.logWarning("output role type %d != %d", (int)db.outputs.ATTRIBUTE_NAME.role(), (int)ROLE_EXPECTED);         \
        foundError = true;                                                                                             \
        foundAnyErrors = true;                                                                                         \
    }                                                                                                                  \
    if (!foundError)                                                                                                   \
    {                                                                                                                  \
        db.outputs.ATTRIBUTE_NAME() = db.inputs.ATTRIBUTE_NAME() + INCREMENT_VARIABLE;                                 \
    }

class OgnTutorialRoleData
{
public:
    static bool compute(OgnTutorialRoleDataDatabase& db)
    {
        // The roles for the attributes only serve to guide how to interpret them. When accessed from the
        // database they take the form of their raw underlying type. For example a point3d will have the
        // same GfVec3d type as a double[3], as will a vector3d and a normal3d.

        // Keep track if any role errors were found with this, continuing to the end of evaluation after errors
        bool foundAnyErrors{ false }; // Toggled on as soon as any error is found
        bool foundError{ false }; // Toggled off and on for each attribute

        // Walk through all of the data types, using the macro to perform error checking
        ComputeOne(a_color3d, increment3d, AttributeRole::eColor);
        ComputeOne(a_color3f, increment3f, AttributeRole::eColor);
        ComputeOne(a_color3h, increment3h, AttributeRole::eColor);
        //
        ComputeOne(a_color4d, increment4d, AttributeRole::eColor);
        ComputeOne(a_color4f, increment4f, AttributeRole::eColor);
        ComputeOne(a_color4h, increment4h, AttributeRole::eColor);
        //
        ComputeOne(a_frame, incrementM4d, AttributeRole::eFrame);
        //
        ComputeOne(a_matrix2d, incrementM2d, AttributeRole::eMatrix
        );
        ComputeOne(a_matrix3d, incrementM3d, AttributeRole::eMatrix
        );
        ComputeOne(a_matrix4d, incrementM4d, AttributeRole::eMatrix
        );
        //
        ComputeOne(a_normal3d, increment3d, AttributeRole::eNormal);
        ComputeOne(a_normal3f, increment3f, AttributeRole::eNormal);
        ComputeOne(a_normal3h, increment3h, AttributeRole::eNormal);
        //
        ComputeOne(a_point3d, increment3d, AttributeRole::ePosition);
        ComputeOne(a_point3f, increment3f, AttributeRole::ePosition);
        ComputeOne(a_point3h, increment3h, AttributeRole::ePosition);
        //
        ComputeOne(a_quatd, incrementQd, AttributeRole::eQuaternion);
        ComputeOne(a_quatf, incrementQf, AttributeRole::eQuaternion);
        ComputeOne(a_quath, incrementQh, AttributeRole::eQuaternion);
        //
        ComputeOne(a_texcoord2d, increment2d, AttributeRole::eTexCoord);
        ComputeOne(a_texcoord2f, increment2f, AttributeRole::eTexCoord);
        ComputeOne(a_texcoord2h, increment2h, AttributeRole::eTexCoord);
        //
        ComputeOne(a_texcoord3d, increment3d, AttributeRole::eTexCoord);
        ComputeOne(a_texcoord3f, increment3f, AttributeRole::eTexCoord);
        ComputeOne(a_texcoord3h, increment3h, AttributeRole::eTexCoord);
        //
        ComputeOne(a_timecode, 1.0, AttributeRole::eTimeCode);
        //
        ComputeOne(a_vector3d, increment3d, AttributeRole::eVector);
        ComputeOne(a_vector3f, increment3f, AttributeRole::eVector);
        ComputeOne(a_vector3h, increment3h, AttributeRole::eVector);

        return foundAnyErrors;
    }
};

REGISTER_OGN_NODE()
