#include "munga.h"
#pragma hdrstop

#include "gauge.h"
#include "gaugrend.h"

extern void
	indent(int level);			// in graph2d.cc

//#########################################################################
//############################# GaugeBase #################################
//#########################################################################
GaugeBase::GaugeBase(
	ModeMask mode_mask,
	GaugeRenderer *new_renderer,
	unsigned int owner_ID,
	const char *identification_string
) :
	Node(Gauge::GaugeClassID),
	alreadyActivatedFlag(False)
{
	Check_Pointer(this);

	modeMask = mode_mask;
	ownerID = owner_ID;
	renderer = new_renderer;
	identificationString = identification_string;

	stayInactive = True;		// used by GaugeRenderer during activation
	//---------------------------------------------------
	// If renderer exists, add to object list
	// (Not having a renderer allows for independent testing)
	//---------------------------------------------------
	if (renderer != NULL)
	{
		Check(renderer);
		renderer->Add(this);
	}
	Check_Fpu();
}

GaugeBase::~GaugeBase()
{
	Check(this);
	Check_Fpu();
}

Logical
	GaugeBase::TestInstance() const
{
	return True;
}

void
	GaugeBase::BecameActive()
{
	Check(this);
	//------------------------------------------------------
	// Default method immediately inactivates the gaugeBase
	//------------------------------------------------------
	DEBUG_STREAM <<
		"BecameActive() has not been defined for '" <<	identificationString <<
		"'. About to call Inactivate()\n";
	Inactivate();
	Check_Fpu();
}

void
	GaugeBase::BecameInactive()
{
	Check(this);
	Check_Fpu();
}

void
	GaugeBase::Inactivate()
{
	Check(this);
	if (renderer != NULL)
	{
		Check(renderer);
		renderer->Inactivate(this);
	}
	Check_Fpu();
}

Logical
	GaugeBase::Update(GaugeRate /*rate_mask*/)
{
	Check(this);
	//------------------------------------------------------
	// Default method immediately inactivates the gaugeBase
	//------------------------------------------------------
	DEBUG_STREAM <<
		"Update() has not been defined for '" <<	identificationString <<
		"'. About to call Inactivate()\n";
	Inactivate();
	Check_Fpu();
   return False;
}

void
	GaugeBase::UpdateProfile(Scalar /*frame_percentage*/)
{
	Check(this);
	Check_Fpu();
}

void
	GaugeBase::ReportProfile()
{
	Check(this);
	Check_Fpu();
}

void
	GaugeBase::LinkToEntity(Entity */*entity*/)
{
	Check(this);
	Check_Fpu();
}

void
	GaugeBase::NotifyOfNewInterestingEntity(Entity */*entity*/)
{
	Check(this);
	Check_Fpu();
}

void
	GaugeBase::NotifyOfBecomingUninterestingEntity(Entity */*entity*/)
{
	Check(this);
	Check_Fpu();
}


int
	GaugeBase::DiscernTier()
{
	Check(this);
	Check_Fpu();
	return 0;
}


//#########################################################################
//########################## GaugeBackground ##############################
//#########################################################################
GaugeBackground::GaugeBackground(
	ModeMask mode_mask,
	GaugeRenderer *new_renderer,
	unsigned int owner_ID,
	const char *identification_string
) :
	GaugeBase(mode_mask, new_renderer, owner_ID, identification_string)
{
	Check_Pointer(this);
	Check_Fpu();
}

//#########################################################################
//######################## GraphicGaugeBackground #########################
//#########################################################################
GraphicGaugeBackground::GraphicGaugeBackground(
	ModeMask mode_mask,
	GaugeRenderer *renderer,
	unsigned int owner_ID,
	int graphics_port_number,
	const char *identification_string
) :
	localView(
		renderer == NULL ?
			NULL :
			renderer->GetGraphicsPort(graphics_port_number)
	),
	GaugeBackground(mode_mask, renderer, owner_ID, identification_string)
{
	Check_Fpu();
}

//#########################################################################
//########################## GaugeConnection ##############################
//#########################################################################

GaugeConnection::GaugeConnection(int parameter_id)
{
	Check_Pointer(this);
	parameterID = parameter_id;
	Check_Fpu();
}

GaugeConnection::~GaugeConnection()
{
	Check_Fpu();
}

Logical
	GaugeConnection::TestInstance() const
{
	Check_Pointer(this);

	return True;
}

void GaugeConnection::Update()
{
	Fail("GaugeConnection::Update should not be called!\n");
}

