//===========================================================================//
// File:	cmpnnt.cc                                                           //
// Project: MUNGA           Brick: Entity												  //
// Contents: 																					  //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 12/14/94  ECH  Initial coding.                                            //
//---------------------------------------------------------------------------//
// Copyright (C) 1994-1995, Virtual World Entertainment, Inc.                //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "AdeptHeaders.hpp"
#include "Entity.hpp"
#include "Channel.hpp"

ComponentWeb::ClassData*
	ComponentWeb::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ComponentWeb::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			ComponentWebClassID,
			"Adept::ComponentWeb",
			Plug::DefaultData
		);
	Register_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ComponentWeb::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ComponentWeb::ComponentWeb(
	ClassData *class_data,
	const ResourceID &script_id,
	ComponentWeb *parent_web
):
	Plug(class_data),
	ownedComponents(NULL),
	ownedWebs(NULL),
	watcherComponents(NULL),
	temporaryWatchers(NULL, false),
	commandReceivers(NULL)
{
	//
	//-----------------------------
	// Initialize the web variables
	//-----------------------------
	//
	parentWeb = parent_web;
	scriptResourceID = script_id;

	//
	//-------------------------------------
	// If we belong to another web, hook up
	//-------------------------------------
	//
	if (parentWeb)
	{
		Check_Object(parentWeb);
		parentWeb->AddComponentWeb(this);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ComponentWeb::~ComponentWeb()
{
	Check_Object(this);

	//
	//----------------------------------
	// Delete any components that we own
	//----------------------------------
	//
	ChainIteratorOf<Component*> components(&ownedComponents);
	Component *component;
	while ((component = components.ReadAndNext()) != NULL)
	{
		Unregister_Object(component);
		delete component;
	}

	//
	//------------------------------------------------------
	// Delete any component webs we own if they don't object
	//------------------------------------------------------
	//
	ChainIteratorOf<ComponentWeb*> webs(&ownedWebs);
	ComponentWeb *web;
	while ((web = webs.ReadAndNext()) != NULL)
	{
		Check_Object(web);
		if (web->ShouldWebBeKilled())
		{
			Unregister_Object(web);
			delete web;
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	ComponentWeb::ShouldWebBeKilled()
{
	Check_Object(this);
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Component*
	ComponentWeb::AddComponent(Component *component)
{
	Check_Object(this);
	Check_Object(component);

	//
	//---------------------------------------------------------------
	// If the component is a watcher, hook it up to our watcher chain
	//---------------------------------------------------------------
	//
	if (component->ShouldSimulationExecute())
	{
		Verify(!component->ShouldRendererExecute());
		watcherComponents.Add(component);
	}

	//
	//-----------------------------------------------------------------------
	//If the component can process commands, hook it up the the command chain
	//-----------------------------------------------------------------------
	//
	if (component->DoesReceiveCommands())
		commandReceivers.Add(component);

	ownedComponents.Add(component);
	return component;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ComponentWeb::AddComponentWeb(ComponentWeb *web)
{
	Check_Object(this);
	Check_Object(web);
	ownedWebs.Add(web);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Component*
	ComponentWeb::FindComponent(const ComponentID &component_id)
{
	Check_Object(this);

	//
	//----------------------------
	// See if we own the component
	//----------------------------
	//
	ChainIteratorOf<Component*> components(&ownedComponents);
	Component *component;
	while ((component = components.ReadAndNext()) != NULL)
	{
		Check_Object(component);
		if (component->GetComponentID() == component_id)
		{
			break;
		}
	}

	//
	//----------------------------------
	// It can't be found, so return NULL
	//----------------------------------
	//
	return component;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ComponentWeb::ExecuteWatcherComponents()
{
	Check_Object(this);

	//
	//-------------------------------
	// Execute the permanent watchers
	//-------------------------------
	//
	if (!watcherComponents.IsEmpty())
	{
		ChainIteratorOf<Component*> components(&watcherComponents);
		Component *component;
		while ((component = components.ReadAndNext()) != NULL)
		{
			Check_Object(component);
			component->Execute();
		}
	}

	//
	//------------------------------------
	// Now, execute any temporary watchers
	//------------------------------------
	//
	if (!temporaryWatchers.IsEmpty())
	{
		SortedChainIteratorOf<Component*, int> temporaries(&temporaryWatchers);
		Component *component;
		while ((component = temporaries.GetCurrent()) != NULL)
		{
			Check_Object(component);
			component->Execute();
			component->ClearSimulationShouldExecuteOnce();
			temporaries.Remove();
			temporaries.First();
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ComponentWeb::AddTemporaryWatcher(Component *component)
{
	Check_Object(this);
	Check_Object(component);
	if (!component->ShouldSimulationExecuteOnce())
	{
		temporaryWatchers.AddValue(component, component->GetWatcherPriority());
		component->SetSimulationShouldExecuteOnce();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ComponentWeb::SendCommand(int component_command)
{
	Check_Object(this);

	Channel command_channel;
	command_channel.SetCommand(component_command);

	Component *component;
	ChainIteratorOf<Component*> iterator(&commandReceivers);

	while((component = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(component);
		component->ChannelChanged(&command_channel);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ComponentWeb::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));
}


EntityComponentWeb::ClassData*
	EntityComponentWeb::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityComponentWeb::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			EntityComponentWebClassID,
			"Adept::EntityComponentWeb",
			ComponentWeb::DefaultData
		);
	Register_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityComponentWeb::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityComponentWeb::EntityComponentWeb(
	ClassData *class_data,
	Entity *entity,
	const ResourceID &script_id,
	EntityComponentWeb *parent_web
):
	ComponentWeb(class_data, script_id, parent_web)
{
	Check_Object(entity);

	//
	//-----------------------------
	// Initialize the web variables
	//-----------------------------
	//
	owningEntity = entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityComponentWeb::~EntityComponentWeb()
{
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	EntityComponentWeb::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));
}

RendererComponentWeb::ClassData*
	RendererComponentWeb::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	RendererComponentWeb::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			RendererComponentWebClassID,
			"Adept::RendererComponentWeb",
			EntityComponentWeb::DefaultData
		);
	Register_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	RendererComponentWeb::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
RendererComponentWeb::RendererComponentWeb(
	ClassData *class_data,
	Entity *entity,
	Renderer *renderer,
	const ResourceID &script_id,
	RendererComponentWeb *parent_web
):
	EntityComponentWeb(class_data, entity, script_id, parent_web)
{
	Check_Object(renderer);

	//
	//-----------------------------
	// Initialize the web variables
	//-----------------------------
	//
	owningRenderer = renderer;
	renderer->AddComponentWeb(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
RendererComponentWeb::~RendererComponentWeb()
{
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	RendererComponentWeb::LoadComponentWeb()
{
	Check_Object(this);

	//
	//-------------------------------
	// Now, find the component stream
	//-------------------------------
	//
	if (scriptResourceID == ResourceID::Null)
	{
		return;
	}
	Resource script(scriptResourceID);

	//
	//-----------------------------------------
	// Create the components, then execute them
	//-----------------------------------------
	//
	LoadFromStream(&script);
	ExecuteWatcherComponents();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	RendererComponentWeb::LoadFromStream(MemoryStream *stream)
{
	Check_Object(this);
	Check_Object(stream);
	Check_Object(owningEntity);

	//
	//--------------------------------------
	// Loop through and build each component
	//--------------------------------------
	//
	int count;
	*stream >> count;
	Warn(count == 0);
	while (count--)
	{
		//
		//---------------------
		// Create the component
		//---------------------
		//
		int length;
		#if defined(_ARMOR)
			char *record_end = Cast_Pointer(char*, stream->GetPointer());
			*stream >> length;
			record_end += length;
		#else
			*stream >> length;
		#endif
		ClassID class_id;
		*stream >> class_id;
		Component *component =
			owningRenderer->CreateComponent(
				class_id,
				stream,
				this,
				owningEntity
			);

		//
		//-----------------------------------------------------------------
		// Add it to the web, then check to see if we need to add it to the
		// renderer execution lists
		//-----------------------------------------------------------------
		//
		AddComponent(component);
		Verify(record_end == Cast_Pointer(char*, stream->GetPointer()));
		if (component->ShouldRendererExecute())
		{
			Verify(!component->ShouldSimulationExecute());
			owningRenderer->AddExecutableComponent(component);
		}

		//
		//----------------------------------------------------------------------
		// Any simulation executed components must run in post collision, so set
		// the entity appropriately
		//----------------------------------------------------------------------
		//
		if (component->ShouldSimulationExecute())
			owningEntity->UsePostCollision();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Component*
	RendererComponentWeb::AddComponent(Component *component)
{
	Check_Object(this);
	Check_Object(component);

	//
	//----------------------------------------------------------------------
	// If we are marked as unique, have the renderer deal with the component
	//----------------------------------------------------------------------
	//
	if (component->IsShared())
	{
		Check_Object(owningRenderer);
		return owningRenderer->AddSharedComponent(component);
	}
	return EntityComponentWeb::AddComponent(component);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Component*
	RendererComponentWeb::FindComponent(const ComponentID &component_id)
{
	Check_Object(this);

	//
	//------------------------------------------------
	// See if the renderer knows who this component is
	//------------------------------------------------
	//
	Check_Object(owningRenderer);
	Component *component = owningRenderer->FindSharedComponent(component_id);
	if (component)
	{
		return component;
	}

	return EntityComponentWeb::FindComponent(component_id);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	RendererComponentWeb::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));
}
