// Copyright (c) 2020-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.
//
#pragma once

// This include must come first
// clang-format off
#include "UsdPCH.h"
// clang-format on

#include <pxr/base/gf/matrix4d.h>
#include <pxr/base/gf/quatf.h>

#include "carb/logging/Log.h"


/**
 * @brief Defined in a separate location to the other lerp functions
 *        in order to avoid breaking C-ABI compatibility
 */

namespace carb
{
namespace flatcache
{
/**
 * @brief Spherical interpolation specialization relying on pxr native
 *        interpolation for quaternions
 */

template <>
inline const pxr::GfQuatf interpolate(const pxr::GfQuatf& q0, const pxr::GfQuatf& q1, float theta)
{
    if (theta < 0.0f || theta > 1.0f)
    {
        CARB_LOG_WARN_ONCE("InterpolationUsd interpolate(): theta %f outside range [0.0, 1.0]", theta);
    }

    pxr::GfQuatf result = pxr::GfSlerp(theta, q0, q1);
    return result;
}

/**
 * @brief pxr::Matrix4d interpolation specialization Used in Kit by OmniHydraDelegate
 */
template <>
inline const pxr::GfMatrix4d interpolate(const pxr::GfMatrix4d& m0, const pxr::GfMatrix4d& m1, float theta)
{
    if (theta < 0.0f || theta > 1.0f)
    {
        CARB_LOG_WARN_ONCE("InterpolationUsd interpolate(): theta %f outside range [0.0, 1.0]", theta);
    }

    pxr::GfMatrix4d r0, r1; // rotations, where -r is inverse of r
    pxr::GfVec3d s0, s1; // scale
    pxr::GfMatrix4d u0, u1; // rotations, may contain shear info
    pxr::GfVec3d t0, t1; // translations
    pxr::GfMatrix4d p0, p1; // p is never modified; can contain projection info

    // Account for rotation, translation, scale
    // (order is mat = r * s * -r * u * t), eps=1e-10 used to avoid zero values
    m0.Factor(&r0, &s0, &u0, &t0, &p0);
    m1.Factor(&r1, &s1, &u1, &t1, &p1);

    // Interpolate component-wise
    pxr::GfVec3d tResult = pxr::GfLerp(theta, t0, t1);
    pxr::GfVec3d sResult = pxr::GfLerp(theta, s0, s1);

    pxr::GfQuatd rResult = pxr::GfSlerp(u0.ExtractRotationQuat(), u1.ExtractRotationQuat(), theta);

    pxr::GfMatrix4d result = pxr::GfMatrix4d(pxr::GfRotation(rResult), pxr::GfCompMult(sResult, tResult));

    return result;
}
}
}
