//===== Copyright � 1996-2006, Valve Corporation, All rights reserved. ======//
//
// Purpose: 	ExprSimplifier builds a binary tree from an infix expression (in the
//				form of a character array). Evaluates C style infix parenthetic logical
//				expressions. Supports !, ||, &&, (). Symbols are resolved via callback.
//				Syntax is $<name>. $0 evaluates to false. $<number> evaluates to true.
//				e.g: ( $1 || ( $FOO || $WHATEVER ) && !$BAR )
//===========================================================================//

#include <ctype.h>
#include <vstdlib/ikeyvaluessystem.h>
#include "tier1/exprevaluator.h"
#include "tier1/convar.h"
#include "tier1/fmtstr.h"
#include "tier0/dbg.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

//-----------------------------------------------------------------------------
// Default conditional symbol handler callback. Symbols are the form $<name>.
// Return true or false for the value of the symbol.
//-----------------------------------------------------------------------------
bool DefaultConditionalSymbolProc( const char *pKey )
{
	if ( pKey[0] == '$' )
	{
		pKey++;
	}

	if ( !V_stricmp( pKey, "WIN32" ) )
	{
		return IsPC();
	}

	if ( !V_stricmp( pKey, "WINDOWS" ) )
	{
		return IsPlatformWindowsPC();
	}
	
	if ( !V_stricmp( pKey, "X360" ) )
	{
		return IsX360();
	}

	if ( !V_stricmp( pKey, "PS3" ) )
	{
		return IsPS3();
	}

	if ( !V_stricmp( pKey, "OSX" ) )
	{
		return IsPlatformOSX();
	}

	if ( !V_stricmp( pKey, "LINUX" ) )
	{
		return IsPlatformLinux();
	}

	if ( !V_stricmp( pKey, "POSIX" ) )
	{
		return IsPlatformPosix();
	}	
	
	if ( !V_stricmp( pKey, "GAMECONSOLE" ) )
	{
		return IsGameConsole();
	}

	if ( !V_stricmp( pKey, "DEMO" ) )
	{
#if defined( _DEMO )
		return true;
#else
		return false;
#endif
	}

	if ( !V_stricmp( pKey, "LOWVIOLENCE" ) )
	{
#if defined( _LOWVIOLENCE )
		return true;
#endif
		// If it is not a LOWVIOLENCE binary build, then fall through
		// and check if there was a run-time symbol installed for it
	}

	// don't know it at compile time, so fall through to installed symbol values
	return KeyValuesSystem()->GetKeyValuesExpressionSymbol( pKey );
}

void DefaultConditionalErrorProc( const char *pReason )
{
	Warning( "Conditional Error: %s\n", pReason );
}

CExpressionEvaluator::CExpressionEvaluator()
{
	m_ExprTree = NULL;
}

CExpressionEvaluator::~CExpressionEvaluator()
{
	FreeTree( m_ExprTree );
}

//-----------------------------------------------------------------------------
//	Sets mCurToken to the next token in the input string. Skips all whitespace.
//-----------------------------------------------------------------------------
char CExpressionEvaluator::GetNextToken( void )
{
	// while whitespace, Increment CurrentPosition
	while ( m_pExpression[m_CurPosition] == ' ' )
		++m_CurPosition;
    
	// CurrentToken = Expression[CurrentPosition]
	m_CurToken = m_pExpression[m_CurPosition++];
  
	return m_CurToken;
}


//-----------------------------------------------------------------------------
//	Utility funcs
//-----------------------------------------------------------------------------
void CExpressionEvaluator::FreeNode( ExprNode *pNode )
{
	delete pNode;
}

ExprNode *CExpressionEvaluator::AllocateNode( void )
{
	return new ExprNode;
}

void CExpressionEvaluator::FreeTree( ExprTree& node )
{
	if ( !node )
		return;

	FreeTree( node->left );
	FreeTree( node->right );
	FreeNode( node );
	node = 0;
}

bool CExpressionEvaluator::IsConditional( bool &bConditional, const char token )
{
	char nextchar = ' ';
	if ( token == OR_OP || token == AND_OP )
	{
		// expect || or &&
		nextchar = m_pExpression[m_CurPosition++];
		if ( (token & nextchar) == token )
		{
			bConditional = true;
		}
		else if ( m_pSyntaxErrorProc )
		{
			m_pSyntaxErrorProc( CFmtStr( "Bad expression operator: '%c%c', expected C style operator", token, nextchar ) );
			return false;
		}
	}
	else
	{
		bConditional = false;
	}

	// valid
	return true;
}

