#pragma once

#include "style.h"

typedef unsigned int GaugeRate;
class GaugeRenderer;
class GaugeBase;
class GraphicGaugeBackground;
class Gauge;
class GraphicGauge;
class GaugeConnection;
class Entity;
class GaugeAlarmManager;

#include "mode.h"
#include "slot.h"
#include "chain.h"
#include "receiver.h"
#include "graph2d.h"

//#########################################################################
//############################# GaugeBase #################################
//#########################################################################
class GaugeBase :
	public Node
{
	friend class GaugeRenderer;
	//----------------------------------------------------------------------
	// Constructor
	//----------------------------------------------------------------------
protected:
	GaugeBase(
		ModeMask mode_mask,
		GaugeRenderer *renderer,
		unsigned int owner_ID,
		const char *identification_string
	);
	//----------------------------------------------------------------------
	// Destructor
	//----------------------------------------------------------------------
public:
	virtual ~GaugeBase();
	//----------------------------------------------------------------------
	// Test methods
	//----------------------------------------------------------------------
	Logical
		TestInstance() const;
	//----------------------------------------------------------------------
	// Update methods
	//----------------------------------------------------------------------
	virtual void
		BecameActive(),
		BecameInactive(),
		Inactivate(),
		UpdateProfile(Scalar frame_percentage),
		ReportProfile();

	virtual Logical
		Update(GaugeRate rate_mask);

	virtual void
		LinkToEntity(Entity *entity),
		NotifyOfNewInterestingEntity(Entity *entity),
		NotifyOfBecomingUninterestingEntity(Entity *entity);

	virtual int
		DiscernTier();

	//----------------------------------------------------------------------
	// Public data
	//----------------------------------------------------------------------
public:
	Logical
		alreadyActivatedFlag;   // This is really a visibility flag.
										// Active/inactive lists are more like
										// "should get redrawn" lists


	//----------------------------------------------------------------------
	// Protected data
	//----------------------------------------------------------------------
protected:
	Logical
		stayInactive;	// used by GaugeRenderer during activation
	unsigned int
		ownerID;
	ModeMask
		modeMask;
	GaugeRenderer
		*renderer;
	const char
		*identificationString;
};

//#########################################################################
//########################### GaugeBackground #############################
//#########################################################################
class GaugeBackground :
	public GaugeBase
{
protected:
GaugeBackground(
		ModeMask mode_mask,
		GaugeRenderer *renderer,
		unsigned int owner_ID,
		const char *identification_string = "GraphicGaugeBackground"
	);
};

//#########################################################################
//######################## GraphicGaugeBackground #########################
//#########################################################################
class GraphicGaugeBackground :
	public GaugeBackground
{
protected:
	GraphicGaugeBackground(
		ModeMask mode_mask,
		GaugeRenderer *renderer,
		unsigned int owner_ID,
		int graphics_port_number,
		const char *identification_string = "GraphicGaugeBackground"
	);
	//----------------------------------------------------------------------
	// Protected data
	//----------------------------------------------------------------------
	GraphicsView
		localView;
};

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

// The GaugeConnection object maintains one instance of a mapping from
// a value in some location to a parameter for a gauge.

