////////////////////////////////////////////////////    //////////////////////////
//
// Asura_GUIMenu_CommandConsole.cpp
//
// Copyright (c) 2007 Rebellion. All rights reserved.
//
// Description:
//
//     interface to the command console for the GUIMenu code for setting
//     variables and executing commands
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"
#ifdef ASURA_USE_GUI_MENU_SYSTEM
// This file's header.
#include "GUIMenu/Asura_GUIMenu_CommandConsole.h"

#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "GUIMenu/Asura_GUIMenu_Root.h"
#include "GUIMenu/Asura_GUIMenu_System.h"

#ifdef ASURA_TOOLS
#include "GUIMenu/Tools_Mode_GUIMenu.h"
#include "GUIMenu/Tools_GUIMenu_PreviewOverlay.h"
#include "GUIMenu/Tools_GUIMenu_XMLExporter.h"
#endif

// C O N S T A N T S /////////////////////////////////////////////////////////
static const u_int uASURA_GUIMENU_CONSOLECMD_VERSION  = 0;
static const u_int uASURA_GUIMENU_CONSOLEVAR_VERSION  = 0;

//static Asura_TemplateHashTree<Asura_CommandConsole_VarRepository::Var*> g_xCommandVarCache;

// C L A S S   I M P L E M E N T A T I O N ///////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// Asura_GUIMenu_ConsoleCmd
//////////////////////////////////////////////////////////////////////////////
bool Asura_GUIMenu_ConsoleCmd::Execute(const char* szArgumentOverride, void (*pfnOutputCallback)(const Asura_UnicodeChar*) /* = NULL */ ) const
{
#ifdef ASURA_TOOLS
	// in tools we might have the command set to "*None*", as this is only 
	// changed to a blank string when saving the chunk.
	if (!m_xConsoleCmdName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE) ||
		!m_xConsoleCmdName.GetLength())
	{
		return false;
	}

	//Probably not safe to execute console commands while testing in tools in general.
	//May need to add some filter function if there are some that are safe , and would be useful for testing.
	//RAR 12/03/2007
	//(MH - 04.09.2008) commands for Display Conditions are now added to the Tools_GUIMenu_PreviewOverlay,
	//so if a command exists in there, we should use the tickbox value and pass it to the output callback for
	//the display condition. Other commands should not be executed.
	if(Tools_Mode_GUIMenu::IsPreview())
	{
		const Tools_GUIMenu_ConsoleData* pxCmdData = Tools_GUIMenu_PreviewOverlay::GetConsoleVarData(m_xConsoleCmdName.GetString());
		if (pxCmdData)
		{
			bool bCommandOutput = Tools_GUIMenu_PreviewOverlay::GetBoolValue(m_xConsoleCmdName.GetString());
			Asura_Assert(pfnOutputCallback, "expected console printf to be overridden");
			if (pfnOutputCallback)
			{
				if (bCommandOutput)
				{
					pfnOutputCallback(L"1");
				}
				else
				{
					pfnOutputCallback(L"0");
				}
				return true;
			}
		}
		return false;
	}

#endif

	Asura_Assert(m_xConsoleCmdName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE), "invalid command name");

	bool bReturn = false;
	if (m_xConsoleCmdName.GetLength())
	{
		Asura_UnicodeChar awcBuffer[256];
		const char* szArgs = szArgumentOverride ? szArgumentOverride : m_xConsoleCmdArgs.GetString();
		if (!szArgs)
		{
			szArgs = "";
		}
		Asura_PrintfToBfr( awcBuffer, L"%S %S", 256, true, m_xConsoleCmdName.GetString(), szArgs );
#ifdef ASURA_ASSERT
		bool bExecuteSuccess = 
#endif
		Asura_CommandConsole::Execute( awcBuffer, &bReturn, pfnOutputCallback );
		Asura_Assertf(bExecuteSuccess, ("failed to execute console command %s, perhaps it doesn't exist", m_xConsoleCmdName.GetString()));
	}

	return bReturn;
}

bool Asura_GUIMenu_ConsoleCmd::ReadFromChunkStream( Asura_Chunk_Stream& xStream )
{
	u_int uVersion;
	xStream >> uVersion;
	if (uVersion > uASURA_GUIMENU_CONSOLECMD_VERSION)
	{
		Asura_Assertf(false, ("GUIMenu Console Command version unknown (%u > %u)", 
			uVersion, uASURA_GUIMENU_CONSOLECMD_VERSION));
		return false;
	}

	xStream >> m_xConsoleCmdName;
	xStream >> m_xConsoleCmdArgs;
	return true;
}


void Asura_GUIMenu_ConsoleCmd::Copy(const Asura_GUIMenu_ConsoleCmd* pxCmd)
{
	m_xConsoleCmdName = pxCmd->m_xConsoleCmdName;
	m_xConsoleCmdArgs = pxCmd->m_xConsoleCmdArgs;
}

#ifdef ASURA_TOOLS
void Asura_GUIMenu_ConsoleCmd::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
	xStream << uASURA_GUIMENU_CONSOLECMD_VERSION;
	if (!m_xConsoleCmdName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE))
	{
		// write out empty strings if none selected
		const Asura_String xEmpty("");
		xStream << xEmpty;	// name
		xStream << xEmpty;	// args
	}
	else
	{
		xStream << m_xConsoleCmdName;
		xStream << m_xConsoleCmdArgs;
	}
}