bool CExpressionEvaluator::IsNotOp( const char token )
{
	if ( token == NOT_OP )
		return true;
	else
		return false;
}

bool CExpressionEvaluator::IsIdentifierOrConstant( const char token )
{
	bool success = false;
	if ( token == '$' )
	{
		// store the entire identifier
		int i = 0;
		m_Identifier[i++] = token;
		while( (isalnum( m_pExpression[m_CurPosition] ) || m_pExpression[m_CurPosition] == '_') && i < MAX_IDENTIFIER_LEN )
		{
			m_Identifier[i] = m_pExpression[m_CurPosition];
			++m_CurPosition;
			++i;
		}

		if ( i < MAX_IDENTIFIER_LEN - 1 )
		{
			m_Identifier[i] = '\0';
			success = true;
		}
	}
	else
	{
		if ( isdigit( token ) )
		{
			int i = 0;
			m_Identifier[i++] = token;
			while( isdigit( m_pExpression[m_CurPosition] ) && ( i < MAX_IDENTIFIER_LEN ) )
			{
				m_Identifier[i] = m_pExpression[m_CurPosition];
				++m_CurPosition;
				++i;
			}
			if ( i < MAX_IDENTIFIER_LEN - 1 )
			{
				m_Identifier[i] = '\0';
				success = true;
			}
		}
	}

	return success;
}

bool CExpressionEvaluator::MakeExprNode( ExprTree &tree, char token, Kind kind, ExprTree left, ExprTree right )
{
	tree = AllocateNode();
	tree->left = left;
	tree->right = right;
	tree->kind = kind;

	switch ( kind )
	{
	case CONDITIONAL:
		tree->data.cond = token;
		break;

	case LITERAL:
		if ( isdigit( m_Identifier[0] ) )
		{
			tree->data.value = ( atoi( m_Identifier ) != 0 );
		}
		else
		{
			tree->data.value = m_pGetSymbolProc( m_Identifier );
		}
		break;

	case NOT:
		break;

	default:
		if ( m_pSyntaxErrorProc )
		{
			Assert( 0 );
			m_pSyntaxErrorProc( CFmtStr( "Logic Error in CExpressionEvaluator" ) );
		}
		return false;
	}

	return true;
}

//-----------------------------------------------------------------------------
//	Makes a factor :: { <expression> } | <identifier>.
//-----------------------------------------------------------------------------
bool CExpressionEvaluator::MakeFactor( ExprTree &tree )
{
	if ( m_CurToken == '(' )
	{
		// Get the next token
		GetNextToken();

		// Make an expression, setting Tree to point to it
		if ( !MakeExpression( tree ) )
		{
			return false;
		}
	}
	else if ( IsIdentifierOrConstant( m_CurToken ) )
	{
		// Make a literal node, set Tree to point to it, set left/right children to NULL. 
		if ( !MakeExprNode( tree, m_CurToken, LITERAL, NULL, NULL ) )
		{
			return false;
		}
	}
	else if ( IsNotOp( m_CurToken ) )
	{
		// do nothing
		return true;
	}
	else
	{
		// This must be a bad token
		if ( m_pSyntaxErrorProc )
		{
			m_pSyntaxErrorProc( CFmtStr( "Bad expression token: %c", m_CurToken ) );
		}
		return false;
	}

	// Get the next token
	GetNextToken();
	return true;
}

//-----------------------------------------------------------------------------
//	Makes a term :: <factor> { <not> }.
//-----------------------------------------------------------------------------
bool CExpressionEvaluator::MakeTerm( ExprTree &tree )
{
	// Make a factor, setting Tree to point to it
	if ( !MakeFactor( tree ) )
	{
		return false;
	}

	// while the next token is !
	while ( IsNotOp( m_CurToken ) )
	{
		// Make an operator node, setting left child to Tree and right to NULL. (Tree points to new node)
		if ( !MakeExprNode( tree, m_CurToken, NOT, tree, NULL ) )
		{
			return false;
		}

		// Get the next token.
		GetNextToken();

		// Make a factor, setting the right child of Tree to point to it.
		if ( !MakeFactor( tree->right ) )
		{
			return false;
		}
	}

	return true;
}