class GaugeConnection :
	public Plug
{
	friend class Gauge;

public:
	GaugeConnection(int parameter_ID);
	~GaugeConnection();

	Logical
		TestInstance() const;

protected:
	virtual void ShowInstance(char *indent);
	virtual void Update();

	int	parameterID;
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GaugeConnectionDirectOf ~~~~~~~~~~~~~~~~~~

template <class T> class GaugeConnectionDirectOf:
	public GaugeConnection
{
	friend class Gauge;

public:
	GaugeConnectionDirectOf(
		int parameter_ID,
		T* data_destination,
		T* data_source
	);
	~GaugeConnectionDirectOf();
	Logical
		TestInstance() const;

protected:
	void
		Update();

	void
		ShowInstance(char *indent);

	T *dataSource;
	T *dataDestination;
};

template <class T> GaugeConnectionDirectOf<T>::GaugeConnectionDirectOf(
	int parameter_ID,
	T* data_destination,
	T* data_source
):
	GaugeConnection(parameter_ID),
	dataSource(data_source),
	dataDestination(data_destination)
{
	Check_Pointer(this);
	Check_Pointer(data_destination);

	//
	// BT DEV-GAUGES bring-up: some gauge widgets bind to mech data sources that
	// are not reconstructed yet, so the attribute resolves to a NULL data_source
	// (see the "(Scalar *) is VERY dangerous!" note in L4GAUGE.cpp).  Rather than
	// AV here (and later in Update()), bind to a static zero so the gauge simply
	// reads 0.  Gated on BT_DEV_GAUGES so the POD path is byte-unchanged.
	//
	if (data_source == NULL)
	{
		static int s_devGauges = -1;
		if (s_devGauges < 0)
			s_devGauges = (getenv("BT_DEV_GAUGES") != NULL) ? 1 : 0;
		if (s_devGauges)
		{
			static T s_gaugeNullSource = T();
			dataSource = &s_gaugeNullSource;
			data_source = &s_gaugeNullSource;
		}
	}
	Check_Pointer(data_source);

	*dataDestination = *dataSource;
	Verify(*dataDestination == *dataSource);
	Check(this);
}

template <class T> GaugeConnectionDirectOf<T>::~GaugeConnectionDirectOf()
	{}

template <class T> void
	GaugeConnectionDirectOf<T>::Update()
{
	Check(this);
	Check_Pointer(dataSource);
	Check_Pointer(dataDestination);
	*dataDestination = *dataSource;
	Verify(*dataDestination == *dataSource);
	Check(this);
}

template <class T> void
	GaugeConnectionDirectOf<T>::ShowInstance(char *indent)
{
	Check(this);
	Check_Pointer(dataSource);
	Check_Pointer(dataDestination);
	std::cout << indent << "GaugeConnectionDirectOf:\n";

	std::cout << indent << "...dataSource      at" <<
		dataSource << "=" << *dataSource << "\n";
	std::cout << indent << "...dataDestination at" <<
		dataSource << "\n" << std::flush;
	GaugeConnection::ShowInstance(indent);
}

template <class T> Logical
	GaugeConnectionDirectOf<T>::TestInstance() const
{
	Check_Pointer(this);
	Check_Pointer(dataSource);
	Check_Pointer(dataDestination);
	return Plug::TestInstance();
}


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

class Gauge :
	public GaugeBase
{
public:
	enum
	{
		gaugeRate_A=0xFFFF,		// every frame

		gaugeRate_B=0xAAAA,		// every other frame
		gaugeRate_C=0x5555,

		gaugeRate_D=0x8888,		// every 4th frame
		gaugeRate_E=0x4444,
		gaugeRate_F=0x2222,
		gaugeRate_G=0x1111,

		gaugeRate_H=0x8080,		// every 8th frame
		gaugeRate_I=0x4040,
		gaugeRate_J=0x2020,
		gaugeRate_K=0x1010,
		gaugeRate_L=0x0808,
		gaugeRate_M=0x0404,
		gaugeRate_N=0x0202,
		gaugeRate_O=0x0101,

		gaugeRate_P=0x8000,		// every 16th frame
		gaugeRate_Q=0x4000,
		gaugeRate_R=0x2000,
		gaugeRate_S=0x1000,
		gaugeRate_T=0x0800,
		gaugeRate_U=0x0400,
		gaugeRate_V=0x0200,
		gaugeRate_W=0x0100,
		gaugeRate_X=0x0080,
		gaugeRate_Y=0x0040,
		gaugeRate_Z=0x0020,
		gaugeRate_Z0=0x0010,
		gaugeRate_Z1=0x0008,
		gaugeRate_Z2=0x0004,
		gaugeRate_Z3=0x0002,
		gaugeRate_Z4=0x0001
	};

	//----------------------------------------------------------------------
	// Public static methods and data
	//----------------------------------------------------------------------
	static GaugeRate
		rateTable[31];

	static GaugeRate
		NextFirstTierGaugeRate(),		// returns either B,C
		NextSecondTierGaugeRate(),		// returns DEFG
		NextThirdTierGaugeRate(),		// returns HIJKLMNO
		NextFourthTierGaugeRate();		// returns PQRSTUVWXYZ

	static GaugeRate
		ConvertIndexToRate(int index);

	//----------------------------------------------------------------------
	// Constructor method
	//----------------------------------------------------------------------
	Gauge(
		GaugeRate new_rate,
		ModeMask mode_mask,
		GaugeRenderer *renderer,
		unsigned int owner_ID,
		const char *identification_string = "Gauge"
	);
	//----------------------------------------------------------------------
	// Destructor method
	//----------------------------------------------------------------------
	~Gauge();
	//----------------------------------------------------------------------
	// Test methods
	//----------------------------------------------------------------------
	Logical
		TestInstance() const;
	void
		ShowInstance(char *indent);
	int
		DiscernTier();
	static Logical
		TestClass();
	//----------------------------------------------------------------------
	// Connection methods
	//----------------------------------------------------------------------
	virtual void
		AddConnection(GaugeConnection *connection),
		RemoveConnection(int parameter_ID),
		RemoveAllConnections();

	//----------------------------------------------------------------------
	// Update methods
	//----------------------------------------------------------------------
	void
		Disable(Logical disable_state);
	Logical
		Update(GaugeRate rate_mask);
protected:
	virtual void
		Execute();	// Execute internal processes to display the gauge

	// BT DEV-GAUGES bring-up: SEH wrapper around Execute() so a gauge whose
	// game-state data binding isn't reconstructed yet is skipped (not crashed).
	// Returns False if Execute() raised a structured exception (e.g. an AV).
	Logical
		GuardedExecute();
	//----------------------------------------------------------------------
	// Protected data
	//----------------------------------------------------------------------
protected:
	GaugeRate
		rate,
		oldRate;
	ChainOf<GaugeConnection*>
		instanceList;
	//---------------------------------------
	// Profiling data
	//---------------------------------------
	void
		UpdateProfile(Scalar frame_percentage),
		ReportProfile();

	Scalar
		profileFramePercentage,
		profileMaxFramePercentage;
	int
		profileCycles;
};

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

class GraphicGauge :
	public Gauge
{
public:
	//
	//----------------------------------------------------------------------
	// Public methods
	//----------------------------------------------------------------------
	//
	GraphicGauge(
		GaugeRate new_rate,
		ModeMask mode_mask,
		GaugeRenderer *renderer,
		unsigned int owner_ID,
		int graphics_port_number,
		const char *identification_string = "GraphicGauge"
	);
	~GraphicGauge();

	Logical
		TestInstance() const;

	void
		ShowInstance(char *indent);
	//----------------------------------------------------------------------
	// Public data
	//----------------------------------------------------------------------
	GraphicsView
		localView;
};
