#pragma once

#include "extntbox.h"

class Line;
class Point3D;
class Origin;
class BoundingBoxTreeNode;
class BoundingBoxList;
class BoundingBoxCollision;

//##########################################################################
//###########################    BoundingBox    ############################
//##########################################################################

class BoundingBox:
	public ExtentBox
{
	friend class BoundingBoxTreeNode;
	friend class BoundingBoxList;

public:
	BoundingBox();
	BoundingBox(const ExtentBox &extents);
	virtual ~BoundingBox();

	Logical
		Intersects(const ExtentBox &extents);
	Logical
		Intersects(
			const ExtentBox &extents,
        ExtentBox *slice
		);
	Logical
		Contains(const Point3D &point);
	Scalar
		FindDistanceBelow(const Point3D &point);
	Logical
		HitBy(Line *line);

	void
		PlaceBoundingBox(const Origin& origin);

protected:
	virtual Logical
		IntersectsBounded(const ExtentBox &extents);
	virtual Logical
		ContainsBounded(const Point3D &point);
	virtual Scalar
		FindDistanceBelowBounded(const Point3D &point);
	virtual Logical
		HitByBounded(
			Line *line,
			Scalar enters,
			Scalar leaves
		);

public:
	static Logical
		TestClass();
	virtual Logical
  	TestInstance() const;
};

//##########################################################################
//######################    BoundingBoxCollision    ########################
//##########################################################################

class BoundingBoxCollision SIGNATURED
{
public:
	BoundingBox
		*treeVolume;
	ExtentBox
		collisionSlice;

	BoundingBox*
		GetTreeVolume()
			{Check(this); return treeVolume;}
	Logical
		TestInstance() const;
};

//##########################################################################
//####################    BoundingBoxCollisionList    ######################
//##########################################################################

class BoundingBoxCollisionList SIGNATURED
{
	friend class BoundingBoxTreeNode;
	friend class BoundingBoxList;

protected:
	BoundingBoxCollision
		*listStart,
		*nextCollision;
	int
		maxCollisions,
		collisionsLeft;

public:
	BoundingBoxCollisionList(int max_length);
	~BoundingBoxCollisionList()
		{Check(this); Unregister_Pointer(listStart); delete[] listStart;}
	void
		Reset()
			{nextCollision	= listStart; collisionsLeft = maxCollisions;}

	void
		AddCollisionToList(
			BoundingBox *tree_volume,
			const ExtentBox &slice
		);

	BoundingBoxCollision&
		operator[](int index)
			{
				Check(this); Verify((unsigned)index < maxCollisions);
				return listStart[index];
			}
	int
		GetCollisionCount()
			{Check(this); return maxCollisions - collisionsLeft;}
	int
		GetCollisionsLeft()
			{Check(this); return collisionsLeft;}

	Logical
		TestInstance() const;
};

//##########################################################################
//########################    TaggedBoundingBox    #########################
//##########################################################################

class TaggedBoundingBox:
	public BoundingBox
{
public:
	void*
		GetTagPointer()
			{Check(this); return tagPointer;}

protected:
	TaggedBoundingBox(void *tag = NULL);
	TaggedBoundingBox(
		const ExtentBox &extents,
     void *tag = NULL
	);
	~TaggedBoundingBox();

	void *tagPointer;
};

//##########################################################################
//#######################    TaggedBoundingBoxOf    ########################
//##########################################################################

template <class T> class TaggedBoundingBoxOf:
	public TaggedBoundingBox
{
public:
	TaggedBoundingBoxOf(T *tag);
	TaggedBoundingBoxOf(
		const ExtentBox &extents,
     T *tag = NULL
	);
	~TaggedBoundingBoxOf();

	T*
		GetTagPointer()
			{return (T*)tagPointer;}
};

//~~~~~~~~~~~~~~~~~~~~~~~~ TaggedBoundingBoxOf templates ~~~~~~~~~~~~~~~~~~~

template <class T>
	TaggedBoundingBoxOf<T>::TaggedBoundingBoxOf(T *tag):
  	TaggedBoundingBox(tag)
{
}

template <class T>
	TaggedBoundingBoxOf<T>::TaggedBoundingBoxOf(
		const ExtentBox &extents,
     T *tag
	):
  	TaggedBoundingBox(extents, tag)
{
}

template <class T>
	TaggedBoundingBoxOf<T>::~TaggedBoundingBoxOf()
{
}