void Asura_GUIMenu_ConsoleCmd::ExportToXML(Tools_GUIMenu_XMLExporter* pxExporter) const
{
	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer("<ConsoleCmd name=\"");
	pxExporter->AddToBufferEscaped( m_xConsoleCmdName.GetString() );
	pxExporter->AddToBuffer("\" args=\"");
	pxExporter->AddToBufferEscaped( m_xConsoleCmdArgs.GetString() );
 	pxExporter->AddToBuffer("\" />\n");
}



#endif

bool Asura_GUIMenu_ConsoleCmd::IsBlank() const
{
	if(m_xConsoleCmdName.IsBlank()) return true;
	#ifdef ASURA_TOOLS
	return stricmp(m_xConsoleCmdName.GetString(),ASURA_GUIMENU_CONSOLEVAR_NONE)==0;
	#else
	return false;
	#endif
}



//////////////////////////////////////////////////////////////////////////////
// Asura_GUIMenu_ConsoleVar
//////////////////////////////////////////////////////////////////////////////

Asura_GUIMenu_ConsoleVar::Asura_GUIMenu_ConsoleVar():
	m_eConsoleVarType( Asura_CommandConsole_VarRepository::Var::TYPE_INVALID ),
	m_xConsoleVarName(""),
	m_uConsoleVarNameHash( ASURA_HASH_ID_UNSET )
	{}

Asura_GUIMenu_ConsoleVar::Asura_GUIMenu_ConsoleVar( const Asura_GUIMenu_ConsoleVar& xOther ):
	m_eConsoleVarType( xOther.GetType() ),
	m_xConsoleVarName( xOther.GetName() ),
	m_uConsoleVarNameHash( xOther.GetHash() )
{}

Asura_GUIMenu_ConsoleVar::Asura_GUIMenu_ConsoleVar( const Asura_String& xName ):
	m_eConsoleVarType( Asura_CommandConsole_VarRepository::Var::TYPE_INVALID ),
	m_xConsoleVarName(xName),
	m_uConsoleVarNameHash( ASURA_HASH_ID_UNSET )
{
	Update();
}

void Asura_GUIMenu_ConsoleVar::SetName( const Asura_String& xName )
{
	m_xConsoleVarName = xName;

	Update();
}

void Asura_GUIMenu_ConsoleVar::GetBoolValue(bool& bValue) const
{
#ifdef ASURA_GUIMENU_ENABLE_METRICS
	Asura_GUIMenu_System::s_xMetrics.uGetConsoleVarCalls_Bool++;
#endif

#ifdef ASURA_TOOLS
	// in tools we might have the var set to "*None*", as this is only 
	// changed to a blank string when saving the chunk.
	if (!m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE))
	{
		return;
	}
#endif
	Asura_Assert(m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE), "invalid command name");

	if (m_xConsoleVarName.GetLength())
	{
		#ifdef ASURA_TOOLS
		//Get/set variables from the tools system while previewing
		if(Tools_Mode_GUIMenu::IsPreview() && ShouldBeHandledByTools())
		{
			bValue = Tools_GUIMenu_PreviewOverlay::GetBoolValue( m_xConsoleVarName.GetString() );
		}
		else
		#endif
		{
//			bValue = Asura_CommandConsole_VarRepository::GetBoolValue( m_xConsoleVarName.GetString() );
			bValue = Asura_CommandConsole_VarRepository::GetBoolValue( m_uConsoleVarNameHash);
		}
	}
}

void Asura_GUIMenu_ConsoleVar::GetFloatValue(float& fValue) const
{
#ifdef ASURA_GUIMENU_ENABLE_METRICS
	Asura_GUIMenu_System::s_xMetrics.uGetConsoleVarCalls_Float++;
#endif

#ifdef ASURA_TOOLS
	// in tools we might have the var set to "*None*", as this is only 
	// changed to a blank string when saving the chunk.
	if (!m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE))
	{
		return;
	}
#endif
	Asura_Assert(m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE), "invalid command name");

	if (m_xConsoleVarName.GetLength())
	{
		#ifdef ASURA_TOOLS
		//Get/set variables from the tools system while previewing
		if(Tools_Mode_GUIMenu::IsPreview() && ShouldBeHandledByTools())
		{
			fValue = Tools_GUIMenu_PreviewOverlay::GetFloatValue( m_xConsoleVarName.GetString() );
		}
		else
		#endif
		{
//			fValue = Asura_CommandConsole_VarRepository::GetFloatValue( m_xConsoleVarName.GetString() );
			fValue = Asura_CommandConsole_VarRepository::GetFloatValue( m_uConsoleVarNameHash);
		}
	}
}

void Asura_GUIMenu_ConsoleVar::GetIntValue(int& iValue) const
{
#ifdef ASURA_GUIMENU_ENABLE_METRICS
	Asura_GUIMenu_System::s_xMetrics.uGetConsoleVarCalls_Int++;
#endif


#ifdef ASURA_TOOLS
	// in tools we might have the var set to "*None*", as this is only 
	// changed to a blank string when saving the chunk.
	if (!m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE))
	{
		return;
	}
