//===========================================================================//
// File:	watcher.hpp                                                         //
// Project: MUNGA           Brick: Watcher											  //
// Contents: 						  															  //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  -----------------------------------------------------------//
// 3/02/97  JMA  Initial coding.															  //
//---------------------------------------------------------------------------//
// Copyright (C) 1997, Virtual World Entertainment, Inc.                     //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#pragma once

#include "Adept.hpp"
#include "Channel.hpp"
#include "Entity.hpp"
#include "EntityClassData.hpp"
#include "Attribute.hpp"

namespace Adept {

	//##########################################################################
	//#########################  AttributeWatcherOf  ###########################
	//##########################################################################

	template <class T, int ID> class AttributeWatcherOf:
		public ChannelOf<T>
	{
	public:
		static void
			InitializeClass();
		static void
			TerminateClass();

		typedef ChannelOf<T> BaseClass;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Constructor/Destructor
	//
	public:
		~AttributeWatcherOf();

		static AttributeWatcherOf<T, ID>*
			Make(
				MemoryStream *stream,
				ComponentWeb *owning_web,
				Entity *entity
			);

	protected:
		AttributeWatcherOf(
			ClassData *class_data,
			MemoryStream *stream,
			ComponentWeb *owning_web,
			Entity *entity
		);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Virtual data
	//
	public:
		static ClassData
			*DefaultData;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Data flow
	//
	public:
		void
			Execute();

	protected:
		Entity
			*entity;
		AttributeEntry
			*attributeEntry;
		Scalar
			differenceThreshold;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Tool Support
	//
	public:
		static ClassData*
			CreateFactoryRequest(
				FactoryRequestParameters *parameters,
				Entity::ClassData *class_data
			);

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Test Support
	//
	public:
		void
			TestInstance();
	};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	template <class T, int ID> AttributeWatcherOf<T, ID>::ClassData*
		AttributeWatcherOf<T, ID>::DefaultData = NULL;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	template <class T, int ID> void
		AttributeWatcherOf<T, ID>::TerminateClass()
	{
		Unregister_Object(DefaultData);
		delete DefaultData;
		DefaultData = NULL;
	}

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

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	template <class T, int ID> AttributeWatcherOf<T, ID>::AttributeWatcherOf(
		ClassData *class_data,
		MemoryStream *stream,
		ComponentWeb *owning_web,
		Entity *watched_entity
	):
		ChannelOf<T>(class_data, stream, owning_web)
	{
		int attribute_id;
		*stream >> attribute_id;
		*stream >> differenceThreshold;

		entity = watched_entity;
		Check_Object(entity);
		attributeEntry = entity->GetAttributeEntry(attribute_id);
		Check_Object(attributeEntry);
		Verify(attributeEntry->attributeType == ID);
	}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	template <class T, int ID> void
		AttributeWatcherOf<T, ID>::Execute()
	{
		Check_Object(attributeEntry);
		if (
			attributeEntry->GetChangedValue(
				entity, 
				&channelOutput,
				&channelOutput,
				differenceThreshold
			)
		)
		{
			NotifyDependantsOfChange();
		}
	}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	template <class T, int ID> void
		AttributeWatcherOf<T, ID>::TestInstance()
	{
		Verify(IsDerivedFrom(DefaultData));
	}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	template <class T, int ID> AttributeWatcherOf<T, ID>::ClassData*
		AttributeWatcherOf<T, ID>::CreateFactoryRequest(
			FactoryRequestParameters *parameters,
			Entity::ClassData *class_data
		)
	{
		bool result = BaseClass::CreateFactoryRequest(parameters) != NULL;

		//
		//-------------------------
		// Read the attribute field
		//-------------------------
		//
		MemoryStream *component_stream = parameters->m_stream;
		Check_Object(component_stream);
		Page *page = parameters->m_page;
		Check_Object(page);
		NotationFile *file = page->GetNotationFile();
		Check_Object(file);
		const char* attribute_name;
		page->GetEntry("Attribute", &attribute_name, true);
		const AttributeEntry *attribute_entry;

		Check_Object(class_data);
		attribute_entry = 
			class_data->attributeTable.GetAttributeEntry(attribute_name);
		if (attribute_entry == NULL)
		{
			STOP((
				"%s: {[%s]Attribute=%s}: Unknown attribute!",
				file->GetFileName(),
				page->GetName(),
				attribute_name
			));
			return false;
		}
		Check_Object(attribute_entry);
		if (attribute_entry->attributeType != ID)
		{
			PAUSE((
				"%s: {[%s]Attribute=%s}: Attribute is not correct type!",
				file->GetFileName(),
				page->GetName(),
				attribute_name
			));
			result = false;
		}
		else
			*component_stream << attribute_entry->attributeID;

		//
		//---------------------------------
		// Read in the difference threshold
		//---------------------------------
		//
		Scalar difference = 0.0f;
		page->GetEntry("Threshold", &difference);
		*component_stream << difference;

		Check_Object(DefaultData);
		return (result) ? DefaultData : NULL;
	}

}

