#include "blitz/maths/Physics.h"
#include "blitz/maths/Maths.h"

#include <cmath>

namespace blitz {
namespace maths {

/**
* @brief Returns `true` if the half-line `ray` _strictly_ intersects with `aabb`,
*  and `false` if it _strictly_ doesn't intersect.
*  Note that if it only intersects with corners, edges, or sides of the box, 
*  or if any coordinate in `ray` is `NAN` or `inf` or if any coordinate in
*  `aabb` is `NAN` the result is unspecified.
 * */
float Distance(const Ray& ray, const AABB& aabb) {

	// This function calculates smallest interval I = ] a, b [, for strictly positive a and b
	// such that for all t in I, for all l, r, o, d corresponding
	// coordinates in aabb.from, aabb.to, ray.origin, ray.direction, respectively
	//
	//	min(l, r) < o + t * d < max(l, r)
	// 
	// and returns true if it's non-empty i.e. a < b 

	// m = min(l, r), M = max(l, r)
	// m < o + t * d < M
	Vec3f l = Min(aabb.from, aabb.to);
	Vec3f r = Max(aabb.to, aabb.from);

	// m - o < t * d < M - o
	l -= ray.origin;
	r -= ray.origin;

	// (m - o) / d < t < (M - o) / d
	l /= ray.direction;
	r /= ray.direction;

	// but if d is negative the inequality is flipped
	Vec3f u = Min(l, r);
	r = Max(l, r);
	l = u;

	float tmin = ReduceMax(l);
	float tmax = ReduceMin(r);

	// Since Min propagates NANs and ReduceMin doesn't, and since NAN !< <any float>
	// the inequality becomes ignored for coordinates where a NAN is involved
	// (as a result of 0.0 / 0.0). If all coordinates are NAN, this means
	// that the box is reduced to a point and the ray has direction 0,
	// in which case this returns -1
	if (tmax >= 0.0f && tmin <= tmax) {
		return std::fmaxf(tmin, 0.0f);
	}

	return -1.0f;
}

bool Intersects(const AABB& aabb1, const AABB& aabb2) {
	return false;
}

bool Intersects(const Ray& ray, const AABB& aabb) {
	return Distance(ray, aabb) >= 0.0f;
}

}  // namespace maths
}  // namespace blitz