#endif
	Asura_Assert(m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE), "invalid command name");

	if (m_xConsoleVarName.GetLength())
	{
		#ifdef ASURA_TOOLS
		//Get/set variables from the tools system while previewing
		if(Tools_Mode_GUIMenu::IsPreview() && ShouldBeHandledByTools())
		{
			iValue = Tools_GUIMenu_PreviewOverlay::GetIntValue( m_xConsoleVarName.GetString() );
		}
		else
		#endif
		{
//			iValue = Asura_CommandConsole_VarRepository::GetIntValue( m_xConsoleVarName.GetString() );
			iValue = Asura_CommandConsole_VarRepository::GetIntValue( m_uConsoleVarNameHash);
		}
	}
}

void Asura_GUIMenu_ConsoleVar::GetStringValue(Asura_UnicodeChar* wszValue, u_int uMaxLength) const
{
#ifdef ASURA_GUIMENU_ENABLE_METRICS
	Asura_GUIMenu_System::s_xMetrics.uGetConsoleVarCalls_String++;
#endif

#ifdef ASURA_TOOLS
	// in tools we might have the var set to "*None*", as this is only 
	// changed to a blank string when saving the chunk.
	if (!m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE))
	{
		return;
	}
#endif
	Asura_Assert(m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE), "invalid command name");

	if (m_xConsoleVarName.GetLength())
	{
		const Asura_UnicodeChar* wszCurVal;
		#ifdef ASURA_TOOLS
		//Get/set variables from the tools system while previewing
		if(Tools_Mode_GUIMenu::IsPreview() && ShouldBeHandledByTools())
		{
			wszCurVal = Tools_GUIMenu_PreviewOverlay::GetUnicodeStringValue( m_xConsoleVarName.GetString() );
		}
		else
		#endif
		{
//			wszCurVal = Asura_CommandConsole_VarRepository::GetUnicodeStringValue( m_xConsoleVarName.GetString() );
			wszCurVal = Asura_CommandConsole_VarRepository::GetUnicodeStringValue( m_uConsoleVarNameHash);
		}
		if (wszCurVal)
		{
			Asura_PrintfToBfr( wszValue, L"%s", uMaxLength + 1, true, wszCurVal );
		}
		else if (uMaxLength)
		{
			*wszValue = 0;
		}
	}
}

void Asura_GUIMenu_ConsoleVar::SetBoolValue(bool bValue) const
{
#ifdef ASURA_TOOLS
	// in tools we might have the var set to "*None*", as this is only 
	// changed to a blank string when saving the chunk.
	if (!m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE))
	{
		return;
	}
#endif
	Asura_Assert(m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE), "invalid command name");

	if (m_xConsoleVarName.GetLength())
	{
		#ifdef ASURA_TOOLS
		//Get/set variables from the tools system while previewing
		if(Tools_Mode_GUIMenu::IsPreview() && ShouldBeHandledByTools())
		{
			Tools_GUIMenu_PreviewOverlay::SetBoolValue( m_xConsoleVarName.GetString(), bValue );
		}
		else
		#endif
		{
			const bool bOK = Asura_CommandConsole_VarRepository::SetBoolValue( m_xConsoleVarName.GetString(), bValue );
			(void)bOK;
			Asura_Assertf(bOK, ("failed  to set var %s", m_xConsoleVarName.GetString()));
		}
		
	}
}

void Asura_GUIMenu_ConsoleVar::SetFloatValue(float fValue) const
{
#ifdef ASURA_TOOLS
	// in tools we might have the var set to "*None*", as this is only 
	// changed to a blank string when saving the chunk.
	if (!m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE))
	{
		return;
	}
#endif
	Asura_Assert(m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE), "invalid command name");

	if (m_xConsoleVarName.GetLength())
	{
		#ifdef ASURA_TOOLS
		//Get/set variables from the tools system while previewing
		if(Tools_Mode_GUIMenu::IsPreview() && ShouldBeHandledByTools())
		{
			Tools_GUIMenu_PreviewOverlay::SetFloatValue( m_xConsoleVarName.GetString(), fValue );
		}
		else
		#endif
		{
			const bool bOK = Asura_CommandConsole_VarRepository::SetFloatValue( m_xConsoleVarName.GetString(), fValue );
			(void)bOK;
			Asura_Assertf(bOK, ("failed  to set var %s", m_xConsoleVarName.GetString()));
		}
	}
}

void Asura_GUIMenu_ConsoleVar::SetIntValue(int iValue) const
{
#ifdef ASURA_TOOLS
	// in tools we might have the var set to "*None*", as this is only 
	// changed to a blank string when saving the chunk.
	if (!m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE))
	{
		return;
	}
#endif
	Asura_Assert(m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE), "invalid command name");

	if (m_xConsoleVarName.GetLength())
	{
		#ifdef ASURA_TOOLS
		//Get/set variables from the tools system while previewing
		if(Tools_Mode_GUIMenu::IsPreview() && ShouldBeHandledByTools())
		{
			Tools_GUIMenu_PreviewOverlay::SetIntValue( m_xConsoleVarName.GetString(), iValue );
		}
		else
		#endif
		{
			const bool bOK = Asura_CommandConsole_VarRepository::SetIntValue( m_xConsoleVarName.GetString(), iValue );
			(void)bOK;
			Asura_Assertf(bOK, ("failed  to set var %s", m_xConsoleVarName.GetString()));
		}
	}
}

