////////////////////////////////////////////////////////////////////////////////////////
//
//  Copyright 2026 OVITO GmbH, Germany
//
//  This file is part of OVITO (Open Visualization Tool).
//
//  OVITO is free software; you can redistribute it and/or modify it either under the
//  terms of the GNU General Public License version 3 as published by the Free Software
//  Foundation (the "GPL") or, at your option, under the terms of the MIT License.
//  If you do not alter this notice, a recipient may use your version of this
//  file under either the GPL or the MIT License.
//
//  You should have received a copy of the GPL along with this program in a
//  file LICENSE.GPL.txt.  You should have received a copy of the MIT License along
//  with this program in a file LICENSE.MIT.txt
//
//  This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
//  either express or implied. See the GPL or the MIT License for the specific language
//  governing rights and limitations.
//
////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#include <ovito/core/Core.h>
#include <ovito/core/oo/RefTarget.h>
#include <ovito/core/dataset/animation/TimeInterval.h>
#include "Controller.h"

namespace Ovito {

/**
 * \brief Base class for animation keys.
 */
class OVITO_CORE_EXPORT AnimationKey : public RefTarget
{
    OVITO_CLASS(AnimationKey)

public:

    /// Constructor.
    void initializeObject(ObjectInitializationFlags flags, AnimationTime time = AnimationTime(0)) {
        RefTarget::initializeObject(flags);
        setTime(time);
    }

    /// Returns the value of this animation key as a QVariant.
    virtual QVariant valueQVariant() const = 0;

    /// Sets the value of the key.
    virtual bool setValueQVariant(const QVariant& v) = 0;

protected:

    /// This method is called once for this object after it has been completely loaded from a stream.
    virtual void loadFromStreamComplete(ObjectLoadStream& stream) override;

private:

    /// The animation time at which the key is positioned.
    DECLARE_MODIFIABLE_PROPERTY_FIELD(AnimationTime{0}, time, setTime);
};

/**
 * \brief Animation key class for float controllers.
 */
class OVITO_CORE_EXPORT FloatAnimationKey : public AnimationKey
{
    OVITO_CLASS(FloatAnimationKey)

public:

    /// The type of value stored by this animation key.
    using value_type = FloatType;

    /// The type used to initialize default values of this key.
    using nullvalue_type = FloatType;

    /// The type used for derivatives/tangents.
    using tangent_type = FloatType;

    /// Constructor.
    void initializeObject(ObjectInitializationFlags flags, AnimationTime time = AnimationTime(0), FloatType value = 0) {
        AnimationKey::initializeObject(flags, time);
        _value.mutableValue() = value;
    }

    /// Returns the value of this animation key as a QVariant.
    virtual QVariant valueQVariant() const override {
        return QVariant::fromValue(value());
    }

    /// Sets the value of the key.
    virtual bool setValueQVariant(const QVariant& v) override {
        if(!v.canConvert<value_type>()) return false;
        setValue(v.value<value_type>());
        return true;
    }

private:

    /// Stores the value of the key.
    DECLARE_MODIFIABLE_PROPERTY_FIELD(value_type{}, value, setValue);
};

/**
 * \brief Animation key class for integer controllers.
 */
class OVITO_CORE_EXPORT IntegerAnimationKey : public AnimationKey
{
    OVITO_CLASS(IntegerAnimationKey)

public:

    /// The type of value stored by this animation key.
    using value_type = int;

    /// The type used to initialize default values of this key.
    using nullvalue_type = int;

    /// The type used for derivatives/tangents.
    using tangent_type = int;

    /// Constructor.
    void initializeObject(ObjectInitializationFlags flags, AnimationTime time = AnimationTime(0), int value = 0) {
        AnimationKey::initializeObject(flags, time);
        _value.mutableValue() = value;
    }

    /// Returns the value of this animation key as a QVariant.
    virtual QVariant valueQVariant() const override {
        return QVariant::fromValue(value());
    }

    /// Sets the value of the key.
    virtual bool setValueQVariant(const QVariant& v) override {
        if(!v.canConvert<value_type>()) return false;
        setValue(v.value<value_type>());
        return true;
    }

private:

    /// Stores the value of the key.
    DECLARE_MODIFIABLE_PROPERTY_FIELD(value_type{}, value, setValue);
};

/**
 * \brief Animation key class for Vector3 controllers.
 */
class OVITO_CORE_EXPORT Vector3AnimationKey : public AnimationKey
{
    OVITO_CLASS(Vector3AnimationKey)

public:

    /// The type of value stored by this animation key.
    using value_type = Vector3;

    /// The type used to initialize default values of this key.
    using nullvalue_type = Vector3::Zero;

    /// The type used for derivatives/tangents.
    using tangent_type = Vector3;

    /// Constructor.
    void initializeObject(ObjectInitializationFlags flags, AnimationTime time = AnimationTime(0), const Vector3& value = Vector3::Zero()) {
        AnimationKey::initializeObject(flags, time);
        _value.mutableValue() = value;
    }

    /// Returns the value of this animation key as a QVariant.
    virtual QVariant valueQVariant() const override {
        return QVariant::fromValue(value());
    }

    /// Sets the value of the key.
    virtual bool setValueQVariant(const QVariant& v) override {
        if(!v.canConvert<value_type>()) return false;
        setValue(v.value<value_type>());
        return true;
    }

private:

    /// Stores the value of the key.
    DECLARE_MODIFIABLE_PROPERTY_FIELD(value_type{}, value, setValue);
};

/**
 * \brief Animation key class for position controllers.
 */
class OVITO_CORE_EXPORT PositionAnimationKey : public AnimationKey
{
    OVITO_CLASS(PositionAnimationKey)

public:

    /// The type of value stored by this animation key.
    using value_type = Vector3;

    /// The type used to initialize default values of this key.
    using nullvalue_type = Vector3::Zero;

    /// The type used for derivatives/tangents.
    using tangent_type = Vector3;

    /// Constructor.
    void initializeObject(ObjectInitializationFlags flags, AnimationTime time = AnimationTime(0), const Vector3& value = Vector3::Zero()) {
        AnimationKey::initializeObject(flags, time);
        _value.mutableValue() = value;
    }

    /// Returns the value of this animation key as a QVariant.
    virtual QVariant valueQVariant() const override {
        return QVariant::fromValue(value());
    }

    /// Sets the value of the key.
    virtual bool setValueQVariant(const QVariant& v) override {
        if(!v.canConvert<value_type>()) return false;
        setValue(v.value<value_type>());
        return true;
    }

private:

    /// Stores the value of the key.
    DECLARE_MODIFIABLE_PROPERTY_FIELD(value_type{}, value, setValue);
};

/**
 * \brief Animation key class for rotation controllers.
 */
class OVITO_CORE_EXPORT RotationAnimationKey : public AnimationKey
{
    OVITO_CLASS(RotationAnimationKey)

public:

    /// The type of value stored by this animation key.
    using value_type = Rotation;

    /// The type used to initialize default values of this key.
    using nullvalue_type = Rotation::Identity;

    /// The type used for derivatives/tangents.
    using tangent_type = Rotation;

    /// Constructor.
    void initializeObject(ObjectInitializationFlags flags, AnimationTime time = AnimationTime(0), const Rotation& value = Rotation::Identity()) {
        AnimationKey::initializeObject(flags, time);
        _value.mutableValue() = value;
    }

    /// Returns the value of this animation key as a QVariant.
    virtual QVariant valueQVariant() const override {
        return QVariant::fromValue(value());
    }

    /// Sets the value of the key.
    virtual bool setValueQVariant(const QVariant& v) override {
        if(!v.canConvert<value_type>()) return false;
        setValue(v.value<value_type>());
        return true;
    }

private:

    /// Stores the value of the key.
    DECLARE_MODIFIABLE_PROPERTY_FIELD(value_type{}, value, setValue);
};

/**
 * \brief Animation key class for scaling controllers.
 */
class OVITO_CORE_EXPORT ScalingAnimationKey : public AnimationKey
{
    OVITO_CLASS(ScalingAnimationKey)

public:

    /// The type of value stored by this animation key.
    using value_type = Scaling;

    /// The type used to initialize default values of this key.
    using nullvalue_type = Scaling::Identity;

    /// The type used for derivatives/tangents.
    using tangent_type = Scaling;

    /// Constructor.
    void initializeObject(ObjectInitializationFlags flags, AnimationTime time = AnimationTime(0), const Scaling& value = Scaling::Identity()) {
        AnimationKey::initializeObject(flags, time);
        _value.mutableValue() = value;
    }

