#pragma once

#include "ElementRenderer.hpp"
#include "Element.hpp"

namespace MidLevelRenderer {
	class MLRShape;
}

namespace ElementRenderer {

	//#########################################################################
	//#########################  ShapeElement  ################################
	//#########################################################################

	class ShapeElement:
		public Element
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Initialization
	//
	public:
		static void InitializeClass(Stuff::NotationFile *startup_ini);
		static void TerminateClass(Stuff::NotationFile *startup_ini);
		static ClassData *DefaultData;

		typedef Element BaseClass;

		static bool s_ShowClippedBounds;
		static bool s_CheckBounds;
		static bool s_BoundsWereBad;
		static int s_MaxLightsPerShape;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Constructors/Destructors
	//
	protected:
		ShapeElement(
			ClassData *class_data,
			Stuff::MemoryStream *stream,
			int version,
			ShapeHolder shapes
		);

	public:
		ShapeElement(ClassData *class_data = DefaultData);
		explicit ShapeElement(
			ShapeElement *shape,
			ClassData *class_data = DefaultData
		);
		~ShapeElement();

		static ShapeElement* Make(
			Stuff::MemoryStream *stream,
			int version,
			ShapeHolder shapes=NULL
		);

		void Save(Stuff::MemoryStream *stream);

		static HGOSHEAP s_Heap;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Testing
	//
	public:
		void TestInstance();
		bool CheckBounds();
		static bool CheckSphereBounds(
			MidLevelRenderer::MLRShape *shape,
			const Stuff::OBB &sphere
		);
		static bool CheckOBBBounds(
			MidLevelRenderer::MLRShape *shape,
			const Stuff::OBB &obb
		);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Hierarchy
	//
	protected:
		void AttachChild(Element *child);
		void DetachChild(Element *child);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Sync support
	//
	public:
		const Stuff::LinearMatrix4D& GetWorldToLocal()
			{
				Check_Object(this); Check_Object(&m_worldToLocal);
				return m_worldToLocal;
			}

	protected:
		void SetSyncState();
		static SyncMethod SyncMethods[SyncStateCount];

		Stuff::LinearMatrix4D m_worldToLocal;

		void DirtyRootSyncMethod();
		void DirtySyncMethod();

		void CleanSphereSyncMethod();
		void DirtySphereSyncMethod();

		void CleanOBBSyncMethod();
		void DirtyOBBSyncMethod();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Collision
	//
	protected:
		bool CastCulledRay(CollisionQuery *query);
		Element* FindSmallestElementContainingCulled(SphereTest *test);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Drawing
	//
	protected:
		void SetDrawState();
		static DrawMethod DrawMethods[DrawStateCount];

		void InheritDrawMethod(
			CameraElement *camera,
			const StateChange *inherited_state,
			int clipping_state
		);
		void OverrideDrawMethod(
			CameraElement *camera,
			const StateChange *inherited_state,
			int clipping_state
		);

		int CullLights(
			LightList culled_lights,
			const LightList lights
		);

		int CountTriangles();

	public:
		void CleanDamage();

		void ApplyDamage(
			const Stuff::LinearMatrix4D &damage_spot,
			Stuff::Scalar radius
		);

		void ApplyDamageDecal(
			const Stuff::LinearMatrix4D &damage_spot,
			Stuff::Scalar radius,
			MidLevelRenderer::MLRTexture *texture
		);

		void CastShadow(
			const Stuff::LinearMatrix4D &shadow_to_world,
			const Stuff::UnitVector3D &sun_in_world,
			Stuff::Scalar radius
		);

		void MakeFootStep(
			MidLevelRenderer::MLRShape *shape,
			const Stuff::LinearMatrix4D &foot,
			Stuff::Scalar radius,
			MidLevelRenderer::MLRTexture *tex
		);

		MidLevelRenderer::MLRShape* GetMLRShape(CameraElement *camera=NULL)
			{Check_Object(this); return m_MLRShape;}
		void SetMLRShape(MidLevelRenderer::MLRShape *shape);

		void GetCoordData(Stuff::DynamicArrayOf<Stuff::Point3D> *array);

	protected:
		MidLevelRenderer::MLRShape *m_MLRShape;
	};

}
