#pragma once

#include "normal.h"
#include "point3d.h"

class Sphere;
class ExtentBox;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Plane ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class Plane SIGNATURED
{
public:
	//
	// The plane equation is P*N = 0, where P is a homogeneous point, and N
	// is a quadruple representing the plane.  Due to some slight
	// improvements gained when the offset is negated, a negative offset is
	// stored.  This must be taken into account whenever we are doing the
	// point-to-plane dot product, where we must do a subtraction instead of
	// an addition
	//
	Normal normal;
	Scalar offset;

	//
	// Constructors
	//
	Plane()	{}
	Plane(Scalar x, Scalar y, Scalar z, Scalar offset) : normal(x,y,z), offset(offset)	{}
	Plane(const Normal& n, Scalar offset) : normal(n), offset(offset)	{}
	Plane(const Point3D& p0, const Point3D& p1, const Point3D& p2);

	//
	// Transform functions
	//
	Plane& Multiply(const Plane &p, const LinearMatrix &m);
	Plane& operator*=(const LinearMatrix &m)
	{
		Check(this);
		Plane t(*this);
		return Multiply(t,m);
	}

	//
	// half-space division functions
	//
	Logical SeenBy(const Vector3D &A_Vector) const	{ return normal * A_Vector < 0.0; }
	Logical SeenBy(const Point3D &A_Point) const	{ return normal * A_Point > offset; }
	Scalar DistanceTo(const Point3D& A_Point) const	{ return normal * A_Point - offset; }

	//
	// half-space containment functions
	//
	Logical Contains(const Point3D &point) const;
	Logical ContainsSomeOf(const Sphere &sphere) const;
	Logical ContainsAllOf(const Sphere &sphere) const;
	Logical ContainsSomeOf(const ExtentBox &box) const;
	Logical ContainsAllOf(const ExtentBox &box) const;

	//
	// plane surface intersection functions
	//
	Logical Intersect(const Sphere &sphere) const;
	Logical Intersect(const ExtentBox &box) const;

	friend std::ostream& operator<<(std::ostream& Stream, const Plane &A_Plane);
	Logical TestInstance() const;
	static Logical TestClass();
		
	//
	// Equation solutions
	//
	Scalar CalculateX(Scalar y, Scalar z);
	Scalar CalculateY(Scalar x, Scalar z);
	Scalar CalculateZ(Scalar x, Scalar y);
};