void
	GaugeConnection::ShowInstance(char *indent)
{
	Check(this);

	std::cout << indent << "GaugeConnection:\n";

	char
		temp[80];

	Str_Copy(temp,indent, 80);
	Str_Cat(temp,"...", 80);
	std::cout << temp << "parameterID=" << parameterID << "\n" << std::flush;
	Check_Fpu();
}

//#########################################################################
//################################# Gauge #################################
//#########################################################################

//----------------------------------------------------------------------------
// GaugeRate masks are used to spread out workload in a priority-based
// manner.  Each GaugeRate refers to the branch of a binary tree as shown:
//
//                               A                                = every frame
//                /--------------^--------------\                 :
// FIRST TIER:   B                              C                 = 1/2
//        /------^-------\               /------^-------\         :
// 2ND:  D               E              F               G         = 1/4
//    /--^---\        /--^---\       /--^---\        /--^---\     :
//   H       I       J       K       L       M       N       O    = 1/8
//  / \     / \     / \     / \     / \     / \     / \     / \   :
// P   Q   R   S   T   U   V   W   X   Y   Z  Z0  Z1  Z2  Z3  Z4  = 1/16
//
// Each 'tier' down indicates an update rate half that of the one above it.
//
// Note that rates Z0..Z4 cannot be specified through the gauge renderer's
// interpreter.  They are available to automatically generated gauges, though.
//----------------------------------------------------------------------------

GaugeRate
	Gauge::rateTable[31] =
		{
			gaugeRate_A,		// every frame

			gaugeRate_B,		// every other frame
			gaugeRate_C,

			gaugeRate_D,		// every 4th frame
			gaugeRate_E,
			gaugeRate_F,
			gaugeRate_G,

			gaugeRate_H,		// every 8th frame
			gaugeRate_I,
			gaugeRate_J,
			gaugeRate_K,
			gaugeRate_L,
			gaugeRate_M,
			gaugeRate_N,
			gaugeRate_O,

			gaugeRate_P,		// every 16th frame
			gaugeRate_Q,
			gaugeRate_R,
			gaugeRate_S,
			gaugeRate_T,
			gaugeRate_U,
			gaugeRate_V,
			gaugeRate_W,
			gaugeRate_X,
			gaugeRate_Y,
			gaugeRate_Z,
			gaugeRate_Z0,
			gaugeRate_Z1,
			gaugeRate_Z2,
			gaugeRate_Z3,
			gaugeRate_Z4
		};

//-------------------------------------------------------------------
// Gauge::NextXXXTierGaugeRate()
// - These functions attempt to spead out workload by returning the
// next available 'GaugeRate' in a given 'tier'.
//
// The higher level tiers bump the lower level tier indices to
// attempt to avoid placing too much load on the same higher-order
// branch of the tree.
//-------------------------------------------------------------------

static int
	first_index = -1,
	second_index = -1,
	third_index = -1,
	fourth_index = -1;

GaugeRate
	Gauge::NextFirstTierGaugeRate()
{
	//----------------------------------------
	// Generate first index
	//----------------------------------------
	first_index = (++first_index) & 1;
	//----------------------------------------
	// Bump other indices to avoid same branch
	//----------------------------------------
	second_index += 2; 
	third_index += 4; 
	fourth_index += 8; 
	//----------------------------------------
	// Return rate mask
	//----------------------------------------
	return rateTable[first_index + 1];
}

GaugeRate
	Gauge::NextSecondTierGaugeRate()
{
	//----------------------------------------
	// Generate first index
	//----------------------------------------
	second_index = (++second_index) & 3;
	//----------------------------------------
	// Bump other indices to avoid same branch
	//----------------------------------------
	third_index += 2; 
	fourth_index += 4; 
	//----------------------------------------
	// Return rate mask
	//----------------------------------------
	return rateTable[second_index + 3];
}

GaugeRate
	Gauge::NextThirdTierGaugeRate()
{
	//----------------------------------------
	// Generate first index
	//----------------------------------------
	third_index = (++third_index) & 7;
	//----------------------------------------
	// Bump other indices to avoid same branch
	//----------------------------------------
	fourth_index += 2; 
	//----------------------------------------
	// Return rate mask
	//----------------------------------------
	return rateTable[third_index + 7];
}

GaugeRate
	Gauge::NextFourthTierGaugeRate()
{
	//----------------------------------------
	// Generate first index
	//----------------------------------------
	fourth_index = (++fourth_index) & 15;
	//----------------------------------------
	// Return rate mask
	//----------------------------------------
	return rateTable[fourth_index + 15];
}

