#pragma once

#include "ElementRenderer.hpp"
#include "ShapeElement.hpp"

namespace MidLevelRenderer {
	class MLRShape;
}

namespace ElementRenderer {

	//#########################################################################
	//#########################  ScalableShapeElement  ################################
	//#########################################################################

	class ScalableShapeElement:
		public ShapeElement
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Initialization
	//
	public:
		static void InitializeClass();
		static void TerminateClass();
		static ClassData *DefaultData;

		typedef ShapeElement BaseClass;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Constructors/Destructors
	//
	protected:
		ScalableShapeElement(
			Stuff::MemoryStream *stream,
			int version,
			ShapeHolder shapes,
			bool read_scale_from_stream
		);

	public:
		ScalableShapeElement();
		explicit ScalableShapeElement(ScalableShapeElement *shape);

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

		void Save(Stuff::MemoryStream *stream);

		static HGOSHEAP s_Heap;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Testing
	//
	public:
		void TestInstance();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Sync support
	//
	public:
		void SetScale(const Stuff::Vector3D &scale)
			{Check_Object(this); m_scale = scale; NeedMatrixSync();}
		const Stuff::Vector3D & GetScale()
			{Check_Object(this); return m_scale;}

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

		Stuff::Vector3D m_scale;

		void CleanSphereSyncMethod();
		void DirtySphereSyncMethod();

		void CleanOBBSyncMethod();
		void DirtyOBBSyncMethod();

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

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Drawing
	//
	public:
		void SetColor(const Stuff::RGBAColor &color)
			{Check_Object(this); m_color = color;}
		const Stuff::RGBAColor& GetColor()
			{Check_Object(this); return m_color;}

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

		Stuff::RGBAColor m_color;

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

}
