// David Eberly, Geometric Tools, Redmond WA 98052
// Copyright (c) 1998-2021
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
// https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
// Version: 4.0.2019.08.13

#pragma once

#include <Mathematics/IntrLine3Plane3.h>
#include <Mathematics/Ray.h>

namespace gte
{
    template <typename Real>
    class TIQuery<Real, Ray3<Real>, Plane3<Real>>
    {
    public:
        struct Result
        {
            bool intersect;
        };

        Result operator()(Ray3<Real> const& ray, Plane3<Real> const& plane)
        {
            Result result;

            // Compute the (signed) distance from the ray origin to the plane.
            DCPQuery<Real, Vector3<Real>, Plane3<Real>> vpQuery;
            auto vpResult = vpQuery(ray.origin, plane);

            Real DdN = Dot(ray.direction, plane.normal);
            if (DdN > (Real)0)
            {
                // The ray is not parallel to the plane and is directed toward
                // the +normal side of the plane.
                result.intersect = (vpResult.signedDistance <= (Real)0);
            }
            else if (DdN < (Real)0)
            {
                // The ray is not parallel to the plane and is directed toward
                // the -normal side of the plane.
                result.intersect = (vpResult.signedDistance >= (Real)0);
            }
            else
            {
                // The ray and plane are parallel.
                result.intersect = (vpResult.distance == (Real)0);
            }

            return result;
        }
    };

    template <typename Real>
    class FIQuery<Real, Ray3<Real>, Plane3<Real>>
        :
        public FIQuery<Real, Line3<Real>, Plane3<Real>>
    {
    public:
        struct Result
            :
            public FIQuery<Real, Line3<Real>, Plane3<Real>>::Result
        {
            // No additional information to compute.
        };

        Result operator()(Ray3<Real> const& ray, Plane3<Real> const& plane)
        {
            Result result;
            DoQuery(ray.origin, ray.direction, plane, result);
            if (result.intersect)
            {
                result.point = ray.origin + result.parameter * ray.direction;
            }
            return result;
        }

    protected:
        void DoQuery(Vector3<Real> const& rayOrigin,
            Vector3<Real> const& rayDirection, Plane3<Real> const& plane,
            Result& result)
        {
            FIQuery<Real, Line3<Real>, Plane3<Real>>::DoQuery(rayOrigin,
                rayDirection, plane, result);
            if (result.intersect)
            {
                // The line intersects the plane in a point that might not be
                // on the ray.
                if (result.parameter < (Real)0)
                {
                    result.intersect = false;
                    result.numIntersections = 0;
                }
            }
        }
    };
}