GaugeRate
	Gauge::ConvertIndexToRate(int index)
{
	Verify(index >= 0);
	Verify(index < 32);

	return rateTable[index];
}

Gauge::Gauge(
	GaugeRate new_rate,
	ModeMask mode_mask,
	GaugeRenderer *renderer,
	unsigned int owner_ID,
	const char *identification_string
):
	GaugeBase(mode_mask, renderer, owner_ID, identification_string),
	instanceList(this)
{
	Check_Pointer(this);

	rate = new_rate;
	oldRate = new_rate;

	stayInactive = False;

	profileFramePercentage = (Scalar) 0;
	profileMaxFramePercentage = (Scalar) 0;
	profileCycles = 0;

	Check_Fpu();
}

Gauge::~Gauge()
{
	Check(this);
	RemoveAllConnections();
	Check_Fpu();
}

Logical
	Gauge::TestInstance() const
{
	Check(&instanceList);
#	if 0
		//--------------------------------------------------------------------
		// Check all owned GaugeConnections
		//--------------------------------------------------------------------
		ChainIteratorOf<GaugeConnection*>
			i(instanceList);

		GaugeConnection
			*the_connection;

		while ((the_connection=i.ReadAndNext()) != NULL)
		{
			Check(the_connection);
		}
#	endif
	//--------------------------------------------------------------------
	// Check the base object
	//--------------------------------------------------------------------
	return GaugeBase::TestInstance();
}

void
	Gauge::ShowInstance(char *indent)
{
	Check(this);

	ChainIteratorOf<GaugeConnection*>
		i(instanceList);

	GaugeConnection
		*the_connection;

	std::cout << indent << "Gauge:\n";

	char
		temp[80];

	Str_Copy(temp,indent, 80);
	Str_Cat(temp,"...", 80);

	std::cout << temp << "modeMask         =" << modeMask << "\n";
	std::cout << temp << "renderer         =" << renderer << "\n";
	std::cout << temp << "ownerID          =" << ownerID << "\n";
	std::cout << temp << "InstanceList -\n" << std::flush;

	Str_Cat(temp,"...", 80);

	while ((the_connection=i.ReadAndNext()) != NULL)
	{
		Check(the_connection);
		the_connection->ShowInstance(temp);
	}
	Check_Fpu();
}

void
	Gauge::AddConnection(GaugeConnection *connection)
{
	Check(this);
	Check(connection);
	instanceList.Add(connection);
	Check_Fpu();
}

void
	Gauge::RemoveConnection(int parameter_ID)
{
	Check(this);

	ChainIteratorOf<GaugeConnection*>
		i(instanceList);

	GaugeConnection
		*gauge_connection;

	while ((gauge_connection=i.ReadAndNext()) != NULL)
	{
		Check(gauge_connection);
		if (gauge_connection->parameterID == parameter_ID)
		{
			Unregister_Object(gauge_connection);
			delete gauge_connection;
		}
	}
	Check_Fpu();
}

void
	Gauge::RemoveAllConnections()
{
	Check(this);

	ChainIteratorOf<GaugeConnection*>
		i(instanceList);

	GaugeConnection
		*gauge_connection;

	while ((gauge_connection=i.ReadAndNext()) != NULL)
	{
		Check(gauge_connection);
		Unregister_Object(gauge_connection);
		delete gauge_connection;
	}
	Check_Fpu();
}

void
	Gauge::Disable(Logical disable_state)
{
	Check(this);
	if (disable_state)
	{
		rate = 0;
		BecameInactive();
	}
	else
	{
		rate = oldRate;
		BecameActive();
	}
	Check_Fpu();
}

Logical
	Gauge::Update(GaugeRate rate_mask)
{
	Check(this);

	Logical
		actually_ran = False;
	//-----------------------------------------
	// Update only if rate mask allows it
	//-----------------------------------------
	if (rate & rate_mask)
	{
		//-----------------------------------------
		// Update parameters
		//-----------------------------------------
		ChainIteratorOf<GaugeConnection*>
			i(instanceList);

		GaugeConnection
			*gauge_connection;

		while ((gauge_connection=i.ReadAndNext()) != NULL)
		{
			Check(gauge_connection);
			gauge_connection->Update();
		}
		//-----------------------------------------
		// Execute gauge code
		//-----------------------------------------
		Execute();
		actually_ran = True;
	}
	Check_Fpu();
   return actually_ran;
}

void
	Gauge::Execute()
{
	Fail("Gauge::Execute not overridden");
}

struct TierLookup
{
	GaugeRate rate;
	int tier;
};

