#ifndef PLANE_HPP_
#define PLANE_HPP_

#include <Utilities/Extern.hpp>
#include <BoundingBox/BoundingBox.hpp>

/**
 * @brief Represents a plane.
 */
struct Plane
{
    /**
     * @brief The normal vector of this plane.
     */
    glm::dvec3 normal;

    /**
     * @brief The distance from origin to the closest point.
     */
    double distance;

    /**
     * @brief Get the distance of `point` from this plane.
     * 
     * @param point The point.
     * @return Distance of `point` from this plane.
     */
    inline double getDistanceOfPoint(const glm::dvec3 &point) const
    {
        return glm::dot(normal, point) - distance;
    }

    /**
     * @brief Returns true if the specified bounding box is in front of this plane.
     * 
     * @param bb The bounding box.
     */
    inline bool isBoxInFront(const BoundingBox<double> &bb) const
    {
        const double r = 0.5 * ((bb.x1 - bb.x0) * std::abs(normal.x) +
            (bb.y1 - bb.y0) * std::abs(normal.y) + (bb.z1 - bb.z0) * std::abs(normal.z)
        );

        return -r <= getDistanceOfPoint(
            glm::dvec3{(bb.x0 + bb.x1) * 0.5, (bb.y0 + bb.y1) * 0.5, (bb.z0 + bb.z1) * 0.5}
        );
    }

    /**
     * @brief Make a plane from a point and normal.
     * 
     * @param point A point on the plane.
     * @param normal The plane's normal (must be normalized).
     * @return The plane.
     */
    static inline Plane fromPointAndNormal(glm::dvec3 point, glm::dvec3 normal)
    {
        return Plane{normal, glm::dot(point, normal)};
    }
};

#endif