//! \~\file piquaternion.h
//! \~\ingroup Math
//! \~\brief
//! \~english Quaternion
//! \~russian Кватернион
/*
    PIP - Platform Independent Primitives
    Class for quaternions
    Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PIQUATERNION_H
#define PIQUATERNION_H

#include "pimathmatrix.h"

//! \~\ingroup Math
//! \~\brief
//! \~english Quaternion for representing 3D rotations and orientations
//! \~russian Кватернион для представления 3D вращений и ориентаций
class PIP_EXPORT PIQuaternion {
	friend PIP_EXPORT PIQuaternion operator*(const PIQuaternion & q0, const PIQuaternion & q1);
	friend PIP_EXPORT PIQuaternion operator*(const double & a, const PIQuaternion & q);

public:
	//! \~english Construct quaternion from rotation axis and angle
	//! \~russian Создать кватернион из оси вращения и угла
	PIQuaternion(const PIMathVectorT3d & u = PIMathVectorT3d(), double a = 0.);

	//! \~english Returns conjugate of this quaternion (negated vector part)
	//! \~russian Возвращает сопряженный кватернион (с инвертированной векторной частью)
	PIQuaternion conjugate() const { return PIQuaternion(-vector(), scalar()); }

	//! \~english Returns new quaternion rotated around axis u by angle a
	//! \~russian Возвращает новый кватернион, повернутый вокруг оси u на угол a
	PIQuaternion rotated(const PIMathVectorT3d & u, double a) const;

	//! \~english Rotate this quaternion around axis u by angle a
	//! \~russian Повернуть этот кватернион вокруг оси u на угол a
	void rotate(const PIMathVectorT3d & u, double a);

	//! \~english Normalize quaternion to unit length
	//! \~russian Нормализовать кватернион к единичной длине
	void normalize();

	//! \~english Get/Set scalar component
	//! \~russian Получить/установить скалярную компоненту
	double & scalar() { return q[0]; }

	//! \~english Get scalar component
	//! \~russian Получить скалярную компоненту
	double scalar() const { return q[0]; }

	//! \~english Returns vector part of quaternion
	//! \~russian Возвращает векторную часть кватерниона
	PIMathVectorT3d vector() const { return PIMathVectorT3d({q[1], q[2], q[3]}); }

	//! \~english Returns Euler angles from quaternion
	//! \~russian Возвращает углы Эйлера из кватерниона
	PIMathVectorT3d eyler() const;

	//! \~english Returns 3x3 rotation matrix from quaternion
	//! \~russian Возвращает матрицу вращения 3x3 из кватерниона
	PIMathMatrixT33d rotationMatrix() const;

	//! \~english Extracts rotation axis from quaternion
	//! \~russian Извлекает ось вращения из кватерниона
	void axis(PIMathVectorT3d * ret) const;

	//! \~english Create quaternion from Euler angles (roll, pitch, yaw)
	//! \~russian Создать кватернион из углов Эйлера (крен, тангаж, рыскание)
	static PIQuaternion fromEyler(double ax, double ay, double az);

	//! \~english Create quaternion from 3x3 rotation matrix
	//! \~russian Создать кватернион из матрицы вращения 3x3
	static PIQuaternion fromRotationMatrix(const PIMathMatrixT33d & m);

	//! \~english Create quaternion from rotation angles
	//! \~russian Создать кватернион из углов поворота
	static PIQuaternion fromAngles(double ax, double ay, double az);

	//! \~english Create quaternion from rotation angles (alternative method)
	//! \~russian Создать кватернион из углов поворота (альтернативный метод)
	static PIQuaternion fromAngles2(double ax, double ay, double az);

protected:
	double q[4];
};

//! \~english Scalar multiplication with quaternion
//! \~russian Умножение скаляра на кватернион
PIP_EXPORT PIQuaternion operator*(const double & a, const PIQuaternion & q);

//! \~english Quaternion multiplication
//! \~russian Умножение кватернионов
//! \~\details
//! \~english Performs quaternion multiplication (Hamilton product)
//! \~russian Выполняет умножение кватернионов (произведение Гамильтона)
//! \~\sa operator+(const PIQuaternion &, const PIQuaternion &)
//! \~\sa operator-(const PIQuaternion &, const PIQuaternion &)
PIP_EXPORT PIQuaternion operator*(const PIQuaternion & q0, const PIQuaternion & q1);

//! \~english Quaternion addition
//! \~russian Сложение кватернионов
//! \~\details
//! \~english Adds corresponding components of two quaternions
//! \~russian Складывает соответствующие компоненты двух кватернионов
//! \~\sa operator*(const PIQuaternion &, const PIQuaternion &)
//! \~\sa operator-(const PIQuaternion &, const PIQuaternion &)
inline PIQuaternion operator+(const PIQuaternion & q0, const PIQuaternion & q1) {
	return PIQuaternion(q0.vector() + q1.vector(), q0.scalar() + q1.scalar());
}

//! \~english Quaternion subtraction
//! \~russian Вычитание кватернионов
//! \~\details
//! \~english Subtracts corresponding components of two quaternions
//! \~russian Вычитает соответствующие компоненты двух кватернионов
//! \~\sa operator*(const PIQuaternion &, const PIQuaternion &)
//! \~\sa operator+(const PIQuaternion &, const PIQuaternion &)
inline PIQuaternion operator-(const PIQuaternion & q0, const PIQuaternion & q1) {
	return PIQuaternion(q0.vector() - q1.vector(), q0.scalar() - q1.scalar());
}

//! \~english Quaternion negation
//! \~russian Унарный минус кватерниона
//! \~\details
//! \~english Negates both vector and scalar parts of the quaternion
//! \~russian Меняет знаки как векторной, так и скалярной частей кватерниона
//! \~\sa conjugate()
inline PIQuaternion operator-(const PIQuaternion & q0) {
	return PIQuaternion(-q0.vector(), -q0.scalar());
}

#endif // PIQUATERNION_H
