////////////////////////////////////////////////////////////////////////////////////////
//
//  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.
//
////////////////////////////////////////////////////////////////////////////////////////

/**
 * \file
 * \brief Contains the definition of the Ovito::Ray_3 class template.
 */

#pragma once


#include <ovito/core/Core.h>
#include <ovito/core/utilities/io/SaveStream.h>
#include <ovito/core/utilities/io/LoadStream.h>
#include "Vector3.h"
#include "Point3.h"
#include "AffineTransformation.h"

namespace Ovito {

/**
 * \brief An infinite ray in 3d space, defined by a base point and a direction vector.
 *
 * This class stores an infinite ray in three-dimensional space, which is described by
 * a base point #base and a direction vector #dir. The direction vector does not have to be
 * a unit vector.
 *
 * The template parameter \a T specifies the data type used for spatial coordinates.
 * The standard template instantiation for floating-point coordinates is predefined:
 *
 * \code
 *      typedef Ray_3<FloatType>  Ray3;
 * \endcode
 */
template<typename T>
class Ray_3
{
public:

    /// A base point on the ray.
    Point_3<T> base;

    /// The direction vector.
    Vector_3<T> dir;

    /////////////////////////////// Constructors /////////////////////////////////

    /// Empty default constructor that does not initialize the fields of the object for performance reasons!
    /// Both the base point and the direction vector are undefined.
    Ray_3() = default;

    /// \brief Initializes the ray with a base point and a direction vector.
    /// \param b A point through which the ray passes.
    /// \param d The ray's direction vector.
    constexpr Ray_3(const Point_3<T>& b, const Vector_3<T>& d) : base(b), dir(d) {}

    /// \brief Initializes the ray from two points.
    /// \param a The first point on the ray
    /// \param b The second point on the ray.
    ///
    /// The direction of the ray is initialized to the vector connecting \a a and \a b.
    constexpr Ray_3(const Point_3<T>& a, const Point_3<T>& b) : base(a), dir(b - a) {}

    ////////////////////////////////// Queries ///////////////////////////////////

    /// \brief Returns a point on the ray at the specified position.
    /// \param t Specifies the position along the ray.
    /// \return The point (#base + #dir * \a t).
    constexpr Point_3<T> point(T t) const { return base + dir * t; }

    /////////////////////////////// Unary operators //////////////////////////////

    /// \brief Flips the ray's direction.
    /// \return A new ray with a reversed direction vector.
    constexpr Ray_3 operator-() const { return Ray_3(base, -dir); }

    ////////////////////////////////// Utilities /////////////////////////////////

    /// \brief Generates a string representation of this ray.
    QString toString() const {
        return QString("[Base: %1 Dir: %2]").arg(base.toString(), dir.toString());
    }
};

/// \brief Transforms a ray.
/// \param tm The affine transformation matrix.
/// \param ray The ray to be transformed.
/// \return A new ray with transformed base point and direction vector.
///         The direction vector is automatically normalized after the transformation.
/// \relates Ray_3
template<typename T>
constexpr inline Ray_3<T> operator*(const AffineTransformationT<T>& tm, const Ray_3<T>& ray) {
    return { tm * ray.base, (tm * ray.dir).normalized() };
}

/// \brief Prints a ray to an output stream.
/// \param os The output stream.
/// \param p The ray to write to the output stream \a os.
/// \return The output stream \a os.
/// \relates Ray_3
template<typename T>
inline std::ostream& operator<<(std::ostream &os, const Ray_3<T> &r) {
    return os << '[' << r.base.x() << ' ' << r.base.y()  << ' ' << r.base.z() << "], (" << r.dir.x() << ' ' << r.dir.y()  << ' ' << r.dir.z() << ')';
}

/// \brief Prints a ray to a Qt debug stream.
/// \relates Ray_3
template<typename T>
inline QDebug operator<<(QDebug dbg, const Ray_3<T>& r) {
    dbg.nospace() << "[" << r.base.x() << " " << r.base.y() << " " << r.base.z() << "], (" << r.dir.x() << " " << r.dir.y()  << " " << r.dir.z() << ')';
    return dbg.space();
}

/// \brief Writes a ray to a binary output stream.
/// \param stream The output stream.
/// \param p The ray to write to the output stream \a stream.
/// \return The output stream \a stream.
/// \relates Ray_3
template<typename T>
inline SaveStream& operator<<(SaveStream& stream, const Ray_3<T>& r) {
    return stream << r.base << r.dir;
}

/// \brief Reads a ray from a binary input stream.
/// \param stream The input stream.
/// \param p Reference to a ray variable where the parsed data will be stored.
/// \return The input stream \a stream.
/// \relates Ray_3
template<typename T>
inline LoadStream& operator>>(LoadStream& stream, Ray_3<T>& r) {
    return stream >> r.base >> r.dir;
}

/// \brief Writes a ray to a Qt data stream.
/// \relates Ray_3
template<typename T>
inline QDataStream& operator<<(QDataStream& stream, const Ray_3<T>& r) {
    return stream << r.base << r.dir;
}

/// \brief Reads a ray from a Qt data stream.
/// \relates Ray_3
template<typename T>
inline QDataStream& operator>>(QDataStream& stream, Ray_3<T>& r) {
    return stream >> r.base >> r.dir;
}

/**
 * \brief Instantiation of the Ray_3 class template with the default floating-point type.
 * \relates Ray_3
 */
using Ray3 = Ray_3<FloatType>;

}   // End of namespace

Q_DECLARE_METATYPE(Ovito::Ray3);
Q_DECLARE_TYPEINFO(Ovito::Ray3, Q_PRIMITIVE_TYPE);
