//#####################################################################
// Copyright 2002-2008, Robert Bridson, Ronald Fedkiw, Eran Guendelman, Jon Gretarsson, Andrew Selle.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
// Class RAY
//#####################################################################
#ifndef __RAY__
#define __RAY__

#include <Core/Basic_Geometry/RAY_HIT.h>
#include <Core/Basic_Geometry/SEGMENT_1D.h>
#include <Core/Basic_Geometry/SEGMENT_2D.h>
#include <Core/Basic_Geometry/SEGMENT_3D.h>
#include <Core/Math_Tools/RANGE.h>
#include <Core/Vectors/VECTOR.h>
#include <cmath>
namespace PhysBAM{

static const struct RAY_NORMALIZED{} ray_normalized;

template<class TV>
class RAY
{
public:
    typedef typename TV::SCALAR T;
    enum WORKAROUND {d=TV::m};
    typedef VECTOR<int,TV::m> IV;
    TV endpoint; // endpoint of the ray where t=0
    TV direction; // direction the ray sweeps out - unit vector

    RAY()
    {direction(TV::m-1)=1;}

    RAY(const TV& endpoint_input,const TV& direction_input,RAY_NORMALIZED)
        :endpoint(endpoint_input),direction(direction_input)
    {}

    RAY(const TV& endpoint_input,const TV& direction_input)
        :endpoint(endpoint_input),direction(direction_input)
    {direction.Normalize();}

    void Initialize(const TV& endpoint_input,const TV& direction_input,RAY_NORMALIZED)
    {endpoint=endpoint_input;direction=direction_input;}

    void Initialize(const TV& endpoint_input,const TV& direction_input)
    {endpoint=endpoint_input;direction=direction_input;direction.Normalize();}

    TV Point(const T t) const // finds the point on the ray, given by the parameter t
    {return endpoint+t*direction;}

    T Parameter(const TV& point) const // finds the parameter t, given a point that lies on the ray
    {int axis=direction.Dominant_Axis();return (point[axis]-endpoint[axis])/direction[axis];}

    TV Reflected_Direction(const TV& normal) const
    {return direction-2*direction.Dot(normal)*normal;}

    T Set(const TV& endp,const TV& length_and_direction)
    {
        endpoint=endp;
        direction=length_and_direction;
        return direction.Normalize();
    }
//#####################################################################
};

template<class TV> std::ostream &operator<<(std::ostream &output,const RAY<TV> &ray)
{output<<"endpoint = "<<ray.endpoint<<", direction = "<<ray.direction<<", ";
if(ray.semi_infinite) output<<"semi infinite";else output<<"t_max = "<<ray.t_max;output<<std::endl;
return output;
}

template<class TV> bool Get_Intersection_Range(const RAY<TV>& ray,
    const RANGE<TV>& box,typename TV::SCALAR& start_t,typename TV::SCALAR& end_t);
template<class TV> RAY_HIT<typename TV::SCALAR>
Intersects(const RAY<TV>& ray,const RANGE<TV>& box, typename TV::SCALAR isocontour=0);
template<class TV,class T> void
All_Intersections(ARRAY<RAY_HIT<T> >& hits,const RAY<TV>& ray,const RANGE<TV>& box,T isocontour=0);
template<class TV,class T> VECTOR<RAY_HIT<T>,2>
Intersection_Interval(const RAY<TV>& ray,const RANGE<TV>& box,T isocontour=0);
}
#endif