void Asura_GUIMenu_ConsoleVar::SetStringValue(const Asura_UnicodeChar* wszValue) const
{
#ifdef ASURA_TOOLS
	// in tools we might have the var set to "*None*", as this is only 
	// changed to a blank string when saving the chunk.
	if (!m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE))
	{
		return;
	}
#endif
	Asura_Assert(m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE), "invalid command name");

	if (m_xConsoleVarName.GetLength())
	{
		#ifdef ASURA_TOOLS
		//Get/set variables from the tools system while previewing
		if(Tools_Mode_GUIMenu::IsPreview() && ShouldBeHandledByTools())
		{
			Tools_GUIMenu_PreviewOverlay::SetUnicodeStringValue( m_xConsoleVarName.GetString(), wszValue );
		}
		else
		#endif
		{
			const bool bOK = Asura_CommandConsole_VarRepository::SetUnicodeStringValue( m_xConsoleVarName.GetString(), wszValue );
			(void)bOK;
			Asura_Assertf(bOK, ("failed  to set var %s", m_xConsoleVarName.GetString()));
		}
	}
}


u_int Asura_GUIMenu_ConsoleVar::GetStringBufferSize() const
{
#ifdef ASURA_TOOLS
	// in tools we might have the var set to "*None*", as this is only 
	// changed to a blank string when saving the chunk.
	if (!m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE))
	{
		return 0;
	}
#endif
	Asura_Assert(m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE), "invalid command name");

	if (m_xConsoleVarName.GetLength())
	{
		const Asura_CommandConsole_VarRepository::Var* pxVar = Asura_CommandConsole_VarRepository::Find( m_xConsoleVarName.GetString());
		if (pxVar && pxVar->m_eType == Asura_CommandConsole_VarRepository::Var::TYPE_STRING)
		{
			return pxVar->m_xData.uBfrSize;
		}
	}
	return 0;
}


bool Asura_GUIMenu_ConsoleVar::ReadFromChunkStream( Asura_Chunk_Stream& xStream )
{
	u_int uVersion;
	xStream >> uVersion;
	if (uVersion > uASURA_GUIMENU_CONSOLEVAR_VERSION)
	{
		Asura_Assertf(false, ("GUIMenu Console Var version unknown (%u > %u)", 
			uVersion, uASURA_GUIMENU_CONSOLEVAR_VERSION));
		return false;
	}

	xStream >> m_xConsoleVarName;
	
	Update();
	
	return true;
}

void Asura_GUIMenu_ConsoleVar::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
	xStream << uASURA_GUIMENU_CONSOLEVAR_VERSION;
	if (!m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE))
	{
		// write out empty strings if none selected
		const Asura_String xEmpty("");
		xStream << xEmpty;	// name
	}
	else
	{
		xStream << m_xConsoleVarName;
	}
}

int Asura_GUIMenu_ConsoleVar::GetChunkSize() const
{
	int iChunkSize = sizeof(u_int);   

	if (!m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE))
	{
		const Asura_String xEmpty("");
		iChunkSize += xEmpty.GetSizeInInts() * 4;
	}
	else
	{
		iChunkSize += m_xConsoleVarName.GetSizeInInts() * 4;
	}

	return iChunkSize;
}

#ifdef ASURA_TOOLS
void Asura_GUIMenu_ConsoleVar::ExportToXML( Tools_GUIMenu_XMLExporter* pxExporter ) const
{
	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer("<ConsoleVar name=\"");
	pxExporter->AddToBufferEscaped( m_xConsoleVarName.GetString() );
	pxExporter->AddToBuffer("\" >\n");
	pxExporter->IncTabs();

	if (!m_xConsoleVarName.Compare(ASURA_GUIMENU_CONSOLEVAR_NONE))
	{
		pxExporter->AddToBuffer( pxExporter->GetTabs() );
		pxExporter->AddToBuffer( "<name>*None*</name>\n" );
	}
	else
	{
		pxExporter->AddToBuffer( pxExporter->GetTabs() );
		pxExporter->AddToBuffer( "<name>" );
		pxExporter->AddToBufferEscaped( m_xConsoleVarName.GetString() );
		pxExporter->AddToBuffer( "</name>\n" );
	}

	pxExporter->DecTabs();
	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer("</ConsoleVar>\n");
}
#endif

bool Asura_GUIMenu_ConsoleVar::IsBlank() const
{
	if(m_xConsoleVarName.IsBlank()) return true;
	#ifdef ASURA_TOOLS
	return stricmp(m_xConsoleVarName.GetString(),ASURA_GUIMENU_CONSOLEVAR_NONE)==0;
	#else
	return false;
	#endif
}

void Asura_GUIMenu_ConsoleVar::Copy(const Asura_GUIMenu_ConsoleVar* pxVar)
{
	m_eConsoleVarType		= pxVar->GetType();
	m_xConsoleVarName		= pxVar->GetName();
	m_uConsoleVarNameHash	= pxVar->GetHash();
}

void Asura_GUIMenu_ConsoleVar::Update()
{
	if( IsBlank() )
	{
		m_uConsoleVarNameHash = ASURA_HASH_ID_UNSET;
	}
	else
	{
		// update hash id
		m_uConsoleVarNameHash = Asura_GetHashID( m_xConsoleVarName.GetString());
		
		// update type
		Asura_CommandConsole_VarRepository::Var* pxConsoleVar( Asura_CommandConsole_VarRepository::Find( GetHash() ) );
		
		if( pxConsoleVar )
		{	
			m_eConsoleVarType = pxConsoleVar->m_eType;
		}
	}
}

