#pragma once

#include "bndgbox.h"
#include "boxtree.h"
#include "boxlist.h"
#include "resource.h"

struct BoxedSolidResource;
class BoxedSolidCollision;
class BoxedSolidCollisionList;
class Simulation;
class NotationFile;
class Normal;

//##########################################################################
//###########################    BoxedSolid    #############################
//##########################################################################

class BoxedSolid:
	public TaggedBoundingBox
{
public:
	enum Type {
		BlockType=0,
		SphereType=1,
		ConeType=2,
		ReducibleBlockType=3,
		RampType=4,
		RampFacingNegativeZType=4,
		RampFacingNegativeXType,
		RampFacingPositiveZType,
		RampFacingPositiveXType,
		InvertedRampType=8,
		InvertedRampFacingNegativeZType=8,
		InvertedRampFacingNegativeXType,
		InvertedRampFacingPositiveZType,
		InvertedRampFacingPositiveXType,
		WedgeType=12,
		WedgeFacingNegativeZAndPositiveXType=12,
		WedgeFacingNegativeZAndNegativeXType,
		WedgeFacingPositiveZAndNegativeXType,
		WedgeFacingPositiveZAndPositiveXType,
		XAxisCylinderType=16,
		YAxisCylinderType=17,
		ZAxisCylinderType=18,
		RightHandedTileType=19,
		LeftHandedTileType=20,
		SolidTypeCount
	}
		solidType;

	enum Material {
		StoneMaterial = 0,
		GravelMaterial,
		ConcreteMaterial,
		SteelMaterial,
		WoodMaterial,
		RockMaterial,
		OurCraftMaterial,
     OtherCraftMaterial,
		MaterialCount
	}
		materialType;

	BoxedSolid(
		const ExtentBox &extents,
		Type type,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	BoxedSolid(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedSolid();

	static BoxedSolid*
		MakeBoxedSolid(
			BoxedSolidResource *resource,
			Simulation *owner,
			BoxedSolid *next_solid
		);

	virtual Logical
		VerifyCollision(BoxedSolidCollision &collision);
	virtual Logical
		ProcessCollision(
			BoxedSolidCollision &collision,
			const Vector3D &velocity,
			BoxedSolidCollisionList *last_collisions,
			Normal *normal,
			Scalar *penetration
		);

	Logical
		TestInstance() const;
	static Logical
		TestClass();

	Simulation*
		GetOwningSimulation()
			{Check(this); return (Simulation*)GetTagPointer();}
	BoxedSolid*
		GetNextSolid()
			{Check(this); return nextSolid;}

	protected:
		BoxedSolid
			*nextSolid;
};

//##########################################################################
//#######################    BoxedSolidCollision    ########################
//##########################################################################

class BoxedSolidCollision:
	public BoundingBoxCollision
{
public:
	BoxedSolid*
		GetTreeVolume()
			{Check(this); return (BoxedSolid*)treeVolume;}
	Logical
		Occludes(
			BoxedSolidCollision &collision,
			const Vector3D &velocity
		);
};

//##########################################################################
//#####################    BoxedSolidCollisionList    ######################
//##########################################################################

class BoxedSolidCollisionList:
	public BoundingBoxCollisionList
{
protected:
	int phantomCollisions;

public:
	BoxedSolidCollisionList(int max_length = 10):
		BoundingBoxCollisionList(max_length)
			{Reset();}
	void
		Reset()
			{phantomCollisions = 0; BoundingBoxCollisionList::Reset();}

	BoxedSolidCollision&
		operator[](int index)
			{
				Check(this); Verify((unsigned)index < maxCollisions);
				return ((BoxedSolidCollision*)listStart)[index];
			}
	int
		GetRealCollisions()
			{Check(this); return GetCollisionCount() - phantomCollisions;}

	void
		ReduceCollisionList(const Vector3D &velocity);
};

//##########################################################################
//#######################    BoxedSolidResource    #########################
//##########################################################################

struct BoxedSolidResource
{
	size_t recordLength;
	ExtentBox
		solidExtents,
		sliceExtents;
	BoxedSolid::Type solidType;
	BoxedSolid::Material materialType;

	void
		Instance(
			const BoxedSolidResource &source,
			const Origin& origin
		);

	static ResourceDescription::ResourceID
		CreateBoxedSolidStream(
			const char *entry_data,
			ResourceFile *file,
			const ResourceDirectories *resource_directories,
			Logical convert_boxes = False
		);
};

//##########################################################################
//###########################    BoxedSphere    ############################
//##########################################################################

class BoxedSphere:
	public BoxedSolid
{
public:
	BoxedSphere(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedSphere();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//############################    BoxedCone    #############################
//##########################################################################

class BoxedCone:
	public BoxedSolid
{
public:
	BoxedCone(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedCone();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//#######################    BoxedReducibleBlock    ########################
//##########################################################################

class BoxedReducibleBlock:
	public BoxedSolid
{
public:
	BoxedReducibleBlock(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedReducibleBlock();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//######################    BoxedRampFacingNegativeZ    ####################
//##########################################################################

class BoxedRampFacingNegativeZ:
	public BoxedSolid
{
public:
	BoxedRampFacingNegativeZ(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedRampFacingNegativeZ();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//####################    BoxedRampFacingPositiveX    ######################
//##########################################################################

class BoxedRampFacingPositiveX:
	public BoxedSolid
{
public:
	BoxedRampFacingPositiveX(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedRampFacingPositiveX();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//####################    BoxedRampFacingPositiveZ    ######################
//##########################################################################

class BoxedRampFacingPositiveZ:
	public BoxedSolid
{
public:
	BoxedRampFacingPositiveZ(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedRampFacingPositiveZ();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//####################    BoxedRampFacingNegativeX    ######################
//##########################################################################

class BoxedRampFacingNegativeX:
	public BoxedSolid
{
public:
	BoxedRampFacingNegativeX(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedRampFacingNegativeX();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//##################    BoxedInvertedRampFacingNegativeZ    ################
//##########################################################################

class BoxedInvertedRampFacingNegativeZ:
	public BoxedSolid
{
public:
	BoxedInvertedRampFacingNegativeZ(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedInvertedRampFacingNegativeZ();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//##################    BoxedInvertedRampFacingNegativeX    ################
//##########################################################################

class BoxedInvertedRampFacingNegativeX:
	public BoxedSolid
{
public:
	BoxedInvertedRampFacingNegativeX(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedInvertedRampFacingNegativeX();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//##################    BoxedInvertedRampFacingPositiveZ    ################
//##########################################################################

class BoxedInvertedRampFacingPositiveZ:
	public BoxedSolid
{
public:
	BoxedInvertedRampFacingPositiveZ(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedInvertedRampFacingPositiveZ();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//##################    BoxedInvertedRampFacingPositiveX    ################
//##########################################################################

class BoxedInvertedRampFacingPositiveX:
	public BoxedSolid
{
public:
	BoxedInvertedRampFacingPositiveX(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedInvertedRampFacingPositiveX();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//##############    BoxedWedgeFacingNegativeZAndPositiveX    ###############
//##########################################################################

class BoxedWedgeFacingNegativeZAndPositiveX:
	public BoxedSolid
{
public:
	BoxedWedgeFacingNegativeZAndPositiveX(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedWedgeFacingNegativeZAndPositiveX();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//##############    BoxedWedgeFacingPositiveZAndPositiveX    ###############
//##########################################################################

class BoxedWedgeFacingPositiveZAndPositiveX:
	public BoxedSolid
{
public:
	BoxedWedgeFacingPositiveZAndPositiveX(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedWedgeFacingPositiveZAndPositiveX();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//##############    BoxedWedgeFacingPositiveZAndNegativeX    ###############
//##########################################################################

class BoxedWedgeFacingPositiveZAndNegativeX:
	public BoxedSolid
{
public:
	BoxedWedgeFacingPositiveZAndNegativeX(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedWedgeFacingPositiveZAndNegativeX();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//##############    BoxedWedgeFacingNegativeZAndNegativeX    ###############
//##########################################################################

class BoxedWedgeFacingNegativeZAndNegativeX:
	public BoxedSolid
{
public:
	BoxedWedgeFacingNegativeZAndNegativeX(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedWedgeFacingNegativeZAndNegativeX();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//#######################    BoxedXAxisCylinder    #########################
//##########################################################################

class BoxedXAxisCylinder:
	public BoxedSolid
{
public:
	BoxedXAxisCylinder(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedXAxisCylinder();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//#######################    BoxedYAxisCylinder    #########################
//##########################################################################

class BoxedYAxisCylinder:
	public BoxedSolid
{
public:
	BoxedYAxisCylinder(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedYAxisCylinder();

	Logical
		VerifyCollision(BoxedSolidCollision &collision);
	Logical
		ProcessCollision(
			BoxedSolidCollision &collision,
			const Vector3D &velocity,
			BoxedSolidCollisionList *last_collisions,
			Normal *normal,
		Scalar *penetration
		);

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

	Logical
		TestInstance() const;
};

//##########################################################################
//#######################    BoxedZAxisCylinder    #########################
//##########################################################################

class BoxedZAxisCylinder:
	public BoxedSolid
{
public:
	BoxedZAxisCylinder(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid
	);
	~BoxedZAxisCylinder();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//#####################    RightHandedTile    #######################
//##########################################################################

class RightHandedTile:
	public BoxedSolid
{
public:
	RightHandedTile(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid,
		Scalar *cornerHeights,
		Type type=RightHandedTileType
	);
	~RightHandedTile();

	Scalar
		cornerHeight[4];

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

	Logical
		TestInstance() const;
};

//##########################################################################
//########################    LeftHandedTile    #########################
//##########################################################################

class LeftHandedTile:
	public RightHandedTile
{
public:
	LeftHandedTile(
		const ExtentBox &extents,
		Material material,
		Simulation *owner,
		BoxedSolid *next_solid,
		Scalar *cornerHeights
	);
	~LeftHandedTile();

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

	Logical
		TestInstance() const;
};

//##########################################################################
//#######################    TerrainTileResource    ########################
//##########################################################################

struct TileResource:
	public BoxedSolidResource
{
	Scalar
		cornerHeight[4];
		
	void
		Instance(
			const TileResource &source,
			const Origin& origin
		);
};

//##########################################################################
//########################    BoxedSolidTree    ############################
//##########################################################################

class BoxedSolidTree:
	public BoundingBoxTree
{

//##########################################################################
// Construction and Destruction
//
public:
	BoxedSolidTree()
		{}
	~BoxedSolidTree()
		{}

//##########################################################################
// Tree Traversal Functions
//
public:
	BoxedSolid*
		FindBoundingBoxContaining(const Point3D &point)
			{
				return
					(BoxedSolid*)
						BoundingBoxTree::FindBoundingBoxContaining(point);
			}

	BoxedSolid*
		FindBoundingBoxUnder(
			const Point3D &point,
			Scalar *height
		)
			{
				return
					(BoxedSolid*)
						BoundingBoxTree::FindBoundingBoxUnder(point, height);
			}

	BoxedSolid*
		FindBoundingBoxHitBy(Line *line)
			{return (BoxedSolid*)BoundingBoxTree::FindBoundingBoxHitBy(line);}
};

//##########################################################################
//########################    BoxedSolidList    ############################
//##########################################################################

class BoxedSolidList:
	public BoundingBoxList
{
//##########################################################################
// Construction and Destruction
//
public:
	BoxedSolidList() {}
	~BoxedSolidList() {}

//##########################################################################
// Tree Traversal Functions
//
public:
	int
		AddBoxedSolids(NotationFile *notation_file);
				
	BoxedSolid*
		FindBoundingBoxContaining(const Point3D &point)
			{
				return
					(BoxedSolid*)
						BoundingBoxList::FindBoundingBoxContaining(point);
			}

	BoxedSolid*
		FindBoundingBoxUnder(
			const Point3D &point,
			Scalar *height
		)
			{
				return
					(BoxedSolid*)
						BoundingBoxList::FindBoundingBoxUnder(point, height);
			}

	BoxedSolid*
		FindBoundingBoxHitBy(Line *line)
			{return (BoxedSolid*)BoundingBoxList::FindBoundingBoxHitBy(line);}

	void
		Reduce();
};
