//===========================================================================//
// File: GameModelAttribute.hpp                                                       //
//---------------------------------------------------------------------------//
//

#pragma once

#include "Adept.hpp"
#include "Entity.hpp"

namespace Adept {

	//##########################################################################
	//########################    ModelAttributeEntry    #######################
	//##########################################################################

	class ModelAttributeEntry:
		public Stuff::Plug
	{

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	public:
		ModelAttributeEntry(
			Entity__GameModel::AttributeID attribute_ID,
			const char *attribute_name,
			RegisteredClass::ClassID attribute_type,
			Entity__GameModel::AttributePointer attribute_address
		);
		virtual 
			~ModelAttributeEntry();

		void
			TestInstance() const
				{}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	public:
		virtual void
			SetValue(
				Entity__GameModel *model, 
				void *new_value
			);

		virtual void
			GetValue(
				Entity__GameModel *model, 
				void *new_value
			);

		virtual bool
			GetChangedValue(
				Entity__GameModel *model, 
				void *new_value,
				void *current_value,
				Stuff::Scalar difference_threshold
			);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	public:
		Entity__GameModel::AttributeID
			attributeID;
		Stuff::MString
			attributeName;
		RegisteredClass::ClassID
			attributeType;
		Entity__GameModel::AttributePointer
			attributeAddress;
	};

	//##########################################################################
	//######################    DirectAttributeEntryOf    ######################
	//##########################################################################

	template <class T, int ID> class DirectModelAttributeEntryOf:
		public ModelAttributeEntry
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	public:
		DirectModelAttributeEntryOf(
			Entity__GameModel::AttributeID attribute_ID,
			const char *attribute_name,
			Entity__GameModel::AttributePointer attribute_address
		);
		~DirectModelAttributeEntryOf();

		void
			TestInstance() const
				{}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	public:
		void
			SetValue(
				Entity__GameModel *model, 
				void *new_value
			);

		bool
			GetChangedValue(
				Entity__GameModel *model, 
				void *new_value,
				void *current_value,
				Stuff::Scalar difference_threshold
			);
		void
			GetValue(
				Entity__GameModel *model, 
				void *new_value
			);
	};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	template <class T, int ID>
		DirectModelAttributeEntryOf<T, ID>::DirectModelAttributeEntryOf(
			Entity__GameModel::AttributeID attribute_ID,
			const char *attribute_name,
			Entity__GameModel::AttributePointer attribute_address
		):
			ModelAttributeEntry(
				attribute_ID,
				attribute_name,
				ID,
				attribute_address
			)			
	{
	}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	template <class T, int ID>
		DirectModelAttributeEntryOf<T, ID>::~DirectModelAttributeEntryOf()
	{
	}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	SetValue(
		Entity__GameModel *model, 
		void *new_value
	);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	GetValue(
		Entity__GameModel *model, 
		void *new_value
	);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	template <class T, int ID> bool
		DirectModelAttributeEntryOf<T, ID>::GetChangedValue(
			Entity__GameModel *model, 
			void *new_value,
			void *current_value,
			Stuff::Scalar diff_threshold
		)
	{
		Check_Object(model);
		Check_Pointer(new_value);
		Check_Pointer(current_value);
		Verify(attributeAddress != NULL);

		void *pointer = &(model->*attributeAddress);

		if (
			!Close_Enough(
				*Cast_Pointer(T*, pointer),
				*Cast_Pointer(T*, current_value),
				diff_threshold
			)
		)
		{
			*Cast_Pointer(T*, new_value) = *Cast_Pointer(T*, pointer);
			return true;
		}
		return false;
	}




	//##########################################################################
	//########################    ModelAttributeTable    #######################
	//##########################################################################

	class ModelAttributeTable
		#if defined(_ARMOR)
			: public Stuff::Signature
		#endif
	{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	public:
		ModelAttributeTable();
		~ModelAttributeTable();

		void
			TestInstance()
				{}
	
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	public:
		void
			CopyFrom(ModelAttributeTable *attribute_table);

		void
			AddAttributeEntry(ModelAttributeEntry *attribute_entry);

		ModelAttributeEntry*
			GetAttributeEntry(Entity__GameModel::AttributeID attribute_ID);
		ModelAttributeEntry*
			GetAttributeEntry(const char *attribute_name);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	public:
		Stuff::TableOf<ModelAttributeEntry*, Entity__GameModel::AttributeID>
			attributesByID;
		Stuff::TableOf<ModelAttributeEntry*, Stuff::MString>
			attributesByName;

		#if defined(_ARMOR)
			bool
				verifyTable;
			void
				VerifyTable();
		#endif
	};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	inline ModelAttributeEntry*
		ModelAttributeTable::GetAttributeEntry(Entity__GameModel::AttributeID attribute_ID)
	{
		#if defined(_ARMOR)
			VerifyTable();
		#endif
		Stuff::TableIteratorOf<ModelAttributeEntry*, Entity__GameModel::AttributeID>
			iterator(&attributesByID);
		return iterator.GetNth(attribute_ID);
	}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	inline ModelAttributeEntry*
		ModelAttributeTable::GetAttributeEntry(const char *attribute_name)
	{
		#if defined(_ARMOR)
			VerifyTable();
		#endif
		return attributesByName.Find(attribute_name);
	}

}