#ifdef ASURA_TOOLS
const bool Asura_GUIMenu_ConsoleVar::FindAndReplaceConsoleVarName( const Asura_UnicodeChar* wszName,const Asura_UnicodeChar* wszReplacementName )
{
	bool bMatchFound(false);

	if( wszName && wszReplacementName )
	{
		Asura_Char szName[256];
		Asura_Char szReplacementName[256];

		Asura_PrintfToBfr( szName, "%S", 256, true, wszName );
		Asura_PrintfToBfr( szReplacementName, "%S", 256, true, wszReplacementName );
	
		bMatchFound = FindAndReplaceConsoleVarName( szName,szReplacementName );
	}

	return bMatchFound;
}

const bool Asura_GUIMenu_ConsoleVar::FindAndReplaceConsoleVarName( const Asura_Char* szName,const Asura_Char* szReplacementName )
{
	bool bMatchFound(false);

	// if var name matches szName then rename it			
	if( szName && szReplacementName && ( strcmp( GetName().GetString(),szName ) == 0 ) )
	{
		SetName( szReplacementName );
		bMatchFound = true;	
	}

	return bMatchFound;
}
#endif // ASURA_TOOLS

// --------------------------------------------------------------------------------------- 
// Asura_GUIMenu_ConditionVar
// --------------------------------------------------------------------------------------- 
Asura_UnicodeChar Asura_GUIMenu_ConditionVar::s_awszCmdOutput[256] = {0};


Asura_GUIMenu_ConditionVar::Asura_GUIMenu_ConditionVar()
{
	m_eCondition = ASURA_GUIMENU_CONDITION_NONE;
}

bool Asura_GUIMenu_ConditionVar::IsConditionMet() const
{
	// RWM - IsBlank functions test for set to *None* when in tools
	if (m_xVar.IsBlank() && m_xCmd.IsBlank())
	{
		return true;
	}
	
	#ifdef ASURA_TOOLS
	const Tools_GUIMenu_ConsoleData* pxToolsVar = 0;
	#endif
	const Asura_CommandConsole_VarRepository::Var* pxVar = 0;
	int iVarType = 0;
	
	// Var or cmd?
	if (!m_xVar.IsBlank())
	{
		#ifdef ASURA_TOOLS
		//If previewing menus in tools , then we get the values for the console variables from the tools system instead
		//RAR 12/02/2007
		if(Tools_Mode_GUIMenu::IsPreview() && m_xVar.ShouldBeHandledByTools())
		{
			pxToolsVar = Tools_GUIMenu_PreviewOverlay::GetConsoleVarData(m_xVar.GetName().GetString());
			if(!pxToolsVar) return true;

			iVarType = pxToolsVar->m_iType;
			if(iVarType == Asura_CommandConsole_VarRepository::Var::TYPE_INVALID)
			{
				iVarType = Asura_CommandConsole_VarRepository::Var::TYPE_INT;
			}
		}
		else
		#endif
		{
			Asura_Assert( m_xVar.GetHash() != ASURA_HASH_ID_UNSET, "Asura_GUIMenu_ConditionVar::IsConditionMet - Console Var Hash Name is UNSET. A programmer should investigate why this is.");
			pxVar = Asura_CommandConsole_VarRepository::Find( m_xVar.GetHash() );
			if (!pxVar)
			{
				Asura_Assertf( pxVar, ("Asura_GUIMenu_ConditionVar::IsConditionMet - Failed to find a command console var using Hash of %s.  Now looking it up using string name.", m_xVar.GetName().GetString()));

				pxVar = Asura_CommandConsole_VarRepository::Find( m_xVar.GetHash() );
				if (!pxVar)
				{
					Asura_Assertf(false, ("failed to find condition var %s", m_xVar.GetName().GetString() ));
					return true;
				}
			}

			iVarType = pxVar->m_eType;
		}
	}
	else
	{
		// Console command; test with return value
		bool bRetVal = m_xCmd.Execute(NULL, CommandCallback);

		if (bRetVal)
		{
			// Okay, we've no idea what the types are for this, so we'll have to do some guesswork...
			bool bArg;
			bool bReturn;
			int iArg;
			int iReturn;
			float fArg;
			float fReturn;
			if (Asura_StringUtil::GetBoolValue(m_xValue.GetString(), &bArg) && Asura_StringUtil::GetBoolValue( s_awszCmdOutput, &bReturn ) )
			{
				return IsConditionMet( bArg, bReturn );
			}
			else if (Asura_StringUtil::GetIntValue(m_xValue.GetString(), &iArg) && Asura_StringUtil::GetIntValue(s_awszCmdOutput, &iReturn))
			{
				return IsConditionMet( iArg, iReturn );
			}
			else if (Asura_StringUtil::GetFloatValue(m_xValue.GetString(), &fArg) && Asura_StringUtil::GetFloatValue(s_awszCmdOutput, &fReturn))
			{
				return IsConditionMet( fArg, fReturn );
			}
			else
			{
				Asura_Assert(false, "IsConditionMet: Failed to get value from either input or output string to test condition cmd");
				return true;
			}
		}
		return false;
	}

	switch (iVarType)
	{
		case Asura_CommandConsole_VarRepository::Var::TYPE_BOOL:
		{
			bool bVar;
			#ifdef ASURA_TOOLS
			if(pxToolsVar)
			{
				bVar = pxToolsVar->m_bData;
			}
			else
			#endif
			{
				m_xVar.GetBoolValue(bVar);
			}
			bool bCompare;
			if (Asura_StringUtil::GetBoolValue(m_xValue.GetString(), &bCompare))
			{
				return IsConditionMet( bVar, bCompare );
			}
			else
			{
#if defined(ASURA_ASSERT)
				char	szAssertText[256];

				Asura_PrintfToBfr(szAssertText, "Failed to get bool value from \"%s\" (variable \"%S\")", 256, true, m_xValue.GetString(), pxVar->m_wszName);
				Asura_Assert(false, szAssertText);
#endif	//defined(ASURA_ASSERT)
			}
			break;
		}
		case Asura_CommandConsole_VarRepository::Var::TYPE_INT:
		{
			int iVar;
			#ifdef ASURA_TOOLS
			if(pxToolsVar)
			{
				iVar = pxToolsVar->m_iData;
			}
			else
			#endif
			{
				m_xVar.GetIntValue(iVar);
			}
			int iCompare;
			if (Asura_StringUtil::GetIntValue(m_xValue.GetString(), &iCompare))
			{
				return IsConditionMet( iVar, iCompare );
			}
			else 
			{
				Asura_Hash_ID uHash = Asura_GetHashID(m_xValue.GetString());

				if ( uHash != ASURA_HASH_ID_UNSET )
				{
					return IsConditionMet( iVar, static_cast<int>(uHash) );
				}
				else
				{
#if defined(ASURA_ASSERT)
					char	szAssertText[256];

					Asura_PrintfToBfr(szAssertText, "Failed to get int value from \"%s\" (variable \"%S\")", 256, true, m_xValue.GetString(), pxVar->m_wszName);
					Asura_Assert(false, szAssertText);
#endif	//defined(ASURA_ASSERT)
				}
			}
			break;
		}
		case Asura_CommandConsole_VarRepository::Var::TYPE_FLOAT:
		{
			float fVar;
			#ifdef ASURA_TOOLS
			if(pxToolsVar)
			{
				fVar = pxToolsVar->m_fData;
			}
			else
			#endif
			{
				m_xVar.GetFloatValue(fVar);
			}
			float fCompare;
			if (Asura_StringUtil::GetFloatValue(m_xValue.GetString(), &fCompare))
			{
				return IsConditionMet( fVar, fCompare );
			}
			else
			{
#if defined(ASURA_ASSERT)
				char	szAssertText[256];

				Asura_PrintfToBfr(szAssertText, "Failed to get float value from \"%s\" (variable \"%S\")", 256, true, m_xValue.GetString(), pxVar->m_wszName);
				Asura_Assert(false, szAssertText);
#endif	//defined(ASURA_ASSERT)
			}
			break;
		}
		default:
		{
			Asura_Assert(false, "unsupported variable type");
			break;
		}
	}

	return true;
}


