//===========================================================================//
// File:        simulate.cc                                                         //
// Project: MUNGA           Brick: Model Manager                             //
// Contents: Implementation details of model class                           //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 01/25/95  JMA  Initial coding.                                            //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved //
//                      PROPRIETARY AND CONFIDENTIAL                         //
//===========================================================================//

#include <munga.hpp>
#pragma hdrstop

#if !defined(SIMULATE_HPP)
#	include <simulate.hpp>
#endif

#if !defined(UPDATE_HPP)
#	include <update.hpp>
#endif

#if !defined(APP_HPP)
	#include <app.hpp>
#endif

#if defined(TRACE_EXECUTE_WATCHERS)
	static BitTrace Execute_Watchers("Execute Watchers");
	#define SET_EXECUTE_WATCHERS() Execute_Watchers.Set()
	#define CLEAR_EXECUTE_WATCHERS() Execute_Watchers.Clear()
#else
	#define SET_EXECUTE_WATCHERS()
	#define CLEAR_EXECUTE_WATCHERS()
#endif

//#############################################################################
//########################    StateIndicator    ###############################
//#############################################################################

//#############################################################################
// Construction and Destruction
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
StateIndicator::StateIndicator():
	audioWatcherSocket(NULL),
	videoWatcherSocket(NULL),
	gaugeWatcherSocket(NULL)
{
	Check_Pointer(this);
	stateCount = 0;
	oldState = 0;
	currentState = 0;
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
StateIndicator::StateIndicator(unsigned max_states):
	audioWatcherSocket(NULL),
	videoWatcherSocket(NULL),
	gaugeWatcherSocket(NULL)
{
	Check_Pointer(this);
	stateCount = max_states;
	oldState = max_states;
	currentState = max_states;
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
StateIndicator::StateIndicator(const StateIndicator &state_indicator):
	audioWatcherSocket(NULL),
	videoWatcherSocket(NULL),
	gaugeWatcherSocket(NULL)
{
	Check_Pointer(this);

	// Do not perform deep copy of watchers
	stateCount = state_indicator.stateCount;
	oldState = state_indicator.oldState;
	currentState = state_indicator.currentState;
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
StateIndicator::~StateIndicator()
{
	Check(this);

	//
	// Manual deletion of existing watchers
	//
	{
		SChainIteratorOf<Component*> iterator(&audioWatcherSocket);
		#if DEBUG_LEVEL>2
			Component *component;
			while ((component = iterator.ReadAndNext()) != NULL)
			{
				Check(component);
				Dump(component->GetClassID());
			}
			Warn(iterator.GetSize() != 0);
		#endif
		iterator.DeletePlugs();
	}
	{
		SChainIteratorOf<Component*> iterator(&videoWatcherSocket);
		#if DEBUG_LEVEL>2
			Component *component;
			while ((component = iterator.ReadAndNext()) != NULL)
			{
				Check(component);
				Dump(component->GetClassID());
			}
			Warn(iterator.GetSize() != 0);
		#endif
		iterator.DeletePlugs();
	}
	{
		SChainIteratorOf<Component*> iterator(&gaugeWatcherSocket);
		#if DEBUG_LEVEL>2
			Component *component;
			while ((component = iterator.ReadAndNext()) != NULL)
			{
				Check(component);
				Dump(component->GetClassID());
			}
			Warn(iterator.GetSize() != 0);
		#endif
		iterator.DeletePlugs();
	}
	Check_Fpu();
}

//#############################################################################
// State stuff
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
StateIndicator&
	StateIndicator::operator=(const StateIndicator &state_indicator)
{
	// Do not perform assignment of watchers
	stateCount = state_indicator.stateCount;
	oldState = state_indicator.oldState;
	currentState = state_indicator.currentState;

	Check_Fpu();
	return *this;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	StateIndicator::operator==(const StateIndicator &state_indicator) const
{
	Check_Fpu();
	return
	(
		stateCount == state_indicator.stateCount &&
		oldState == state_indicator.oldState &&
		currentState == state_indicator.currentState
	);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	StateIndicator::SetState(unsigned new_state)
{
	Check(this);
	Verify(new_state < stateCount);

	//
	//--------------------------------------------------------------------------
	// See if the state really changes
	//
	// NOTE - the old state does change to the current state, simulating a loop
	//        in the state engine.  If it turns out that someone is watching the
	//        level of the state indicator and doing their own edge detection,
	//        this might possibly maybe screw something up
	//--------------------------------------------------------------------------
	//
	oldState = currentState;
	if (new_state == currentState)
	{
		return;
	}

	//
	//-------------------------------------------------------------------
	// If the state has changed, update the state values and then run any
	// watchers
	//-------------------------------------------------------------------
	//
	currentState = new_state;
	Component *watcher;

	SET_EXECUTE_WATCHERS();

	// Audio
	{
		SChainIteratorOf<Component*> iterator(audioWatcherSocket);
		Check(&iterator);
		while ((watcher = iterator.ReadAndNext()) != NULL)
		{
			watcher->Execute();
		}
	}

	// Video
	{
		SChainIteratorOf<Component*> iterator(videoWatcherSocket);
		Check(&iterator);
		while ((watcher = iterator.ReadAndNext()) != NULL)
		{
			watcher->Execute();
		}
	}

	// Gauge
	{
		SChainIteratorOf<Component*> iterator(gaugeWatcherSocket);
		Check(&iterator);
		while ((watcher = iterator.ReadAndNext()) != NULL)
		{
			watcher->Execute();
		}
	}

	CLEAR_EXECUTE_WATCHERS();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ostream&
	operator << (ostream &strm, const StateIndicator &state_indicator)
{
   Check(&state_indicator);

   strm << "[" << state_indicator.stateCount << ",";
   strm << state_indicator.oldState << ",";
   strm << state_indicator.currentState << "]";

	return strm;
}

//#############################################################################
// Test Support
//
Logical
	StateIndicator::TestInstance() const
{
	return True;
}

//#############################################################################
//##########################    Simulation    #################################
//#############################################################################

//#############################################################################
// Virtual Data support
//
Derivation
	Simulation::ClassDerivations(Receiver::ClassDerivations,"Simulation");

Simulation::SharedData
	Simulation::DefaultData(
		Simulation::ClassDerivations,
		Simulation::MessageHandlers,
		Simulation::AttributeIndex,
      Simulation::StateCount
	);

//#############################################################################
// Model support
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Simulation::ReadUpdateRecord(UpdateRecord *message)
{
	Check(this);
	Check_Pointer(message);

	lastUpdate = Now();     // HACK - should be based upon message->timeStamp
	SetSimulationState(message->simulationState);
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Simulation::WriteUpdateRecord(
		UpdateRecord *message,
		int update_model
	)
{
	Check(this);
	Check_Pointer(message);

	message->timeStamp = lastPerformance;
	message->simulationState = GetSimulationState();
	message->recordLength = sizeof(*message);
	message->recordID = (Word)update_model;
	lastUpdate = lastPerformance;
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Simulation::WriteSimulationUpdate(MemoryStream *update_stream)
{
	Check(this);
	Check(update_stream);

	//
	//-----------------
	// Write the update
	//-----------------
	//
	int bit=0;
	int update_model = updateModel;
	updateModel = 0;
	while (update_model)
	{
		
		if (update_model & 1)
		{
			UpdateRecord* update = (UpdateRecord*)update_stream->GetPointer();
			WriteUpdateRecord(update, bit);
			update_stream->AdvancePointer(update->recordLength);
		}
		update_model >>= 1;
		++bit;
	}
	Check_Fpu();
}

//#############################################################################
// Construction and Destruction
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Simulation::Simulation(
	Simulation::ClassID class_ID,
	Simulation::SharedData &virtual_data
):
	Receiver(class_ID, virtual_data),
	simulationState(GetSharedData()->stateCount),
	audioWatcherSocket(NULL),
	videoWatcherSocket(NULL),
	gaugeWatcherSocket(NULL),
   effectWatcherSocket(NULL)
{
	Check_Pointer(this);

	SetSimulationState(DefaultState);
	lastPerformance = Now();
	lastUpdate = lastPerformance;
	activePerformance = &Simulation::DoNothingOnce;
	updateModel = 0;
   simulationFlags = 0;
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Simulation::~Simulation()
{
	Check(this);

	//
	// Watchers should be deleted by renderers by now
	//
	#if DEBUG_LEVEL>0
		{
			SChainIteratorOf<Component*> iterator(&audioWatcherSocket);
			Verify(iterator.GetSize() == 0);
		}
		{
			SChainIteratorOf<Component*> iterator(&videoWatcherSocket);
			Verify(iterator.GetSize() == 0);
		}
		{
			SChainIteratorOf<Component*> iterator(&gaugeWatcherSocket);
			Verify(iterator.GetSize() == 0);
		}
	#endif
	Check_Fpu();
}

//#############################################################################
// Attribute Support
//
const Simulation::AttributePointer
	Simulation::NullAttribute = NULL;

const Simulation::IndexEntry
	Simulation::AttributePointers[]=
{
	{
		Simulation::SimulationStateAttributeID,
		"SimulationState",
		(Simulation::AttributePointer)&Simulation::simulationState
	}
};

Simulation::AttributeIndexSet
	Simulation::AttributeIndex(
		ELEMENTS(Simulation::AttributePointers),
		Simulation::AttributePointers
	);

void*
	Simulation::GetAttributePointer(Simulation::AttributeID attribute)
{
	Check(this);

	AttributePointer attr =
		GetSharedData()->activeAttributeIndex->Find(attribute);
	Check_Fpu();
	if (attr == NullAttribute)
	{
		return NULL;
	}
	else
	{
		return &(this->*attr);
	}
}

void*
	Simulation::GetAttributePointer(const char* attribute_name)
{
	Check(this);

	AttributePointer attr =
		GetSharedData()->activeAttributeIndex->Find(attribute_name);
	Check_Fpu();
	if (attr == NullAttribute)
	{
		return NULL;
	}
	else
	{
		return &(this->*attr);
	}
}

//#############################################################################
// Simulation Support
//
void
	Simulation::PerformAndWatch(
		const Time& till,
		MemoryStream *update_stream
	)
{
	Check(this);
	Check(&till);

	Scalar slice = till - lastPerformance;
	lastPerformance = till;

	Perform(slice);
	if (!AreWatchersDelayed())
	{
		ExecuteWatchers();
	}
	WriteSimulationUpdate(update_stream);

	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Simulation::DoNothingOnce(Scalar)
{
	NeverExecute();
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Simulation::DoNothing(Scalar)
{
	Check_Fpu();
}

//#############################################################################
// Watcher Support
//
void
	Simulation::ExecuteWatchers()
{
	SET_EXECUTE_WATCHERS();

	Component *watcher;

	// Audio
	{
		SChainIteratorOf<Component*> iterator(audioWatcherSocket);
		while ((watcher = iterator.ReadAndNext()) != NULL)
		{
			watcher->Execute();
		}
	}

	// Video
	{
		SChainIteratorOf<Component*> iterator(videoWatcherSocket);
		while ((watcher = iterator.ReadAndNext()) != NULL)
		{
			watcher->Execute();
		}
	}

	// Gauge
	{
		SChainIteratorOf<Component*> iterator(gaugeWatcherSocket);
		while ((watcher = iterator.ReadAndNext()) != NULL)
		{
			watcher->Execute();
		}
	}

	// Effect
	{
		SChainIteratorOf<Component*> iterator(effectWatcherSocket);
		while ((watcher = iterator.ReadAndNext()) != NULL)
		{
			watcher->Execute();
		}
	}
	CLEAR_EXECUTE_WATCHERS();
}

//#############################################################################
// Test Support
//

Logical
	Simulation::TestInstance() const
{
	return IsDerivedFrom(ClassDerivations);
}

//#############################################################################
//###################    Simulation::AttributeIndexSet    #####################
//#############################################################################

const Simulation::AttributeIndexSet
	Simulation::AttributeIndexSet::NullSet;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Simulation__AttributeIndexSet::~Simulation__AttributeIndexSet()
{
	if (attributeIndex)
	{
		Unregister_Pointer(attributeIndex);
		delete[] attributeIndex;
	}
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Simulation::AttributeIndexSet::Build(
		Simulation::AttributeID count,
		const Simulation::IndexEntry index_table[],
		const Simulation::AttributeIndexSet *inheritance
	)
{
	//
	//-------------------------------------------------------
	// Find out the highest message type we have to deal with
	//-------------------------------------------------------
	//
	Check(this);
	Check_Pointer(index_table);
	entryCount = 0;
	Simulation::AttributeID i;
	for (i=0; i<count; ++i)
	{
		if (index_table[i].entryID > entryCount)
		{
			entryCount = index_table[i].entryID;
		}
	}
	if (inheritance)
	{
		Check(inheritance);
		if (entryCount<inheritance->entryCount)
		{
			entryCount = inheritance->entryCount;
		}
		#if DEBUG_LEVEL>0
		else if (entryCount > inheritance->entryCount)
		{
			i = inheritance->entryCount+1;
			goto Check_Table;
		}
		#endif
	}
	else
	{
		Verify(entryCount == count);
		#if DEBUG_LEVEL>0
		i = 1;
Check_Table:
			while (i <= entryCount)
			{
				int j;
				for (j=0; j<count; ++j)
				{
					if (index_table[j].entryID == i)
					{
						break;
					}
				}
				if (j == count)
				{
					break;
				}
	    ++i;
			}
			Verify(i > count);
		#endif
	}

	//
	//-----------------------------------------------------------------------
	// Allocate the memory for the new handler set, and copy the inherited
	// handlers to the new table.  We are guaranteed to have enough space for
	// the inherited table
	//-----------------------------------------------------------------------
	//
	attributeIndex = new Simulation::IndexEntry[entryCount];
	Check_Pointer(attributeIndex);
	Register_Pointer(attributeIndex);
	i = 0;
	if (inheritance)
	{
		for (; i<inheritance->entryCount; ++i)
		{
			attributeIndex[i] = inheritance->attributeIndex[i];
		}
	}

	//
	//----------------------------------------------------------------------
	// Step through the new table supplied, placing each handler in the slot
	// determined by the message type
	//----------------------------------------------------------------------
	//
	for (i=0; i<count; ++i)
	{
		Verify(!inheritance || index_table[i].entryID > inheritance->entryCount);
		attributeIndex[index_table[i].entryID-1] = index_table[i];
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Simulation::AttributePointer
	Simulation::AttributeIndexSet::Find(const char* attribute_name) const
{
	Check(this);
	Check_Pointer(attribute_name);

	for (int attribute=0; attribute<entryCount; ++attribute)
	{
		if (!strcmp(attribute_name, attributeIndex[attribute].entryName))
		{
			Check_Fpu();
			return attributeIndex[attribute].entryAddress;
		}
	}
	Check_Fpu();
	return Simulation::NullAttribute;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const Simulation::IndexEntry*
	Simulation::AttributeIndexSet::FindEntry(const char* attribute_name) const
{
	Check(this);
	Check_Pointer(attribute_name);

	for (int attribute=0; attribute<entryCount; ++attribute)
	{
		if (!strcmp(attribute_name, attributeIndex[attribute].entryName))
		{
			Check_Fpu();
			return &attributeIndex[attribute];
		}
	}
	Check_Fpu();
	return NULL;
}

void
	Simulation::RequestEncore(Encore encore)
{
	Check(this);
	SetWatcherDelay();

	Check(application);
	UpdateManager *updater = application->GetUpdateManager();
	Check(updater);
	updater->RequestEncore(this, encore);
}

#if defined(TEST_CLASS) && 0
#	include "model.tcp"
#endif

