//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Sample/Particle/Rotations.h
//! @brief     Defines class IRotationes.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#ifndef BORNAGAIN_SAMPLE_SCATTERING_ROTATIONS_H
#define BORNAGAIN_SAMPLE_SCATTERING_ROTATIONS_H

#include "Base/Type/Cloneable.h"
#include "Base/Vector/RotMatrix.h"
#include <heinz/Vectors3D.h>

//! Abstract base class for rotations.

class Rotation : public Cloneable {
public:
    static Rotation* newRotation(const RotMatrix& matrix);

    Rotation();

#ifndef SWIG
    Rotation* clone() const override = 0;

    //! Returns a new Rotation object that is the current object's inverse
    virtual Rotation* newInverse() const = 0;

    //! Returns transformation.
    virtual RotMatrix rotMatrix() const = 0;

    R3 transformed(const R3& v) const;

    //! Returns true if rotation matrix is identity matrix (no rotations)
    virtual bool isIdentity() const;

    bool zInvariant() const;

#endif // SWIG
};

Rotation* newProduct(const Rotation& left, const Rotation& right);


//! The identity rotation, which leaves everything in place.

class IdentityRotation : public Rotation // TODO RECONSIDER: merge with class Rotation
{
public:
    IdentityRotation();

#ifndef SWIG
    IdentityRotation* clone() const override { return new IdentityRotation(); }

    IdentityRotation* newInverse() const override { return new IdentityRotation(); }

    RotMatrix rotMatrix() const override;

    bool isIdentity() const override { return true; }
#endif // SWIG
};

//! A rotation about the x axis.

class RotationX : public Rotation {
public:
    RotationX(double angle);

#ifndef SWIG
    RotationX* clone() const override { return new RotationX(m_angle); }

    RotationX* newInverse() const override { return new RotationX(-m_angle); }

    double angle() const { return m_angle; }

    RotMatrix rotMatrix() const override;

protected:
    double m_angle;
#endif // SWIG
};

//! A rotation about the y axis.

class RotationY : public Rotation {
public:
    RotationY(double angle);

#ifndef SWIG
    RotationY* clone() const override { return new RotationY(m_angle); }

    RotationY* newInverse() const override { return new RotationY(-m_angle); }

    double angle() const { return m_angle; }

    RotMatrix rotMatrix() const override;

protected:
    double m_angle;
#endif // SWIG
};

//! A rotation about the z axis.

class RotationZ : public Rotation {
public:
    RotationZ(double angle);

#ifndef SWIG
    RotationZ* clone() const override { return new RotationZ(m_angle); }

    RotationZ* newInverse() const override { return new RotationZ(-m_angle); }

    double angle() const { return m_angle; }

    RotMatrix rotMatrix() const override;

protected:
    double m_angle;
#endif // SWIG
};

//! A sequence of rotations about the z-x'-z'' axes.

class RotationEuler : public Rotation {
public:
    RotationEuler(double alpha, double beta, double gamma);

#ifndef SWIG
    RotationEuler* clone() const override { return new RotationEuler(m_alpha, m_beta, m_gamma); }

    Rotation* newInverse() const override;

    double alpha() const { return m_alpha; } //!< First Euler angle, rotation around z axis
    double beta() const { return m_beta; }   //!< Second Euler angle, rotation around x' axis
    double gamma() const { return m_gamma; } //!< Third Euler angle, rotation around z'' axis

    RotMatrix rotMatrix() const override;

protected:
    double m_alpha, m_beta, m_gamma;
#endif // SWIG
};

#endif // BORNAGAIN_SAMPLE_SCATTERING_ROTATIONS_H