template<class T> bool Asura_GUIMenu_ConditionVar::IsConditionMet(T tVarVal, T tCompVal) const
{
	switch (m_eCondition)
	{
		default:
		// fallthrough
		case ASURA_GUIMENU_CONDITION_NONE:
		{
			Asura_Assert(false, "invalid condition");
			return true;
		}
		
		case ASURA_GUIMENU_CONDITION_EQUAL:
		{
			return tVarVal == tCompVal;
		}

		case ASURA_GUIMENU_CONDITION_GREATER:
		{
			return tVarVal > tCompVal;
		}

		case ASURA_GUIMENU_CONDITION_GREATER_EQUAL:
		{
			return tVarVal >= tCompVal;
		}

		case ASURA_GUIMENU_CONDITION_LESS:
		{
			return tVarVal < tCompVal;
		}

		case ASURA_GUIMENU_CONDITION_LESS_EQUAL:
		{
			return tVarVal <= tCompVal;
		}

		case ASURA_GUIMENU_CONDITION_NOT_EQUAL:
		{
			return tVarVal != tCompVal;
		}

	}
}

bool Asura_GUIMenu_ConditionVar::ReadFromChunkStream( Asura_Chunk_Stream& xStream )
{
	u_int uVersion;
	xStream >> uVersion;
	if (uVersion > ASURA_CHUNK_CONDITION_VAR_VERSION)
	{
		Asura_Assertf(false, ("GUIMenu Console Command version unknown (%u > %u)", 
			uVersion, ASURA_CHUNK_CONDITION_VAR_VERSION));
		return false;
	}

	// only continue processing if var is read in OK.
	if (m_xVar.ReadFromChunkStream(xStream))
	{
		if (uVersion >= 1)
		{
			m_xCmd.ReadFromChunkStream(xStream);
		}

		xStream >> m_xValue;
		u_int uCondition;
		xStream >> uCondition;
		m_eCondition = static_cast<Asura_GUIMenu_Display_Condition>(uCondition);
		return true;
	}
	return false;
}

void Asura_GUIMenu_ConditionVar::Copy(const Asura_GUIMenu_ConditionVar* pxCondVar)
{
	m_xVar.Copy(&pxCondVar->m_xVar);
	m_xCmd.Copy(&pxCondVar->m_xCmd);
	m_xValue = pxCondVar->m_xValue;
	m_eCondition = pxCondVar->m_eCondition;
}