//-----------------------------------------------------------------------------
//	Makes a complete expression :: <term> { <cond> <term> }.
//-----------------------------------------------------------------------------
bool CExpressionEvaluator::MakeExpression( ExprTree &tree )
{
	// Make a term, setting Tree to point to it
	if ( !MakeTerm( tree ) )
	{
		return false;
	}

	// while the next token is a conditional
	while ( 1 )
	{
		bool bConditional = false;
		bool bValid = IsConditional( bConditional, m_CurToken );
		if ( !bValid )
		{
			return false;
		}

		if ( !bConditional )
		{
			break;
		}

		// Make a conditional node, setting left child to Tree and right to NULL. (Tree points to new node)
		if ( !MakeExprNode( tree, m_CurToken, CONDITIONAL, tree, NULL ) )
		{
			return false;
		}

		// Get the next token.
		GetNextToken();

		// Make a term, setting the right child of Tree to point to it.
		if ( !MakeTerm( tree->right ) )
		{
			return false;
		}
	}

	return true;
}

//-----------------------------------------------------------------------------
//	returns true for success, false for failure
//-----------------------------------------------------------------------------
bool CExpressionEvaluator::BuildExpression( void )
{
	// Get the first token, and build the tree.
	GetNextToken();

	return ( MakeExpression( m_ExprTree ) );
}

//-----------------------------------------------------------------------------
//	returns the value of the node after resolving all children
//-----------------------------------------------------------------------------
bool CExpressionEvaluator::SimplifyNode( ExprTree& node )
{
	if ( !node )
		return false;

	// Simplify the left and right children of this node
	bool leftVal = SimplifyNode(node->left);
	bool rightVal = SimplifyNode(node->right);

	// Simplify this node
	switch( node->kind )
	{
	case NOT:
		// the child of '!' is always to the right
		node->data.value = !rightVal;
		break;
	
	case CONDITIONAL:
		if ( node->data.cond == AND_OP )
		{
			node->data.value = leftVal && rightVal;
		}
		else // OR_OP
		{	
			node->data.value = leftVal || rightVal;
		}
		break;

	default: // LITERAL
		break;
	}

	// This node has beed resolved
	node->kind = LITERAL;
	return node->data.value;
}

//-----------------------------------------------------------------------------
//	Interface to solve a conditional expression. Returns false on failure, Result is undefined.
//-----------------------------------------------------------------------------
bool CExpressionEvaluator::Evaluate( bool &bResult, const char *pInfixExpression, GetSymbolProc_t pGetSymbolProc, SyntaxErrorProc_t pSyntaxErrorProc )
{
	if ( !pInfixExpression )
	{
		return false;
	}

	// for caller simplicity, we strip of any enclosing braces
	// strip the bracketing [] if present
	char szCleanToken[512];
	if ( pInfixExpression[0] == '[' )
	{
		int len = V_strlen( pInfixExpression );

		// SECURITY: Bail on input buffers that are too large, they're used for RCEs and we don't 
		// need to support them.
		if ( len + 1 > ARRAYSIZE( szCleanToken ) )
		{
			return false;
		}

		// SECURIY: Because this starts one character late, it picks up the null termination from pInfixExpression.
		V_strncpy( szCleanToken, pInfixExpression + 1, len );
		len--;
		if ( szCleanToken[len-1] == ']' )
		{
			szCleanToken[len-1] = '\0';
		}
		pInfixExpression = szCleanToken;
	}

	// reset state
	m_pExpression = pInfixExpression;
	m_pGetSymbolProc = pGetSymbolProc ? pGetSymbolProc : DefaultConditionalSymbolProc;
	m_pSyntaxErrorProc = pSyntaxErrorProc ? pSyntaxErrorProc : DefaultConditionalErrorProc;
	m_ExprTree = 0;
	m_CurPosition = 0;
	m_CurToken = 0;

	// Building the expression tree will fail on bad syntax
	bool bValid = BuildExpression();
	if ( bValid )
	{
		bResult = SimplifyNode( m_ExprTree );
	}

	// don't leak
	FreeTree( m_ExprTree );
	m_ExprTree = NULL;

	return bValid;
}