    /// Returns the value of this animation key as a QVariant.
    virtual QVariant valueQVariant() const override {
        return QVariant::fromValue(value());
    }

    /// Sets the value of the key.
    virtual bool setValueQVariant(const QVariant& v) override {
        if(!v.canConvert<value_type>()) return false;
        setValue(v.value<value_type>());
        return true;
    }

private:

    /// Stores the value of the key.
    DECLARE_MODIFIABLE_PROPERTY_FIELD(value_type{}, value, setValue);
};

/**
 * \brief Default implementation of the value interpolator concept that does linear interpolation.
 *
 * This template class interpolates linearly between two values of arbitrary types.
 * The value 0.0 of the interpolation parameter \c t is mapped to the first value.
 * The value 1.0 of the interpolation parameter \c t is mapped to the second value.
 */
template<typename ValueType>
struct LinearValueInterpolator {
    ValueType operator()(FloatType t, const ValueType& value1, const ValueType& value2) const {
        return static_cast<ValueType>(value1 + (t * (value2 - value1)));
    }
};

/**
 * \brief Implementation of the value interpolator concept for rotations.
 *
 * This class is required because the Rotation class does not support the standard
 * addition, scalar multiplication and subtraction operators.
 */
template<>
struct LinearValueInterpolator<Rotation> {
    Rotation operator()(FloatType t, const Rotation& value1, const Rotation& value2) const {
#if 1
        return interpolate(value1, value2, t);
#else
        Rotation diff = value2 * value1.inverse();
        return Rotation(diff.axis(), diff.angle() * t) * value1;
#endif
    }

    template<typename T>
    static RotationT<T> interpolate(const RotationT<T>& rot1, const RotationT<T>& rot2, T t) {
        OVITO_ASSERT(t >= 0 && t <= 1);

        RotationT<T> _rot2;
        if(rot1.axis().dot(rot2.axis()) < T(0))
            _rot2 = RotationT<T>(-rot2.axis(), -rot2.angle(), false);
        else
            _rot2 = rot2;

        // Determine interpolation type, compute extra spins, and adjust angles accordingly.
        if(rot1.axis().equals(_rot2.axis())) {
            return RotationT<T>((T(1) - t) * rot1.axis() + t * _rot2.axis(), (T(1) - t) * rot1.angle() + t * _rot2.angle());
        }
        else if(rot1.angle() != T(0)) {
            T fDiff = _rot2.angle() - rot1.angle();
            T fDiffUnit = fDiff/(2*Ovito::pi_v<T>);
            int extraSpins = (int)std::floor(fDiffUnit + T(0.5));
            if(extraSpins * fDiffUnit * (fDiffUnit - extraSpins) < 0)
                extraSpins = -extraSpins;

            QuaternionT<T> q1 = (QuaternionT<T>)rot1;
            QuaternionT<T> q2 = (QuaternionT<T>)_rot2;

            // Eliminate any non-acute angles between quaternions. This
            // is done to prevent potential discontinuities that are the result of
            // invalid intermediate value quaternions.
            if(q1.dot(q2) < T(0))
                q2 = -q2;

            // Clamp identity quaternions so that |w| <= 1 (avoids problems with
            // call to acos() in slerpExtraSpins).
            if(q1.w() < T(-1)) q1.w() = T(-1); else if(q1.w() > T(1)) q1.w() = T(1);
            if(q2.w() < T(-1)) q2.w() = T(-1); else if(q2.w() > T(1)) q2.w() = T(1);

            RotationT<T> result = RotationT<T>(slerpExtraSpins(t, q1, q2, extraSpins));
            if(result.axis().dot(interpolateAxis(t, rot1.axis(), _rot2.axis())) < T(0))
                result = RotationT<T>(-result.axis(), -result.angle(), false);
            int nrev = std::floor((t * _rot2.angle() + (T(1) - t) * rot1.angle() - result.angle())/(2*Ovito::pi_v<T>) + T(0.5));
            result.addRevolutions(nrev);
            return result;
        }
        else {
            return RotationT<T>(interpolateAxis(t, rot1.axis(), _rot2.axis()), (T(1) - t) * rot1.angle() + t * _rot2.angle());
        }
    }