#ifdef ASURA_TOOLS
void Asura_GUIMenu_ConditionVar::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
	xStream << ASURA_CHUNK_CONDITION_VAR_VERSION;
	m_xVar.WriteToChunkStream( xStream );
	m_xCmd.WriteToChunkStream( xStream );
	xStream << m_xValue;
	xStream << static_cast<u_int>(m_eCondition);
}


void Asura_GUIMenu_ConditionVar::ExportToXML( Tools_GUIMenu_XMLExporter* pxExporter ) const
{
	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer("<ConditionVar>\n");
	pxExporter->IncTabs();

	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer( "<console_var>\n" );
	pxExporter->IncTabs();
	m_xVar.ExportToXML( pxExporter );
	pxExporter->DecTabs();
	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer( "</console_var>\n" );

	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer( "<console_cmd>\n" );
	pxExporter->IncTabs();
	m_xCmd.ExportToXML( pxExporter );
	pxExporter->DecTabs();
	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer( "</console_cmd>\n" );

	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer( "<value>" );
	pxExporter->AddToBufferEscaped( m_xValue.GetString() );
	pxExporter->AddToBuffer( "</value>\n" );

	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer( "<condition>" );
	pxExporter->AddToBuffer( static_cast<u_int>(m_eCondition) );
	pxExporter->AddToBuffer( "</condition>\n" );

	pxExporter->DecTabs();
	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer("</ConditionVar>\n");
}

#endif // #ifdef ASURA_TOOLS

void Asura_GUIMenu_ConditionVar::CommandCallback(const Asura_UnicodeChar* pwszInput)
{
	const Asura_UnicodeChar awszFormatString[] = L"%s";
	Asura_PrintfToBfr(s_awszCmdOutput, awszFormatString, 255, true, pwszInput);
}

// --------------------------------------------------------------------------------------- 
// Asura_GUIMenu_ConditionVarManager
// --------------------------------------------------------------------------------------- 
Asura_GUIMenu_ConditionVarManager::Asura_GUIMenu_ConditionVarManager()
{
	m_eOperator = ASURA_GUIMENU_LOGIC_AND;
	m_uNumConditionVars = 0;
	m_pxConditionVars = 0;
	m_bForcedHidden = false;
}

Asura_GUIMenu_ConditionVarManager::~Asura_GUIMenu_ConditionVarManager()
{
	delete [] m_pxConditionVars;
}

bool Asura_GUIMenu_ConditionVarManager::IsConditionMet() const
{
	// if forced hidden say condition is false
	if (m_bForcedHidden)
	{
		return false;
	}

	//condition is always met if there aren't any conditions
	if(!m_uNumConditionVars) 
	{
		return true;
	}

	bool bResult = true;

	switch(m_eOperator)
	{
		case ASURA_GUIMENU_LOGIC_AND :
		{
			for(u_int i=0;i<m_uNumConditionVars;i++)
			{
				if(!m_pxConditionVars[i].IsConditionMet())
				{
					bResult = false;
					break;
				}
			}
			break;
		}
		case ASURA_GUIMENU_LOGIC_OR :
		{
			bResult = false;
			for(u_int i=0;i<m_uNumConditionVars;i++)
			{
				if(m_pxConditionVars[i].IsConditionMet())
				{
					bResult = true;
					break;
				}
			}
			break;
		}
		case ASURA_GUIMENU_LOGIC_XOR :
		{
			bResult = false;
			for(u_int i=0;i<m_uNumConditionVars;i++)
			{
				//ignore blank condition vars
				if(m_pxConditionVars[i].IsVarBlank() && m_pxConditionVars[i].IsCmdBlank()) continue;

				if(m_pxConditionVars[i].IsConditionMet())
				{
					bResult = !bResult;
				}
			}
			break;
		}

		default :
			Asura_Assert(0,"Unknown logic type.");
			break;
	}

	return bResult;

}

void Asura_GUIMenu_ConditionVarManager::Copy(const Asura_GUIMenu_ConditionVarManager* pxManager)
{
	m_eOperator = pxManager->m_eOperator;
	m_uNumConditionVars = pxManager->m_uNumConditionVars;

	delete [] m_pxConditionVars;
	m_pxConditionVars = m_uNumConditionVars ? new Asura_GUIMenu_ConditionVar[m_uNumConditionVars] : 0;

	for(u_int i=0;i<m_uNumConditionVars;i++)
	{
		m_pxConditionVars[i].Copy(&pxManager->m_pxConditionVars[i]);
	}
}


bool Asura_GUIMenu_ConditionVarManager::ReadFromChunkStream( Asura_Chunk_Stream& xStream )
{
	u_int uVersion;
	xStream >> uVersion;
	if (uVersion > ASURA_CHUNK_CONDITION_VAR_VERSION)
	{
		Asura_Assertf(false, ("GUIMenu condition var manager  version unknown (%u > %u)", 
			uVersion, ASURA_CHUNK_CONDITION_VAR_VERSION));
		return false;
	}

	u_int uOperator;
	xStream >> uOperator;
	m_eOperator = (Asura_GUIMenu_LogicalOperator) uOperator;
	xStream >> m_uNumConditionVars;
	if(m_uNumConditionVars) m_pxConditionVars = new Asura_GUIMenu_ConditionVar[m_uNumConditionVars];

	for(u_int i=0;i<m_uNumConditionVars;i++)
	{
		if(!m_pxConditionVars[i].ReadFromChunkStream(xStream)) return false;
	}
	return true;
}


