#pragma once

#include "ElementRenderer.hpp"
#include "ListElement.hpp"

//#########################################################################
//#########################  SwitchElement  ###############################
//#########################################################################

namespace ElementRenderer {

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

		typedef ListElement BaseClass;

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

	public:
		SwitchElement(ClassData *class_data=DefaultData);
		~SwitchElement();

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

		void Save(Stuff::MemoryStream *stream);

		static HGOSHEAP s_Heap;

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

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Hierarchy
	//
	public:
		void SetSwitch(WORD index)
			{
				Check_Object(this); if (m_switch != index)
				{
					m_switch = index;
					if (m_switch < m_list.GetLength())
					{
						m_list[m_switch]->Sync();
					}
					else if (m_switch & 0xf0) // special case
					{
						Element *element = GetFirstSwitchElement();
						if (element)
						{
							Check_Object(element);
							element->Sync();
						}
						element = GetSecondSwitchElement();
						if (element)
						{
							Check_Object(element);
							element->Sync();
						}
					}
				}
			}
		WORD GetSwitch()
			{Check_Object(this); return m_switch;}
		void SetSize(WORD max_size);

		Element* GetFirstSwitchElement()
			{
				int nSwitch = m_switch & 0x0f;
				if (nSwitch < m_list.GetLength())
					return m_list[nSwitch];
				return NULL;
			}
		
		Element* GetSecondSwitchElement()
			{
				int nSwitch = (m_switch & 0xf0) >> 4;
				if (nSwitch < m_list.GetLength())
					return m_list[nSwitch];
				return NULL;
			}
		
	protected:
		WORD m_switch;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Sync support
	//
	protected:
		void SetSyncState();
		static SyncMethod SyncMethods[SyncStateCount];

		void CleanRootSyncMethod();
		void DirtyRootSyncMethod();

		void CleanSyncMethod();
		void DirtySyncMethod();

		void CleanSphereSyncMethod();
		void MatrixDirtySphereSyncMethod();
		void BoundsWrongSphereSyncMethod();
		void FullSphereSyncMethod();

		void CleanOBBSyncMethod();
		void MatrixDirtyOBBSyncMethod();
		void BoundsWrongOBBSyncMethod();
		void FullOBBSyncMethod();

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// 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
		);

	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
		);
	};

}
