#include "math/Quaternion.hpp"

Core::Quaternion::Quaternion() : w(1.0f) {
}

Core::Quaternion::Quaternion(const Vector3& axis, float angle) : xyz(axis) {
    xyz.normalize();
    float factor = 0.0f;
    Core::Math::sinCos(Core::Math::degreeToRadian(angle) * 0.5f, factor, w);
    xyz *= factor;
}

Core::Quaternion Core::Quaternion::lerp(float f,
                                        const Quaternion& other) const {
    Quaternion q;
    q.xyz = xyz * (1.0f - f) + other.xyz * f;
    q.w = w * (1.0f - f) + other.w * f;
    float iLength =
        1.0f / Core::Math::squareRoot(q.xyz.squareLength() + q.w * q.w);
    q.xyz *= iLength;
    q.w *= iLength;
    return q;
}

Core::Quaternion& Core::Quaternion::operator*=(const Quaternion& other) {
    float dot = xyz.dot(other.xyz);
    xyz = other.xyz * w + xyz * other.w + xyz.cross(other.xyz);
    w = w * other.w - dot;
    return *this;
}

Core::Quaternion Core::Quaternion::operator*(const Quaternion& other) const {
    Quaternion q(*this);
    q *= other;
    return q;
}

Core::Vector3 Core::Quaternion::operator*(const Vector3& v) const {
    Vector3 qv = v * w + xyz.cross(v);
    Vector3 qvq = xyz * xyz.dot(v) + qv * w - qv.cross(xyz);
    return qvq;
}