    template<typename T>
    static inline Vector_3<T> interpolateAxis(T time, const Vector_3<T>& axis0, const Vector_3<T>& axis1) {
        // assert:  axis0 and axis1 are unit length
        // assert:  axis0.dot(axis1) >= 0
        // assert:  0 <= time <= 1

        T cos = axis0.dot(axis1);  // >= 0 by assertion
        OVITO_ASSERT(cos >= T(0));
        if(cos > T(1)) cos = T(1); // round-off error might create problems in acos call

        T angle = std::acos(cos);
        T invSin = T(1) / std::sin(angle);
        T timeAngle = time * angle;
        T coeff0 = std::sin(angle - timeAngle) * invSin;
        T coeff1 = std::sin(timeAngle) * invSin;

        return (coeff0 * axis0 + coeff1 * axis1);
    }

    template<typename T>
    static inline QuaternionT<T> slerpExtraSpins(T t, const QuaternionT<T>& p, const QuaternionT<T>& q, int iExtraSpins) {
        T fCos = p.dot(q);
        OVITO_ASSERT(fCos >= T(0));

        // Numerical round-off error could create problems in call to acos.
        if(fCos < T(-1)) fCos = T(-1);
        else if(fCos > T(1)) fCos = T(1);

        T fAngle = std::acos(fCos);
        T fSin = std::sin(fAngle);  // fSin >= 0 since fCos >= 0

        if(fSin < T(1e-3)) {
            return p;
        }
        else {
            T fPhase = Ovito::pi_v<T> * (T)iExtraSpins * t;
            T fInvSin = T(1) / fSin;
            T fCoeff0 = std::sin((T(1) - t) * fAngle - fPhase) * fInvSin;
            T fCoeff1 = std::sin(t * fAngle + fPhase) * fInvSin;
            return QuaternionT<T>(fCoeff0*p.x() + fCoeff1*q.x(), fCoeff0*p.y() + fCoeff1*q.y(),
                                    fCoeff0*p.z() + fCoeff1*q.z(), fCoeff0*p.w() + fCoeff1*q.w());
        }
    }
};

/**
 * \brief Implementation of the value interpolator concept for scaling values.
 *
 * This class is required because the Scaling class does not support the standard
 * addition, scalar multiplication and subtraction operators.
 */
template<>
struct LinearValueInterpolator<Scaling> {
    Scaling operator()(FloatType t, const Scaling& value1, const Scaling& value2) const {
        return Scaling::interpolate(value1, value2, t);
    }
};

/**
 * \brief Default implementation of the value interpolator concept that does smooth interpolation.
 *
 * This template class interpolates using a cubic spline between two values of arbitrary data type.
 * The value 0.0 of the interpolation parameter \c t is mapped to the first value.
 * The value 1.0 of the interpolation parameter \c t is mapped to the second value.
 */
template<typename ValueType>
struct SplineValueInterpolator {
    ValueType operator()(FloatType t, const ValueType& value1, const ValueType& value2, const ValueType& outPoint1, const ValueType& inPoint2) const {
        FloatType Ti = FloatType(1) - t;
        FloatType U2 = t * t, T2 = Ti * Ti;
        FloatType U3 = U2 * t, T3 = T2 * Ti;
        return value1 * T3 + outPoint1 * (FloatType(3) * t * T2) + inPoint2 * (FloatType(3) * U2 * Ti) + value2 * U3;
    }
};

/**
 * \brief Implementation of the smooth value interpolator concept for rotations.
 *
 * This class is required because the Rotation class does not support the standard
 * addition, scalar multiplication, and subtraction operators.
 */
template<>
struct SplineValueInterpolator<Rotation> {
    Rotation operator()(FloatType t, const Rotation& value1, const Rotation& value2, const Rotation& outPoint1, const Rotation& inPoint2) const {
        return Rotation(Rotation::interpolateQuad(value1, value2, outPoint1, inPoint2, t));
    }
};

/**
 * \brief Implementation of the smooth value interpolator concept for scaling values.
 *
 * This class is required because the Scaling class does not support the standard
 * addition, scalar multiplication, and subtraction operators.
 */
template<>
struct SplineValueInterpolator<Scaling> {
    Scaling operator()(FloatType t, const Scaling& value1, const Scaling& value2, const Scaling& outPoint1, const Scaling& inPoint2) const {
        return Scaling::interpolateQuad(value1, value2, outPoint1, inPoint2, t);
    }
};

}   // End of namespace
