///////////////////////////////////////////////////////////////////////////////
//
// File: Asura_ClientEntity_ConsoleVar.h
//
// Created by Martin G.Harris (20/6/2007)
// 
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//
// Entity allowing designers to add a run-time console variable in tools and modify
// the value using static messages. This allows easy hooking up of game logic to
// (for example) the HUD system.
//
///////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __ASURA_CLIENT_ENTITY_CONSOLE_VAR_H__
#define __ASURA_CLIENT_ENTITY_CONSOLE_VAR_H__

// Need command console for the console variables.
#ifdef ASURA_SUPPORT_COMMAND_CONSOLE

// Includes
#include "Entity/Asura_Entity.h"
#include "Interface/Asura_CommandConsole.h"

// Forward declarations
class Asura_Chunk_Stream;
struct Asura_Message_Change_ConsoleVar;

///////////////////////////////////
// Asura_ClientEntity_ConsoleVar //
///////////////////////////////////
class Asura_ClientEntity_ConsoleVar : public Asura_Entity_Client
{
	typedef Asura_Entity_Client PARENT;

public:
	Asura_ClientEntity_ConsoleVar(Asura_Guid uGuid);
	virtual ~Asura_ClientEntity_ConsoleVar();

	virtual bool HandleMessage( const Asura_Message* pxMessage );
	void HandleMessage_Change( const Asura_Message_Change_ConsoleVar* pxData );

	virtual	void ReadFromChunkStream( Asura_Chunk_Stream& xStream );

	// Matches types in Asura_CommandConsole_VarRepository::Var::TYPE (except for string type)
	enum TYPE
	{
		TYPE_INVALID	= -1, // Invalid or string
        TYPE_BOOL		= 0,
        TYPE_INT		= 1,
        TYPE_FLOAT		= 2,
	};

	// Type of the console variable.
	TYPE GetType() const { return m_eType; }

	// Initialise or change variable name. Adding/removing console variables as necessary.
	void ChangeVarName(const Asura_Char* szVarName, const TYPE eType);

	// Name of the console variable.
	const Asura_Char* GetVarName() const { return m_szVarName; }

	// Look up the variable.
	Asura_CommandConsole_VarRepository::Var* GetVar()
	{
		return Asura_CommandConsole_VarRepository::Find( m_uVarNameHash );
	}
	const Asura_CommandConsole_VarRepository::Var* GetVar() const
	{
		return Asura_CommandConsole_VarRepository::Find( m_uVarNameHash );
	}

	// Helper functions to convert between types.
	static TYPE ConvertType(Asura_CommandConsole_VarRepository::Var::TYPE eVarType);
	static Asura_CommandConsole_VarRepository::Var::TYPE ConvertType(TYPE eType);

	// Count the number of ConsoleVar entities refering to a specified variable.
	// Return true if one of these entities owns the console variable.
	static bool GetNumberOfReferences(Asura_Hash_ID uVarNameHash, int& iNumber);

	// Try to transfer the ownership of the console variable from an entity to the code.
	// This is called if a code-defined variable is set up after a level has finished loading,
	// and that level contains a ConsoleVar entity refering to the variable.
	// Return true if transfered successfully.
	static bool TransferConsoleVarToCode(const Asura_UnicodeChar* wszVarName, 
										Asura_CommandConsole_VarRepository::Var::TYPE eType, 
										const Asura_CommandConsole_VarRepository::Var::Data& xData, 
										Asura_CommandConsole::REMOVE eRemove);

#ifdef ASURA_TOOLS
	// Update the Type of all entities pointing to the specified variable.
	static void ChangeType(Asura_Hash_ID uVarNameHash, TYPE eNewType);
#endif //ASURA_TOOLS

protected:
	void RemoveConsoleVar();
	void AddConsoleVar();

	void CopyValueToThis();

	// Transfer ownership of console variable.
	static bool TransferConsoleVar(Asura_Hash_ID uHash, int iData);

	// Generate default data for specified type.
	Asura_CommandConsole_VarRepository::Var::Data GetDefaultData(TYPE eNewType);

	// Apply operations.
	static void DoSetOperation(Asura_CommandConsole_VarRepository::Var* pxVar, float fData);
	static void DoAddOperation(Asura_CommandConsole_VarRepository::Var* pxVar, float fData);
	static void DoMultiplyOperation(Asura_CommandConsole_VarRepository::Var* pxVar, float fData);
	static void DoToggleBoolean(Asura_CommandConsole_VarRepository::Var* pxVar);

	// Set the variable to be the new value, applying clamping as necessary.
	static void SetBoolean(Asura_CommandConsole_VarRepository::Var* pxVar, bool bNewValue);
	static void SetInteger(Asura_CommandConsole_VarRepository::Var* pxVar, int iNewValue);
	static void SetFloat(Asura_CommandConsole_VarRepository::Var* pxVar, float fNewValue);

private:
// Private functions...
	Asura_ClientEntity_ConsoleVar(void);
	Asura_ClientEntity_ConsoleVar(const Asura_ClientEntity_ConsoleVar& xOther);
	Asura_ClientEntity_ConsoleVar& operator= (const Asura_ClientEntity_ConsoleVar& xOther);

protected:
	Asura_Char* m_szVarName;			// Name of console variable.
	Asura_Hash_ID m_uVarNameHash;		// Hash of variable name.

	bool m_bOwnsConsoleVar;				// Did we create this variable, can we remove it?

	TYPE m_eType;	// Type of the console var.

	// The actual data.
	union
	{
		bool	m_bData;
		int		m_iData;
		float	m_fData;
	};

	// Misc flags
	u_int m_uFlags;

// Static members
public:
	static void Create( Asura_Guid uGuid, Asura_Chunk_Stream& xStream );

	static Asura_ClientEntity_ConsoleVar* FindEntity( Asura_Guid uGuid ) { return s_xList.FindEntity( uGuid ); }
	static const Asura_Hashed_List<Asura_ClientEntity_ConsoleVar>* GetList() { return &s_xList; }

private:
	static Asura_Hashed_List<Asura_ClientEntity_ConsoleVar> s_xList;

	// Friends
	friend class Tools_Entity_ConsoleVar;
};

#endif //ASURA_SUPPORT_COMMAND_CONSOLE

#endif //__ASURA_CLIENT_ENTITY_CONSOLE_VAR_H__

