#ifndef Foundation_Math_Plane_INCLUDED
#define Foundation_Math_Plane_INCLUDED

#include <utility>

#include <foundation/Math/Ray.hpp>

namespace Tn {

namespace math {

template <typename T>
struct Plane {

    /*
      Plane defined by

      Ax + By + Cz + D = 0
    */

    typedef T component_type;

    Plane(T na, T nb, T nc, T nd) :
        a(na),
        b(nb),
        c(nc),
        d(nd) {
    }

    Plane() :
        a(0),
        b(0),
        c(0),
        d(0) {
    }

    T a;
    T b;
    T c;
    T d;
};


template <typename T>
Plane<T> normalize(Plane<T> const & p) {
    T d = sqrt(p.a * p.a  +
               p.b * p.b  +
               p.c * p.c);
    T inv_d = T(1) / d;

    return Plane<T>(p.a * inv_d, 
                    p.b * inv_d, 
                    p.c * inv_d,
                    p.d * inv_d);
}

typedef Plane<float> Planef;
typedef Plane<double> Planed;


/*
  Returns true if ray intersects plane. Assumes ray is normalized.

  http://www.siggraph.org/education/materials/HyperGraph/raytrace/rayplane_intersection.htm
*/

template <typename T>
std::pair<bool,Point<3,T> > intersect(Plane<T> const & plane, Ray<3,T> const & ray) {

    Vec<3,T> pn = Vec3<T>(plane.a, plane.b, plane.c);
    T vd = dot(pn, ray.direction);
    if (vd==T(0)) return std::pair<bool,Point<3,T> >(false, Point<3,T>());

    T v0 = -(dot(pn,toVec(ray.origin)) + plane.d);

    T t = v0/vd;

    if (t<T(0)) return std::pair<bool,Point<3,T> >(false, Point<3,T>());

    return std::pair<bool,Point<3,T> >(true, ray.origin + ray.direction * t);
}


template <typename T>
T intersect(Plane<T> const & plane, Point<3,T> const & point) {

    return
        plane.a * point[0] +
        plane.b * point[1] +
        plane.c * point[2] +
        plane.d;
}

} // namespace math

} // namespace Tn


#endif