int
	Gauge::DiscernTier()
{
	Check(this);

	TierLookup
		*tier_pointer;
	static TierLookup
		tier_lookup[] =
			{
				{gaugeRate_A, 0 },

				{gaugeRate_B, 1 },
				{gaugeRate_C, 1 },

				{gaugeRate_D, 2 },
				{gaugeRate_E, 2 },
				{gaugeRate_F, 2 },
				{gaugeRate_G, 2 },

				{gaugeRate_H, 3 },
				{gaugeRate_I, 3 },
				{gaugeRate_J, 3 },
				{gaugeRate_K, 3 },
				{gaugeRate_L, 3 },
				{gaugeRate_M, 3 },
				{gaugeRate_N, 3 },
				{gaugeRate_O, 3 },

				{gaugeRate_P, 4 },
				{gaugeRate_Q, 4 },
				{gaugeRate_R, 4 },
				{gaugeRate_S, 4 },
				{gaugeRate_T, 4 },
				{gaugeRate_U, 4 },
				{gaugeRate_V, 4 },
				{gaugeRate_W, 4 },
				{gaugeRate_X, 4 },
				{gaugeRate_Y, 4 },
				{gaugeRate_Z, 4 },
				{gaugeRate_Z0,4 },
				{gaugeRate_Z1,4 },
				{gaugeRate_Z2,4 },
				{gaugeRate_Z3,4 },
				{gaugeRate_Z4,4 },
				{0,0}
			};

	for (tier_pointer=tier_lookup; tier_pointer->rate != 0; ++tier_pointer)
	{
		if (tier_pointer->rate == rate)
		{
			Check_Fpu();
			return tier_pointer->tier;
		}
	}

	Check_Fpu();
	return 0;
}

void
	Gauge::UpdateProfile(Scalar frame_percentage)
{
	Check(this);

	//---------------------------------------------
	// Keep worst case
	//---------------------------------------------
	if (profileMaxFramePercentage < frame_percentage)
	{
		profileMaxFramePercentage = frame_percentage;
	}
	//---------------------------------------------
	// Keep running total
	//---------------------------------------------
	profileFramePercentage += frame_percentage;
	//---------------------------------------------
	// Prevent overflow
	//---------------------------------------------
	if (++profileCycles > 16384)
	{
		profileFramePercentage /= (Scalar) 2;
		profileCycles >>= 1;
	}

	Check_Fpu();	
}

void
	Gauge::ReportProfile()
{
	Check(this);

	int
		i;

	DEBUG_STREAM << identificationString << std::flush;
	for(i=strlen(identificationString); i<32; ++i)
	{
		DEBUG_STREAM << "." << std::flush;
	}

	if (profileCycles > 0)
	{
		Scalar
			average = profileFramePercentage / (Scalar)profileCycles;

		char
			buffer[80];

		sprintf(buffer, "%6d %6.4f %6.4f\n",
			profileCycles,
			average,
			profileMaxFramePercentage
		);
      DEBUG_STREAM << buffer << std::flush;
	}
	else
	{
		DEBUG_STREAM << "never ran.\n" << std::flush;
	}

	//----------------------------------------
	// Reset maximum percentage
	//----------------------------------------
	profileMaxFramePercentage = (Scalar) 0;

	Check_Fpu();	
}

//#########################################################################
//############################# GraphicGauge #############################
//#########################################################################

GraphicGauge::GraphicGauge(
	GaugeRate new_rate,
	ModeMask mode_mask,
	GaugeRenderer *renderer,
	unsigned int owner_ID,
	int graphics_port_number,
	const char *identification_string
	) :
	Gauge(new_rate, mode_mask, renderer, owner_ID, identification_string),
	localView(
		renderer == NULL ?
			NULL :
			renderer->GetGraphicsPort(graphics_port_number)
	)
{
	Check_Fpu();
}


GraphicGauge::~GraphicGauge()
{
	Check(this);
	Check_Fpu();
}

Logical
	GraphicGauge::TestInstance() const
{
	Check(&localView);
	Check_Fpu();
	return Gauge::TestInstance();
}

void
	GraphicGauge::ShowInstance(char *indent)
{
	Check(this);

	std::cout << indent << "GraphicGauge:\n";

	char
		temp[80];

	Str_Copy(temp,indent, 80);
	Str_Cat(temp,"...", 80);

	localView.ShowInstance(temp);
	Gauge::ShowInstance(temp);
	Check_Fpu();
}


#if defined(TEST_CLASS)
#	include "gauge.tcp"
#endif