#ifdef ASURA_TOOLS
void Asura_GUIMenu_ConditionVarManager::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
	//only want to save non-blank condition vars
	u_int uVarsToSave = 0;
	for(u_int i=0;i<m_uNumConditionVars;i++)
	{
		if(!m_pxConditionVars[i].IsVarBlank() || !m_pxConditionVars[i].IsCmdBlank())
		{
			uVarsToSave++;
		}
	}


	xStream << ASURA_CHUNK_CONDITION_VAR_MANAGER_VERSION;
	xStream << static_cast<u_int>(m_eOperator);
	xStream << uVarsToSave;
	for(u_int i=0;i<m_uNumConditionVars;i++)
	{
		if(!m_pxConditionVars[i].IsVarBlank() || !m_pxConditionVars[i].IsCmdBlank())
		{
			m_pxConditionVars[i].WriteToChunkStream(xStream);
		}
	}
}

void Asura_GUIMenu_ConditionVarManager::ExportToXML( Tools_GUIMenu_XMLExporter* pxExporter ) const
{
	//only want to write out non-blank condition vars
	u_int uVarsToSave = 0;
	for(u_int i=0;i<m_uNumConditionVars;i++)
	{
		if(!m_pxConditionVars[i].IsVarBlank() || !m_pxConditionVars[i].IsCmdBlank())
		{
			uVarsToSave++;
		}
	}
	if(!uVarsToSave) return;
	

	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer("<console_var_conditions>\n");
	pxExporter->IncTabs();


	const char* szOperator = 0;
	switch(m_eOperator)
	{
		case ASURA_GUIMENU_LOGIC_AND : szOperator = "And"; break;
		case ASURA_GUIMENU_LOGIC_OR  : szOperator = "Or"; break;
		case ASURA_GUIMENU_LOGIC_XOR : szOperator = "Xor"; break;
		default :
			Asura_Assert(0,"Unknown operator type");
			szOperator = "Unknown";
			break;
	}

	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer( "<operator>" );
	pxExporter->AddToBufferEscaped( szOperator );
	pxExporter->AddToBuffer( "</operator>\n" );
	
	
	u_int uIndex = 0;
	for(u_int i=0;i<m_uNumConditionVars;i++)
	{
		if(!m_pxConditionVars[i].IsVarBlank() || !m_pxConditionVars[i].IsCmdBlank())
		{
			pxExporter->AddToBuffer( pxExporter->GetTabs() );
			pxExporter->AddToBuffer( "<condition num='" ); pxExporter->AddToBuffer( uIndex ); pxExporter->AddToBuffer( "' >\n" );
			pxExporter->IncTabs();

			m_pxConditionVars[i].ExportToXML( pxExporter );
			
			pxExporter->DecTabs();
			pxExporter->AddToBuffer( pxExporter->GetTabs() );
			pxExporter->AddToBuffer( "</condition>\n" );
		}
	}
	
	
	pxExporter->DecTabs();
	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer("</console_var_conditions>\n");

	

/*
	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer("<ConditionVarManager>\n");
	pxExporter->IncTabs();

	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer( "<console_var>\n" );
	pxExporter->IncTabs();
	m_xVar.ExportToXML( pxExporter );
	pxExporter->DecTabs();
	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer( "</console_var>\n" );

	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer( "<value>" );
	pxExporter->AddToBufferEscaped( m_xValue.GetString() );
	pxExporter->AddToBuffer( "</value>\n" );

	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer( "<condition>" );
	pxExporter->AddToBuffer( static_cast<u_int>(m_eCondition) );
	pxExporter->AddToBuffer( "</condition>\n" );

	pxExporter->DecTabs();
	pxExporter->AddToBuffer( pxExporter->GetTabs() );
	pxExporter->AddToBuffer("</ConditionVarManager>\n");
*/
}

const bool Asura_GUIMenu_ConditionVarManager::FindAndReplaceConsoleVarName( const Asura_UnicodeChar* wszName,const Asura_UnicodeChar* wszReplacementName )
{
	bool bMatchFound(false);

	if( wszName && wszReplacementName )
	{
		// convert to 8-bit char strings as that is the format used by console vars
		Asura_Char szName[256];
		Asura_Char szReplacementName[256];

		Asura_PrintfToBfr( szName, "%S", 256, true, wszName );
		Asura_PrintfToBfr( szReplacementName, "%S", 256, true, wszReplacementName );

		bMatchFound = FindAndReplaceConsoleVarName( szName,szReplacementName );
	}
	
	return bMatchFound;
}

const bool Asura_GUIMenu_ConditionVarManager::FindAndReplaceConsoleVarName( const Asura_Char* szName,const Asura_Char* szReplacementName )
{
	bool bMatchFound(false);

	if( szName && szReplacementName )
	{
		// call find and replace for each console var in this
		for( u_int uIndex(0);uIndex<m_uNumConditionVars;++uIndex )
		{
			bMatchFound |= 	m_pxConditionVars[uIndex].FindAndReplaceConsoleVarName( szName,szReplacementName );
		}
	}

	return bMatchFound;
}

#endif // #ifdef ASURA_TOOLS


#endif // ASURA_USE_GUI_MENU_SYSTEM
