//========= Copyright © 1996-2010, Valve LLC, All rights reserved. ============
//
// Purpose: Implementation for CWebAPIResponse objects
//
//=============================================================================

#include "stdafx.h"
#include "thirdparty/JSON_parser/JSON_parser.h"

using namespace GCSDK;

#include "tier0/memdbgoff.h"

// !FIXME! DOTAMERGE
//IMPLEMENT_CLASS_MEMPOOL_MT( CWebAPIValues, 1000, UTLMEMORYPOOL_GROW_SLOW );

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

//-----------------------------------------------------------------------------
// Purpose: Helper for emitting properly escaped json string values
//-----------------------------------------------------------------------------
void EmitJSONString( CUtlBuffer &outputBuffer, const char *pchValue )
{
	outputBuffer.PutChar( '"' );

	if ( pchValue )
	{
		int i = 0;
		while( pchValue[i] )
		{
			switch ( pchValue[i] )
			{
			case '"':
				outputBuffer.Put( "\\\"", 2 );
				break;
			case '\\':
				outputBuffer.Put( "\\\\", 2 );
				break;
			case '\n':
				outputBuffer.Put( "\\n", 2 );
				break;
			case '\r':
				outputBuffer.Put( "\\r", 2 );
				break;
			case '\t':
				outputBuffer.Put( "\\t", 2 );
				break;
			default:
				if ( (uint8) pchValue[i] < 32 )
				{
					outputBuffer.Put( "\\u00", 4 );
					outputBuffer.PutChar( ( pchValue[i] & 16 ) ? '1' : '0' );
					outputBuffer.PutChar( "0123456789abcdef"[ pchValue[i] & 0xF ] );
				}
				else
				{
					outputBuffer.PutChar( pchValue[i] );
				}
			}
			++i;
		}
	}

	outputBuffer.PutChar( '"' );
}


//-----------------------------------------------------------------------------
// Purpose: Helper for emitting properly escaped XML string values, we always use UTF8,
// so we only really need to encode & ' " < >
//-----------------------------------------------------------------------------
void EmitXMLString( CUtlBuffer &outputBuffer, const char *pchValue )
{
	if ( pchValue )
	{
		int i = 0;
		while( pchValue[i] )
		{
			switch ( pchValue[i] )
			{
			case '&':
				outputBuffer.Put( "&amp;", 5 );
				break;
			case '\'':
				outputBuffer.Put( "&apos;", 6 );
				break;
			case '"':
				outputBuffer.Put( "&quot;", 6 );
				break;
			case '<':
				outputBuffer.Put( "&lt;", 4 );
				break;
			case '>':
				outputBuffer.Put( "&gt;", 4 );
				break;
			default:
				outputBuffer.PutChar( pchValue[i] );
			}
			++i;
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: Helper for emitting properly escaped VDF string values, we always use UTF8,
// and we escape only " and \
//-----------------------------------------------------------------------------
void EmitVDFString( CUtlBuffer &outputBuffer, const char *pchValue )
{
	outputBuffer.PutChar( '"' );

	if ( pchValue )
	{
		int i = 0;
		while( pchValue[i] )
		{
			switch ( pchValue[i] )
			{
			case '\\':
				outputBuffer.Put( "\\\\", 2 );
				break;
			case '"':
				outputBuffer.Put( "\\\"", 2 );
				break;
			default:
				outputBuffer.PutChar( pchValue[i] );
			}
			++i;
		}
	}

	outputBuffer.PutChar( '"' );
}

namespace GCSDK
{

enum { k_LineBreakEveryNGroups = 18 }; // line break every 18 groups of 4 characters (every 72 characters)

uint32 Base64EncodeMaxOutput( const uint32 cubData, const char *pszLineBreak )
{
	// terminating null + 4 chars per 3-byte group + line break after every 18 groups (72 output chars) + final line break
	uint32 nGroups = (cubData+2)/3;
	uint32 cchRequired = 1 + nGroups*4 + ( pszLineBreak ? Q_strlen(pszLineBreak)*(1+(nGroups-1)/k_LineBreakEveryNGroups) : 0 );
	return cchRequired;
}

bool Base64Encode( const uint8 *pubData, uint32 cubData, char *pchEncodedData, uint32 *pcchEncodedData, const char *pszLineBreak )
{
	if ( pchEncodedData == NULL )
	{
		AssertMsg( *pcchEncodedData == 0, "NULL output buffer with non-zero size passed to Base64Encode" );
		*pcchEncodedData = Base64EncodeMaxOutput( cubData, pszLineBreak );
		return true;
	}
	
	const uint8 *pubDataEnd = pubData + cubData;
	char *pchEncodedDataStart = pchEncodedData;
	uint32 unLineBreakLen = pszLineBreak ? Q_strlen( pszLineBreak ) : 0;
	int nNextLineBreak = unLineBreakLen ? k_LineBreakEveryNGroups : INT_MAX;

	const char * const pszBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

	uint32 cchEncodedData = *pcchEncodedData;
	if ( cchEncodedData == 0 )
		goto out_of_space;

	--cchEncodedData; // pre-decrement for the terminating null so we don't forget about it

	// input 3 x 8-bit, output 4 x 6-bit
	while ( pubDataEnd - pubData >= 3 )
	{
		if ( cchEncodedData < 4 + unLineBreakLen )
			goto out_of_space;
		
		if ( nNextLineBreak == 0 )
		{
			memcpy( pchEncodedData, pszLineBreak, unLineBreakLen );
			pchEncodedData += unLineBreakLen;
			cchEncodedData -= unLineBreakLen;
			nNextLineBreak = k_LineBreakEveryNGroups;
		}

		uint32 un24BitsData;
		un24BitsData  = (uint32) pubData[0] << 16;
		un24BitsData |= (uint32) pubData[1] << 8;
		un24BitsData |= (uint32) pubData[2];
		pubData += 3;

		pchEncodedData[0] = pszBase64Chars[ (un24BitsData >> 18) & 63 ];
		pchEncodedData[1] = pszBase64Chars[ (un24BitsData >> 12) & 63 ];
		pchEncodedData[2] = pszBase64Chars[ (un24BitsData >>  6) & 63 ];
		pchEncodedData[3] = pszBase64Chars[ (un24BitsData      ) & 63 ];
		pchEncodedData += 4;
		cchEncodedData -= 4;
		--nNextLineBreak;
	}

	// Clean up remaining 1 or 2 bytes of input, pad output with '='
	if ( pubData != pubDataEnd )
	{
		if ( cchEncodedData < 4 + unLineBreakLen )
			goto out_of_space;

		if ( nNextLineBreak == 0 )
		{
			memcpy( pchEncodedData, pszLineBreak, unLineBreakLen );
			pchEncodedData += unLineBreakLen;
			cchEncodedData -= unLineBreakLen;
		}

		uint32 un24BitsData;
		un24BitsData = (uint32) pubData[0] << 16;
		if ( pubData+1 != pubDataEnd )
		{
			un24BitsData |= (uint32) pubData[1] << 8;
		}
		pchEncodedData[0] = pszBase64Chars[ (un24BitsData >> 18) & 63 ];
		pchEncodedData[1] = pszBase64Chars[ (un24BitsData >> 12) & 63 ];
		pchEncodedData[2] = pubData+1 != pubDataEnd ? pszBase64Chars[ (un24BitsData >> 6) & 63 ] : '=';
		pchEncodedData[3] = '=';
		pchEncodedData += 4;
		cchEncodedData -= 4;
	}

	if ( unLineBreakLen )
	{
		if ( cchEncodedData < unLineBreakLen )
			goto out_of_space;
		memcpy( pchEncodedData, pszLineBreak, unLineBreakLen );
		pchEncodedData += unLineBreakLen;
		cchEncodedData -= unLineBreakLen;
	}

	*pchEncodedData = 0;
	*pcchEncodedData = pchEncodedData - pchEncodedDataStart;
	return true;

out_of_space:
	*pchEncodedData = 0;
	*pcchEncodedData = Base64EncodeMaxOutput( cubData, pszLineBreak );
	AssertMsg( false, "CCrypto::Base64Encode: insufficient output buffer (up to n*4/3+5 bytes required, plus linebreaks)" );
	return false;
}

bool Base64Decode( const char *pchData, uint32 cchDataMax, uint8 *pubDecodedData, uint32 *pcubDecodedData, bool bIgnoreInvalidCharacters )
{
	uint32 cubDecodedData = *pcubDecodedData;
	uint32 cubDecodedDataOrig = cubDecodedData;

	if ( pubDecodedData == NULL )
	{
		AssertMsg( *pcubDecodedData == 0, "NULL output buffer with non-zero size passed to Base64Decode" );
		cubDecodedDataOrig = cubDecodedData = ~0u;
	}
	
	// valid base64 character range: '+' (0x2B) to 'z' (0x7A)
	// table entries are 0-63, -1 for invalid entries, -2 for '='
	static const char rgchInvBase64[] = {
		62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
		-1, -1, -1, -2, -1, -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,
		 8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
		23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31,
		32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
		47, 48, 49, 50, 51
	};
	COMPILE_TIME_ASSERT( Q_ARRAYSIZE(rgchInvBase64) == 0x7A - 0x2B + 1 );

	uint32 un24BitsWithSentinel = 1;
	while ( cchDataMax-- > 0 )
	{
		char c = *pchData++;

		if ( (uint8)(c - 0x2B) >= Q_ARRAYSIZE( rgchInvBase64 ) )
		{
			if ( c == '\0' )
				break;

			if ( !bIgnoreInvalidCharacters && !( c == '\r' || c == '\n' || c == '\t' || c == ' ' ) )
				goto decode_failed;
			else
				continue;
		}

		c = rgchInvBase64[(uint8)(c - 0x2B)];
		if ( c < 0 )
		{
			if ( c == -2 ) // -2 -> terminating '='
				break;

			if ( !bIgnoreInvalidCharacters )
				goto decode_failed;
			else
				continue;
		}

		un24BitsWithSentinel <<= 6;
		un24BitsWithSentinel |= c;
		if ( un24BitsWithSentinel & (1<<24) )
		{
			if ( cubDecodedData < 3 ) // out of space? go to final write logic
				break;
			if ( pubDecodedData )
			{
				pubDecodedData[0] = (uint8)( un24BitsWithSentinel >> 16 );
				pubDecodedData[1] = (uint8)( un24BitsWithSentinel >> 8);
				pubDecodedData[2] = (uint8)( un24BitsWithSentinel );
				pubDecodedData += 3;
			}
			cubDecodedData -= 3;
			un24BitsWithSentinel = 1;
		}
	}

	// If un24BitsWithSentinel contains data, output the remaining full bytes
	if ( un24BitsWithSentinel >= (1<<6) )
	{
		// Possibilities are 3, 2, 1, or 0 full output bytes.
		int nWriteBytes = 3;
		while ( un24BitsWithSentinel < (1<<24) )
		{
			nWriteBytes--;
			un24BitsWithSentinel <<= 6;
		}

		// Write completed bytes to output
		while ( nWriteBytes-- > 0 )
		{
			if ( cubDecodedData == 0 )
			{
				AssertMsg( false, "CCrypto::Base64Decode: insufficient output buffer (up to n*3/4+2 bytes required)" );
				goto decode_failed;
			}
			if ( pubDecodedData )
			{
				*pubDecodedData++ = (uint8)(un24BitsWithSentinel >> 16);
			}
			--cubDecodedData;
			un24BitsWithSentinel <<= 8;
		}
	}
	
	*pcubDecodedData = cubDecodedDataOrig - cubDecodedData;
	return true;

decode_failed:
	*pcubDecodedData = cubDecodedDataOrig - cubDecodedData;
	return false;
}

}

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CWebAPIResponse::CWebAPIResponse()
{
	m_pValues = NULL;
	m_unExpirationSeconds = 0;
	m_rtLastModified = 0;
	m_bExtendedArrays = false;
	m_bJSONAnonymousRootNode = false;
	m_eStatusCode = k_EHTTPStatusCode500InternalServerError;
}


//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CWebAPIResponse::~CWebAPIResponse()
{
	if ( m_pValues)
		delete m_pValues;
	m_pValues = NULL;
}


//-----------------------------------------------------------------------------
// Purpose: Outputs formatted data to buffer
//-----------------------------------------------------------------------------
bool CWebAPIResponse::BEmitFormattedOutput( EWebAPIOutputFormat eFormat, CUtlBuffer &outputBuffer, size_t unMaxResultSize )
{
	VPROF_BUDGET( "CWebAPIResponse::BEmitFormattedOutput", VPROF_BUDGETGROUP_STEAM );
	outputBuffer.Clear();

	switch( eFormat )
	{
		case k_EWebAPIOutputFormat_JSON:
			return BEmitJSON( outputBuffer, unMaxResultSize );
		case k_EWebAPIOutputFormat_XML:
			return BEmitXML( outputBuffer, unMaxResultSize );
		case k_EWebAPIOutputFormat_VDF:
			return BEmitVDF( outputBuffer, unMaxResultSize );
		case k_EWebAPIOutputFormat_ParameterEncoding:
			return BEmitParameterEncoding( outputBuffer );
		default:
			return false;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Emits JSON formatted representation of response
//-----------------------------------------------------------------------------
bool CWebAPIResponse::BEmitJSON( CUtlBuffer &outputBuffer, size_t unMaxResultSize )
{
	outputBuffer.PutChar( '{' );
	outputBuffer.PutChar( '\n' );

	CWebAPIValues *pValues = m_pValues;

	//if we have an anonymous root, get the first child instead of the root itself
	if ( m_bJSONAnonymousRootNode && m_pValues )
	{
		pValues = m_pValues->GetFirstChild();
	}

	if ( pValues )
	{
		if( !CWebAPIValues::BEmitJSONRecursive( pValues, outputBuffer, 1, unMaxResultSize, m_bExtendedArrays ) )
			return false;
	}

	outputBuffer.PutChar( '\n' );
	outputBuffer.PutChar( '}' );
	if ( !outputBuffer.IsValid() )
		return false;

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Emits KeyValues .vdf style formatted representation of response
//-----------------------------------------------------------------------------
bool CWebAPIResponse::BEmitVDF( CUtlBuffer &outputBuffer, size_t unMaxResultSize )
{
	if ( m_pValues )
		if( !CWebAPIValues::BEmitVDFRecursive( m_pValues, outputBuffer, 0, 0, unMaxResultSize, m_bExtendedArrays ) )
			return false;

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Emits XML formatted representation of response
//-----------------------------------------------------------------------------
bool CWebAPIResponse::BEmitXML( CUtlBuffer &outputBuffer, size_t unMaxResultSize )
{
	const char *pchProlog = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
	outputBuffer.Put( pchProlog, Q_strlen( pchProlog ) );

	outputBuffer.Put( "<!DOCTYPE ", Q_strlen( "<!DOCTYPE " ) );
	if ( m_pValues && m_pValues->GetName() )
		EmitXMLString( outputBuffer, m_pValues->GetName() );
	outputBuffer.PutChar('>');
	outputBuffer.PutChar('\n');

	if ( m_pValues )
		if( !CWebAPIValues::BEmitXMLRecursive( m_pValues, outputBuffer, 0, unMaxResultSize ) )
			return false;

	if ( !outputBuffer.IsValid() )
		return false;

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Emits Parameter Encoding formatted representation of response
//-----------------------------------------------------------------------------
bool CWebAPIResponse::BEmitParameterEncoding( CUtlBuffer &outputBuffer )
{
	if ( !m_pValues )
		return true;

	CWebAPIValues *pValue = m_pValues->GetFirstChild();
	while ( pValue != NULL )
	{
		outputBuffer.Put( pValue->GetName(), Q_strlen( pValue->GetName() ) );
		outputBuffer.Put( "=", 1 );

		CUtlString sValue;
		switch ( pValue->GetType() )
		{
		case k_EWebAPIValueType_Object:
			Assert( false );
			return false;	// no cursive values
		case k_EWebAPIValueType_NumericArray:
			Assert( false );
			return false;	// no arrays
		case k_EWebAPIValueType_BinaryBlob:
			Assert( false );
			return false;	// no binary

		case k_EWebAPIValueType_Int32:
			sValue = CNumStr( pValue->GetInt32Value() );
			break;
		case k_EWebAPIValueType_Int64:
			sValue = CNumStr( pValue->GetInt64Value() );
			break;
		case k_EWebAPIValueType_UInt32:
			sValue = CNumStr( pValue->GetUInt32Value() );
			break;
		case k_EWebAPIValueType_UInt64:
			sValue = CNumStr( pValue->GetUInt64Value() );
			break;
		case k_EWebAPIValueType_Double:
			sValue = CNumStr( pValue->GetDoubleValue() );
			break;
		case k_EWebAPIValueType_String:
			pValue->GetStringValue( sValue );
			break;
		case k_EWebAPIValueType_Bool:
			sValue = CNumStr( pValue->GetBoolValue() );
			break;
		}
		outputBuffer.Put( sValue, sValue.Length() );

		pValue = pValue->GetNextChild();
		if ( pValue )
			outputBuffer.Put( "&", 1 );
	}
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Resets the response to be empty
//-----------------------------------------------------------------------------
void CWebAPIResponse::Clear()
{
	if ( m_pValues )
		delete m_pValues;

	m_pValues = NULL;
}

//-----------------------------------------------------------------------------
// Purpose: Access the root value element in the response
//-----------------------------------------------------------------------------
CWebAPIValues *CWebAPIResponse::CreateRootValue( const char *pchName )
{
	if ( m_pValues )
	{
		AssertMsg( false, "CWwebAPIResponse::CreateRootValue called while root already existed." );
		Clear();
	}

	m_pValues = new CWebAPIValues( pchName );
	return m_pValues;
}


//----------------------------------------------------------------------------
// Purpose: Gets a singleton buffer pool for webapi values
//----------------------------------------------------------------------------
#ifdef GC
static GCConVar webapi_values_max_pool_size_mb( "webapi_values_max_pool_size_mb", "10", "Maximum size in bytes of the WebAPIValues buffer pool" );
static GCConVar webapi_values_init_buffer_size( "webapi_values_init_buffer_size", "65536", "Initial buffer size for buffers in the WebAPIValues buffer pool" );
/*static*/ CBufferPoolMT &CWebAPIValues::GetBufferPool()
{
	static CBufferPoolMT s_bufferPool( "WebAPIValues", webapi_values_max_pool_size_mb, webapi_values_init_buffer_size );
	return s_bufferPool;
}
#endif


//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CWebAPIValues::CWebAPIValues( CWebAPIValues *pParent, const char *pchName, EWebAPIValueType eValueType, const char *pchArrayElementNames )
{
	InitInternal( pParent, -1, eValueType, pchArrayElementNames );
	SetName( pchName );
}
	

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CWebAPIValues::CWebAPIValues( const char *pchName )
{
	InitInternal( NULL, -1, k_EWebAPIValueType_Object, NULL );
	SetName( pchName );
}


//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CWebAPIValues::CWebAPIValues( const char *pchName, const char *pchArrayElementNames )
{
	InitInternal( NULL, -1, k_EWebAPIValueType_NumericArray, pchArrayElementNames );
	SetName( pchName );
}


//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CWebAPIValues::CWebAPIValues( CWebAPIValues *pParent, int nNamePos, EWebAPIValueType eValueType, const char *pchArrayElementNames )
{
	InitInternal( pParent, nNamePos, eValueType, pchArrayElementNames );
}


//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
void CWebAPIValues::InitInternal( CWebAPIValues *pParent, int nNamePos, EWebAPIValueType eValueType, const char *pchArrayElementNames )
{
	if ( NULL == pParent )
	{
#ifdef GC
		m_pStringBuffer = GetBufferPool().GetBuffer();
#else
		m_pStringBuffer = new CUtlBuffer;
#endif
	}
	else
	{
		m_pStringBuffer = pParent->m_pStringBuffer;
	}

	m_nNamePos = nNamePos;

	m_eValueType = eValueType;
	if ( m_eValueType == k_EWebAPIValueType_NumericArray )
	{
		Assert( pchArrayElementNames );
		m_nArrayChildElementNamePos = m_pStringBuffer->TellPut();
		m_pStringBuffer->PutString( pchArrayElementNames );
	}

	m_pFirstChild = NULL;
	m_pLastChild = NULL;
	m_pNextPeer = NULL;
	m_pParent = pParent;
}


//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CWebAPIValues::~CWebAPIValues()
{
	ClearValue();

	CWebAPIValues *pChild = m_pFirstChild;
	while( pChild )
	{
		CWebAPIValues *pDelete = pChild;
		pChild = pChild->m_pNextPeer;
		delete pDelete;
	}
	m_pFirstChild = NULL;
	m_pNextPeer = NULL;

	if ( NULL == m_pParent )
	{
#ifdef GC
		GetBufferPool().ReturnBuffer( m_pStringBuffer );
#else
		delete m_pStringBuffer;
#endif
	}

	// This two ptrs are just for optimized traversal at runtime, deleting just
	// our first child and next peer will lead to the full tree being deleted correctly.
	m_pLastChild = NULL;
	m_pParent = NULL;
}


//-----------------------------------------------------------------------------
// Purpose: Sets the name of the values node
//-----------------------------------------------------------------------------
void CWebAPIValues::SetName( const char * pchName ) 
{
	if ( pchName == NULL )
	{
		AssertMsg( false, "CWebAPIValues constructed with NULL name, breaks some output serialization.  Shouldn't do this." );
		m_nNamePos = -1;
	}
	else
	{
		// Shouldn't use ', ", &, <, or > in names since they can't output well in XML.  : is no good since it implies namespacing in XML.
		// Assert about it so we don't end up with responses that are badly formed in XML output.
		int unLen = 0;
		while ( pchName[unLen] != 0 )
		{
			if ( pchName[unLen] == '\'' || pchName[unLen] == '"' || pchName[unLen] == '&'
				|| pchName[unLen] == '>' || pchName[unLen] == '>' || pchName[unLen] == ':' )
			{
				AssertMsg( false, "Shouldn't use any of '\"&<>: in CWebAPIValues node names, you used %s", pchName );
				break;
			}
			++unLen;
		}

		m_nNamePos = m_pStringBuffer->TellPut();
		m_pStringBuffer->PutString( pchName );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Assert that we don't have any child nodes, this is used when setting a 
// native type value.  We don't support having both our own value and children.  You 
// are either an array of more values, or you are a value yourself.
//-----------------------------------------------------------------------------
void CWebAPIValues::AssertNoChildren()
{
	AssertMsg( m_pFirstChild == NULL, "CWebAPIValues has child nodes, but you are trying to set a direct value for it.  Can't have both children and your own value." );
}

//-----------------------------------------------------------------------------
// Purpose:  Clears any existing value, freeing memory if needed
//-----------------------------------------------------------------------------
void CWebAPIValues::ClearValue()
{
	m_eValueType = k_EWebAPIValueType_Object;
}


//-----------------------------------------------------------------------------
// Purpose: Setter
//-----------------------------------------------------------------------------
void CWebAPIValues::SetStringValue( const char *pchValue )
{
	ClearValue();
	AssertNoChildren();
	m_eValueType = k_EWebAPIValueType_String;
	if ( pchValue == NULL )
	{
		m_nStrValuePos = -1;
	}
	else
	{
		m_nStrValuePos = m_pStringBuffer->TellPut();
		m_pStringBuffer->PutString( pchValue );
	}
}


//-----------------------------------------------------------------------------
// Purpose: Setter
//-----------------------------------------------------------------------------
void CWebAPIValues::SetInt32Value( int32 nValue )
{
	ClearValue();
	AssertNoChildren();
	m_eValueType = k_EWebAPIValueType_Int32;
	m_nValue = nValue;
}


//-----------------------------------------------------------------------------
// Purpose: Setter
//-----------------------------------------------------------------------------
void CWebAPIValues::SetUInt32Value( uint32 unValue )
{
	ClearValue();
	AssertNoChildren();
	m_eValueType = k_EWebAPIValueType_UInt32;
	m_unValue = unValue;
}


//-----------------------------------------------------------------------------
// Purpose: Setter
//-----------------------------------------------------------------------------
void CWebAPIValues::SetInt64Value ( int64 lValue )
{
	ClearValue();
	AssertNoChildren();
	m_eValueType = k_EWebAPIValueType_Int64;
	m_lValue = lValue;
}


//-----------------------------------------------------------------------------
// Purpose: Setter
//-----------------------------------------------------------------------------
void CWebAPIValues::SetUInt64Value( uint64 ulValue )
{
	ClearValue();
	AssertNoChildren();
	m_eValueType = k_EWebAPIValueType_UInt64;
	m_ulValue = ulValue;
}


//-----------------------------------------------------------------------------
// Purpose: Setter
//-----------------------------------------------------------------------------
void CWebAPIValues::SetDoubleValue( double flValue )
{
	ClearValue();
	AssertNoChildren();
	m_eValueType = k_EWebAPIValueType_Double;
	m_flValue = flValue;
}


//-----------------------------------------------------------------------------
// Purpose: Setter
//-----------------------------------------------------------------------------
void CWebAPIValues::SetBoolValue( bool bValue )
{
	ClearValue();
	AssertNoChildren();
	m_eValueType = k_EWebAPIValueType_Bool;
	m_bValue = bValue;
}


//-----------------------------------------------------------------------------
// Purpose: Setter
//-----------------------------------------------------------------------------
void CWebAPIValues::SetNullValue()
{
	ClearValue();
	AssertNoChildren();
	m_eValueType = k_EWebAPIValueType_Null;
}


//-----------------------------------------------------------------------------
// Purpose: Setter
//-----------------------------------------------------------------------------
void CWebAPIValues::SetBinaryValue( const uint8 *pValue, uint32 unBytes )
{
	ClearValue();
	AssertNoChildren();
	m_eValueType = k_EWebAPIValueType_BinaryBlob;
	if ( pValue == NULL || unBytes < 1 )
	{
		m_BinaryValue.m_nDataPos = 0;
		m_BinaryValue.m_unBytes = 0;
	}
	else
	{
		m_BinaryValue.m_unBytes = unBytes;
		m_BinaryValue.m_nDataPos = m_pStringBuffer->TellPut();
		m_pStringBuffer->Put( pValue, unBytes );
	}
}


//-----------------------------------------------------------------------------
// Purpose: Get the type currently held by the node
//-----------------------------------------------------------------------------
EWebAPIValueType CWebAPIValues::GetType() const
{
	return m_eValueType;
}


//-----------------------------------------------------------------------------
// Purpose: Get int32 value
//-----------------------------------------------------------------------------
int32 CWebAPIValues::GetInt32Value() const
{
	// we can read uint64 values this way too
	switch ( m_eValueType )
	{
	case k_EWebAPIValueType_Int32:
		return m_nValue;
	case k_EWebAPIValueType_UInt32: 	// because we can't tell the type of an int when we parse this node might have different type
	case k_EWebAPIValueType_UInt64:
	case k_EWebAPIValueType_String:
		{
			uint32 uiVal = GetUInt32Value();
			AssertMsg( uiVal < INT_MAX, "GetInt32Value called on node with %u, which is too big to fit in an Int32", uiVal );
			return (int32)uiVal;
		}
	}

	AssertMsg( false, "Shouldn't call CWebAPIValues::GetInt32Value unless value type is int32, uint32, uint64, or string. %d does not.", m_eValueType );
	return 0;
}


//-----------------------------------------------------------------------------
// Purpose: Get uint32 value
//-----------------------------------------------------------------------------
uint32 CWebAPIValues::GetUInt32Value() const
{
	// we can read uint64 values this way too
	switch ( m_eValueType )
	{
	case k_EWebAPIValueType_UInt32:
		return m_unValue;
	case k_EWebAPIValueType_UInt64:
	case k_EWebAPIValueType_String:
		{
			uint64 uiVal = GetUInt64Value();
			AssertMsg( uiVal <= UINT_MAX, "GetUInt32Value called on node with %llu, which is too big to fit in an UInt32", uiVal );
			return (uint32)uiVal;
		}
	}

	AssertMsg( false, "Shouldn't call CWebAPIValues::GetUInt32Value unless value type is uint32, uint64, or string. %d does not", m_eValueType );
	return 0;
}


//-----------------------------------------------------------------------------
// Purpose: Get int64 value
//-----------------------------------------------------------------------------
int64 CWebAPIValues::GetInt64Value() const
{
	// we can read int32 values this way too
	switch ( m_eValueType )
	{
	case k_EWebAPIValueType_Int32:
		return GetInt32Value();
	case k_EWebAPIValueType_Int64:
		return m_lValue;
	case k_EWebAPIValueType_String:
		if ( m_nStrValuePos < 0 )
		{
			return 0ull;
		}
		else
		{
#if defined(_PS3) || defined(POSIX)
			return strtoll( (const char *)m_pStringBuffer->Base() +  m_nStrValuePos, NULL, 10);
#else
			return _strtoi64( (const char *)m_pStringBuffer->Base() +  m_nStrValuePos, NULL, 10);
#endif
		}
	default:
		AssertMsg1( false, "Shouldn't call CWebAPIValues::GetInt64Value unless value type matches. %d does not", m_eValueType );
		return 0;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Get uint64 value
//-----------------------------------------------------------------------------
uint64 CWebAPIValues::GetUInt64Value() const
{
	// we can read uint32 values this way too
	switch ( m_eValueType )
	{
	case k_EWebAPIValueType_UInt32:
		return GetUInt32Value();
	case k_EWebAPIValueType_UInt64:
		return m_ulValue;
	case k_EWebAPIValueType_String:
		if ( m_nStrValuePos < 0 )
		{
			return 0ull;
		}
		else
		{
#if defined(_PS3) || defined(POSIX)
			return strtoull( (const char *)m_pStringBuffer->Base() +  m_nStrValuePos, NULL, 10);
#else
			return _strtoui64( (const char *)m_pStringBuffer->Base() +  m_nStrValuePos, NULL, 10);
#endif
		}
	default:
		AssertMsg1( false, "Shouldn't call CWebAPIValues::GetUInt64Value unless value type matches. %d does not", m_eValueType );
		return 0;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Get double value
//-----------------------------------------------------------------------------
double CWebAPIValues::GetDoubleValue() const
{
	switch ( m_eValueType )
	{
	case k_EWebAPIValueType_Int32:
		return (double)m_nValue;
	case k_EWebAPIValueType_UInt32:
		return (double)m_unValue;
	case k_EWebAPIValueType_Int64:
		return (double)m_lValue;
	case k_EWebAPIValueType_UInt64:
		return (double)m_ulValue;
	case k_EWebAPIValueType_Double:
		return m_flValue;
	default:
		AssertMsg1( false, "Shouldn't call CWebAPIValues::GetDoubleValue unless value type matches. %d does not", m_eValueType );
		return 0.0f;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Get bool value
//-----------------------------------------------------------------------------
bool CWebAPIValues::GetBoolValue() const
{
	if ( m_eValueType != k_EWebAPIValueType_Bool )
	{
		AssertMsg( false, "Shouldn't call CWebAPIValues::GetBoolValue unless value type matches" );
		return false;
	}

	return m_bValue;
}



//-----------------------------------------------------------------------------
// Purpose: Get string value
//-----------------------------------------------------------------------------
void CWebAPIValues::GetStringValue( CUtlString &stringOut ) const
{
	switch ( m_eValueType )
	{
	case k_EWebAPIValueType_String:
		if ( m_nStrValuePos < 0 )
		{
			stringOut.Clear();
		}
		else
		{
			stringOut = (const char *)m_pStringBuffer->Base() +  m_nStrValuePos;
		}

		return;
	case k_EWebAPIValueType_Int32:
		stringOut = CNumStr( m_nValue ).String();
		return;
	case k_EWebAPIValueType_Int64:
		stringOut = CNumStr( m_lValue ).String();
		return;
	case k_EWebAPIValueType_UInt32:
		stringOut = CNumStr( m_unValue ).String();
		return;
	case k_EWebAPIValueType_UInt64:
		stringOut = CNumStr( m_ulValue ).String();
		return;
	case k_EWebAPIValueType_Double:
		stringOut = CNumStr( m_flValue ).String();
		return;
	case k_EWebAPIValueType_Bool:
		stringOut = m_bValue ? "true" : "false";
		return;
	default:
		AssertMsg1( false, "CWebAPIValues::GetStringValue(), unable to convert data type %d to string", m_eValueType );
		stringOut = "";
		return;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Get binary blob value
//-----------------------------------------------------------------------------
void CWebAPIValues::GetBinaryValue( CUtlBuffer &bufferOut ) const
{
	if ( m_eValueType != k_EWebAPIValueType_BinaryBlob )
	{
		AssertMsg( false, "Shouldn't call CWebAPIValues::GetBinaryValue unless value type matches" );
		bufferOut.Clear();
		return;
	}

	bufferOut.Clear();
	bufferOut.EnsureCapacity( m_BinaryValue.m_unBytes );
	if ( m_BinaryValue.m_unBytes )
		bufferOut.Put( (byte*)m_pStringBuffer->Base() + m_BinaryValue.m_nDataPos, m_BinaryValue.m_unBytes );

	return;
}


//-----------------------------------------------------------------------------
// Purpose:  Create a  child array of this node.  Note that array nodes can only
// have un-named children, in XML the pchArrayElementNames value will be used
// as the element name for each of the children of the array, in JSON it will simply
// be a numerically indexed [] array.
//-----------------------------------------------------------------------------
CWebAPIValues * CWebAPIValues::CreateChildArray( const char *pchName, const char *pchArrayElementNames )
{
	return CreateChildInternal( pchName, k_EWebAPIValueType_NumericArray, pchArrayElementNames );
}


//-----------------------------------------------------------------------------
// Purpose:  Create a child of this node.  Note, it's possible to create multiple,
// children with the same name, but you really don't want to.  We'll assert about it
// in debug builds to detect, but not in release.  If you do create duplicates you'll
// have broken JSON output.
//-----------------------------------------------------------------------------
CWebAPIValues * CWebAPIValues::CreateChildObject( const char *pchName )
{
	return CreateChildInternal( pchName, k_EWebAPIValueType_Object );
}

//-----------------------------------------------------------------------------
// Purpose: Return an existing child object - otherwise create one and return that.
//-----------------------------------------------------------------------------
CWebAPIValues *CWebAPIValues::FindOrCreateChildObject( const char *pchName )
{
	CWebAPIValues *pChild = FindChild( pchName );
	if ( pChild )
	{
		return pChild;
	}

	return CreateChildObject( pchName );
}

//-----------------------------------------------------------------------------
// Purpose: Add a child object to the array, this should only be called on objects that are of the array type
//-----------------------------------------------------------------------------
CWebAPIValues * CWebAPIValues::AddChildObjectToArray()
{
	if ( m_eValueType != k_EWebAPIValueType_NumericArray )
	{
		AssertMsg( m_eValueType == k_EWebAPIValueType_NumericArray, "Can't add array elements to CWebAPIVAlues unless type is of numeric array." );
		return NULL;
	}

	// Use child element array name as name of all children of arrays
	return CreateChildInternal( NULL, k_EWebAPIValueType_Object );
}


//-----------------------------------------------------------------------------
// Purpose: Add a child array to the array, this should only be called on objects that are of the array type
//-----------------------------------------------------------------------------
CWebAPIValues * CWebAPIValues::AddChildArrayToArray( const char * pchArrayElementNames )
{
	if ( m_eValueType != k_EWebAPIValueType_NumericArray )
	{
		AssertMsg( m_eValueType == k_EWebAPIValueType_NumericArray, "Can't add array elements to CWebAPIVAlues unless type is of numeric array." );
		return NULL;
	}

	// Use child element array name as name of all children of arrays
	return CreateChildInternal( NULL, k_EWebAPIValueType_NumericArray, pchArrayElementNames );
}

//-----------------------------------------------------------------------------
// Purpose:  Internal helper for creating children
//-----------------------------------------------------------------------------
CWebAPIValues * CWebAPIValues::CreateChildInternal( const char *pchName, EWebAPIValueType eValueType, const char *pchArrayElementNames )
{
	// Shouldn't create children if you have a direct value.  You are either an object or array of children,
	// or a native value type.  Not both.

	AssertMsg( m_eValueType == k_EWebAPIValueType_Object || m_eValueType == k_EWebAPIValueType_NumericArray, "You are trying to create a child node of a CWebAPIValues object, but it has a direct value already.  Can't have children and a value." );
	if ( m_eValueType != k_EWebAPIValueType_Object && m_eValueType != k_EWebAPIValueType_NumericArray )
		ClearValue();

	// Shouldn't create named children if you are a numeric array
	CWebAPIValues *pNewNode;
	if  ( m_eValueType == k_EWebAPIValueType_NumericArray )
	{
		if ( pchName )
		{
			AssertMsg( false, "Can't create named child of CWebAPIValues object of type NumericArray.  Should call AddArrayElement instead of CreateChild*." );
		}
		// Force name to match what all items in the array should use
		pNewNode = new CWebAPIValues( this, m_nArrayChildElementNamePos, eValueType, pchArrayElementNames );
	}
	else
	{
		pNewNode = new CWebAPIValues( this, pchName, eValueType, pchArrayElementNames );
	}

	if ( eValueType == k_EWebAPIValueType_NumericArray )
	{
		Assert( pchArrayElementNames );
	}

	if ( !m_pFirstChild )
	{
		m_pLastChild = m_pFirstChild = pNewNode;
		return m_pFirstChild;
	}
	else
	{

		CWebAPIValues *pCurLastChild = m_pLastChild;

#ifdef _DEBUG
		// In debug, traverse all children so we can check for duplicate names, which will break JSON output!
		pCurLastChild = m_pFirstChild;
		if ( m_eValueType != k_EWebAPIValueType_NumericArray )
		{
			if ( Q_stricmp( pCurLastChild->GetName(), pchName ) == 0 )
			{
				AssertMsg( false, "Trying to create CWebAPIValues child with name %s that conflicts with existing child.  Breaks JSON output!", pchName );
			}
		}

		while ( pCurLastChild->m_pNextPeer )
		{
			pCurLastChild = pCurLastChild->m_pNextPeer;
			if ( m_eValueType != k_EWebAPIValueType_NumericArray )
			{
				if ( Q_stricmp( pCurLastChild->GetName(), pchName ) == 0 )
				{
					AssertMsg( false, "Trying to create CWebAPIValues child with name %s that conflicts with existing child.  Breaks JSON output!", pchName );
				}
			}
		}
		// Also, in debug assert last child ptr looks correct
		Assert( m_pLastChild == pCurLastChild );
#endif


		m_pLastChild = pCurLastChild->m_pNextPeer = pNewNode;
		return m_pLastChild;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Set a child node's string value
//-----------------------------------------------------------------------------
void CWebAPIValues::SetChildStringValue( const char *pchChildName, const char *pchValue )
{
	CreateChildObject( pchChildName )->SetStringValue( pchValue );
}


//-----------------------------------------------------------------------------
// Purpose: Set a child node's int32 value
//-----------------------------------------------------------------------------
void CWebAPIValues::SetChildInt32Value( const char *pchChildName, int32 nValue )
{
	CreateChildObject( pchChildName )->SetInt32Value( nValue );
}


//-----------------------------------------------------------------------------
// Purpose: Set a child node's uint32 value
//-----------------------------------------------------------------------------
void CWebAPIValues::SetChildUInt32Value( const char *pchChildName, uint32 unValue )
{
	CreateChildObject( pchChildName )->SetUInt32Value( unValue );
}


//-----------------------------------------------------------------------------
// Purpose: Set a child node's int64 value
//-----------------------------------------------------------------------------
void CWebAPIValues::SetChildInt64Value ( const char *pchChildName, int64 lValue )
{
	CreateChildObject( pchChildName )->SetInt64Value( lValue );
}


//-----------------------------------------------------------------------------
// Purpose: Set a child node's uint64 value
//-----------------------------------------------------------------------------
void CWebAPIValues::SetChildUInt64Value( const char *pchChildName, uint64 ulValue )
{
	CreateChildObject( pchChildName )->SetUInt64Value( ulValue );
}


//-----------------------------------------------------------------------------
// Purpose: Set a child node's double value
//-----------------------------------------------------------------------------
void CWebAPIValues::SetChildDoubleValue( const char *pchChildName, double flValue )
{
	CreateChildObject( pchChildName )->SetDoubleValue( flValue );
}


//-----------------------------------------------------------------------------
// Purpose: Set a child node's binary blob value
//-----------------------------------------------------------------------------
void CWebAPIValues::SetChildBinaryValue( const char *pchChildName, const uint8 *pValue, uint32 unBytes )
{
	CreateChildObject( pchChildName )->SetBinaryValue( pValue, unBytes );
}


//-----------------------------------------------------------------------------
// Purpose: Set a child node's boolean value
//-----------------------------------------------------------------------------
void CWebAPIValues::SetChildBoolValue( const char *pchChildName, bool bValue )
{
	CreateChildObject( pchChildName )->SetBoolValue( bValue );
}


//-----------------------------------------------------------------------------
// Purpose: Set a child node's boolean value
//-----------------------------------------------------------------------------
void CWebAPIValues::SetChildNullValue( const char *pchChildName )
{
	CreateChildObject( pchChildName )->SetNullValue();
}


//-----------------------------------------------------------------------------
// Purpose: Get a child node's int32 value or return the default if the node doesn't exist
//-----------------------------------------------------------------------------
int32 CWebAPIValues::GetChildInt32Value( const char *pchChildName, int32 nDefault ) const
{
	const CWebAPIValues *pChild = FindChild( pchChildName );
	if( pChild )
		return pChild->GetInt32Value();
	else
		return nDefault;
}


//-----------------------------------------------------------------------------
// Purpose: Get a child node's uint32 value or return the default if the node doesn't exist
//-----------------------------------------------------------------------------
uint32 CWebAPIValues::GetChildUInt32Value( const char *pchChildName, uint32 unDefault ) const
{
	const CWebAPIValues *pChild = FindChild( pchChildName );
	if( pChild )
		return pChild->GetUInt32Value();
	else
		return unDefault;
}



//-----------------------------------------------------------------------------
// Purpose: Get a child node's int64 value or return the default if the node doesn't exist
//-----------------------------------------------------------------------------
int64 CWebAPIValues::GetChildInt64Value( const char *pchChildName, int64 lDefault ) const
{
	const CWebAPIValues *pChild = FindChild( pchChildName );
	if( pChild )
		return pChild->GetInt64Value();
	else
		return lDefault;
}



//-----------------------------------------------------------------------------
// Purpose: Get a child node's uint64 value or return the default if the node doesn't exist
//-----------------------------------------------------------------------------
uint64 CWebAPIValues::GetChildUInt64Value( const char *pchChildName, uint64 ulDefault ) const
{
	const CWebAPIValues *pChild = FindChild( pchChildName );
	if( pChild )
		return pChild->GetUInt64Value();
	else
		return ulDefault;
}



//-----------------------------------------------------------------------------
// Purpose: Get a child node's double value or return the default if the node doesn't exist
//-----------------------------------------------------------------------------
double CWebAPIValues::GetChildDoubleValue( const char *pchChildName, double flDefault ) const
{
	const CWebAPIValues *pChild = FindChild( pchChildName );
	if( pChild )
		return pChild->GetDoubleValue();
	else
		return flDefault;
}



//-----------------------------------------------------------------------------
// Purpose: Get a child node's string value or return the default if the node doesn't exist
//-----------------------------------------------------------------------------
void CWebAPIValues::GetChildStringValue( CUtlString &stringOut, const char *pchChildName, const char *pchDefault ) const
{
	const CWebAPIValues *pChild = FindChild( pchChildName );
	if( pChild )
	{
		pChild->GetStringValue( stringOut );
	}
	else
	{
		stringOut = pchDefault;
	}
}



//-----------------------------------------------------------------------------
// Purpose: Get a child node's binary blob value (returns false if the child wasn't found)
//-----------------------------------------------------------------------------
bool CWebAPIValues::BGetChildBinaryValue( CUtlBuffer &bufferOut, const char *pchChildName ) const
{
	const CWebAPIValues *pChild = FindChild( pchChildName );
	if( pChild )
	{
		pChild->GetBinaryValue( bufferOut );
		return true;
	}
	else
	{
		return false;
	}
}


//-----------------------------------------------------------------------------
// Purpose: Get a child node's binary blob value (returns false if the child wasn't found)
//-----------------------------------------------------------------------------
bool CWebAPIValues::IsChildNullValue( const char *pchChildName ) const
{
	const CWebAPIValues *pChild = FindChild( pchChildName );
	if( pChild )
		return pChild->IsNullValue();
	else
		return false;
}



//-----------------------------------------------------------------------------
// Purpose: Get a child node's bool value or return the default if the node doesn't exist
//-----------------------------------------------------------------------------
bool CWebAPIValues::GetChildBoolValue( const char *pchChildName, bool bDefault ) const
{
	const CWebAPIValues *pChild = FindChild( pchChildName );
	if( pChild )
		return pChild->GetBoolValue();
	else
		return bDefault;
}


//-----------------------------------------------------------------------------
// Purpose:  Find first matching child by name, O(N) on number of children, this class isn't designed for searching
//-----------------------------------------------------------------------------
CWebAPIValues * CWebAPIValues::FindChild( const char *pchName )
{
	CWebAPIValues *pCurLastChild = m_pFirstChild;
	
	while ( pCurLastChild )
	{
		if ( Q_stricmp( pCurLastChild->GetName(), pchName ) == 0 )
			return pCurLastChild;

		pCurLastChild = pCurLastChild->m_pNextPeer;
	}

	return NULL;
}


//-----------------------------------------------------------------------------
// Purpose:  Get the first child of this node
//-----------------------------------------------------------------------------
CWebAPIValues * CWebAPIValues::GetFirstChild()
{
	return m_pFirstChild;
}


//-----------------------------------------------------------------------------
// Purpose:  Call this on the returned value from GetFirstChild() or a previous GetNextChild() call to
// proceed to the next child of the parent GetFirstChild() was originally called on.
//-----------------------------------------------------------------------------
CWebAPIValues * CWebAPIValues::GetNextChild()
{
	return m_pNextPeer;
}


//-----------------------------------------------------------------------------
// Purpose:  Call this on any node to return the parent of that node
//-----------------------------------------------------------------------------
CWebAPIValues * CWebAPIValues::GetParent()
{
	return m_pParent;
}

//-----------------------------------------------------------------------------
// Purpose:  Deletes a child node by name
//-----------------------------------------------------------------------------
void CWebAPIValues::DeleteChild( const char *pchName )
{
	CWebAPIValues *pChild = NULL;	// child we're examining, could be NULL at exit if we don't find it
	CWebAPIValues *pPrev = NULL;	// previous sibling, or NULL

	for ( pChild = m_pFirstChild; pChild != NULL; pPrev = pChild, pChild = pChild->m_pNextPeer )
	{
		if ( !Q_stricmp( pChild->GetName(), pchName ) )
		{
			if ( pChild == m_pFirstChild )
			{
				// first child, fixup parent's pointer to take pChild out
				Assert( pPrev == NULL );
				m_pFirstChild = pChild->m_pNextPeer;
			}
			else
			{
				// not first child, fixup sibling's pointer to take pChild out
				Assert( pPrev != NULL );
				pPrev->m_pNextPeer = pChild->m_pNextPeer;
			}

			// clean up next ptr on child so we don't double free
			pChild->m_pNextPeer = NULL;

			// fixup last child pointer if pChild is the last child
			if ( pChild == m_pLastChild )
			{
				m_pLastChild = pPrev;
			}

			break;
		}
	}

	// debug only, check that there is no child by the specified name any more and that it was excised OK
	AssertMsg( FindChild( pchName ) == NULL, "cwebapivalues deleted child is still lurking" );
	Assert( pChild == NULL || pChild->m_pNextPeer == NULL );

	// now take the removed child out of the heap
	delete pChild;
}

//-----------------------------------------------------------------------------
// Purpose: Emits JSON formatted representation of values
//
// when bEmitOldStyleArrays is true, arrays are emitted as a child of a singlet object, and any empty arrays
// will be emitted as having a single null member.
//
// when bEmitOldStyleArrays is false, arrays are emitted bare (no subobject) and empty arrays
// are emitted empty.
//-----------------------------------------------------------------------------
bool CWebAPIValues::BEmitJSONRecursive( const CWebAPIValues *pCurrent, CUtlBuffer &outputBuffer, int nTabLevel, size_t unMaxResultSize, bool bEmitOldStyleArrays )
{
	bool bSuccess = true;


	while( pCurrent )
	{
		// don't let the buffer grow until it consumes all available memory
		if( unMaxResultSize && (size_t)outputBuffer.TellMaxPut() > unMaxResultSize )
		{
			return false;
		}

		// Can't emit nameless nodes in JSON.  Nodes should always have a name.
		Assert( pCurrent->GetName() );
		if ( pCurrent->GetName() )
		{
			for( int i=0; i < nTabLevel; ++i )
				outputBuffer.PutChar ( '\t' );

			if ( !pCurrent->m_pParent || pCurrent->m_pParent->GetType() != k_EWebAPIValueType_NumericArray )
			{
				EmitJSONString( outputBuffer, pCurrent->GetName() );
				outputBuffer.PutChar( ':' );
				outputBuffer.PutChar( ' ' );
			}

			if ( pCurrent->m_eValueType == k_EWebAPIValueType_Object || pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray )
			{

				if( bEmitOldStyleArrays || pCurrent->m_eValueType == k_EWebAPIValueType_Object )
				{
					outputBuffer.PutChar( '{' );
					outputBuffer.PutChar( '\n' );
				}

				if ( pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray )
				{
					if( bEmitOldStyleArrays )
					{
						for( int i=0; i < nTabLevel+1; ++i )
							outputBuffer.PutChar ( '\t' );

						EmitJSONString( outputBuffer, (const char *)pCurrent->m_pStringBuffer->Base() + pCurrent->m_nArrayChildElementNamePos );
						outputBuffer.PutChar( ':' );
						outputBuffer.PutChar( ' ' );
					}

					outputBuffer.PutChar( '[' );
					outputBuffer.PutChar( '\n' );
				}

				// First add any children
				if ( pCurrent->m_pFirstChild )
				{
					int nChildTabLevel = nTabLevel+1;
					if ( pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray && bEmitOldStyleArrays )
						++nChildTabLevel;

					bSuccess = BEmitJSONRecursive( pCurrent->m_pFirstChild, outputBuffer, nChildTabLevel, unMaxResultSize, bEmitOldStyleArrays );
					if ( !bSuccess )
						return false;
				}
				else if ( bEmitOldStyleArrays )
				{
					for( int i=0; i < nTabLevel + 1; ++i )
						outputBuffer.PutChar ( '\t' );

					outputBuffer.Put( "null", 4 );
				}

				outputBuffer.PutChar( '\n' );

				for( int i=0; i < nTabLevel; ++i )
					outputBuffer.PutChar ( '\t' );

				if ( pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray )
				{
					if( bEmitOldStyleArrays )
						outputBuffer.PutChar( '\t' );
					outputBuffer.PutChar( ']' );
					outputBuffer.PutChar( '\n' );

					for( int i=0; i < nTabLevel; ++i )
						outputBuffer.PutChar ( '\t' );
				}

				if( bEmitOldStyleArrays || pCurrent->m_eValueType == k_EWebAPIValueType_Object )
				{
					outputBuffer.PutChar( '}' );
				}
			}
			else
			{

				switch ( pCurrent->m_eValueType )
				{
				case k_EWebAPIValueType_Int32:
					{
						CNumStr numStr( pCurrent->m_nValue );
						outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
					}
					break;
				case k_EWebAPIValueType_Int64:
					{
						CNumStr numStr( pCurrent->m_lValue );
						outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
					}
					break;
				case k_EWebAPIValueType_UInt32:
					{
						CNumStr numStr( pCurrent->m_unValue );
						outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
					}
					break;
				case k_EWebAPIValueType_UInt64:
					{
						CNumStr numStr( pCurrent->m_ulValue );
						outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
					}
					break;
				case k_EWebAPIValueType_Double:
					{
						CNumStr numStr( pCurrent->m_flValue );
						outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
					}
					break;

				case k_EWebAPIValueType_String:
					{
						if ( pCurrent->m_nStrValuePos < 0 )
							outputBuffer.Put( "null", 4 );
						else
							EmitJSONString( outputBuffer, (const char *)pCurrent->m_pStringBuffer->Base() + pCurrent->m_nStrValuePos );
					}
					break;

				case k_EWebAPIValueType_Bool:
					{
						if ( !pCurrent->m_bValue )
							outputBuffer.Put( "false", 5 );
						else
							outputBuffer.Put( "true", 4 );
					}
					break;

				case k_EWebAPIValueType_Null:
					{
						outputBuffer.Put( "null", 4 );
					}
					break;

				case k_EWebAPIValueType_BinaryBlob:
					{
						if ( pCurrent->m_BinaryValue.m_unBytes == 0 )
							outputBuffer.Put( "null", 4 );
						else
						{
							CUtlMemory<char> buffEncoded;
							DbgVerify( Base64EncodeIntoUTLMemory( (const uint8 *)pCurrent->m_pStringBuffer->Base() + pCurrent->m_BinaryValue.m_nDataPos, pCurrent->m_BinaryValue.m_unBytes, buffEncoded ) );
							EmitJSONString( outputBuffer, buffEncoded.Base() );
						}
					}
					break;
				default:
					break;
				}
			}
		}

		// Now, check for any peers
		if ( bSuccess && pCurrent->m_pNextPeer )
		{
			outputBuffer.PutChar( ',' );
			outputBuffer.PutChar( '\n' );

			pCurrent = pCurrent->m_pNextPeer;
		}
		else
		{
			// We're done, or failing early
			pCurrent = NULL;
		}
	}

	return bSuccess && outputBuffer.IsValid();
}


//-----------------------------------------------------------------------------
// Purpose: Emits KeyValues .vdf style formatted representation of values
//-----------------------------------------------------------------------------
bool CWebAPIValues::BEmitVDFRecursive( const CWebAPIValues *pCurrent, CUtlBuffer &outputBuffer, int nTabLevel, uint32 nArrayElement, size_t unMaxResultSize, bool bIncludeArrayElementName  )
{
	bool bSuccess = true;

	// We can have lots of peers, so this is an optimization to avoid tail recursion and resulting stack-overflows!	
	while( pCurrent )
	{
		// don't let the buffer grow until it consumes all available memory
		if( unMaxResultSize && (size_t)outputBuffer.TellMaxPut() > unMaxResultSize )
		{
			return false;
		}
		if ( pCurrent->GetName() )
		{
			for( int i=0; i < nTabLevel; ++i )
				outputBuffer.PutChar ( '\t' );

			// Open node, special naming when inside arrays
			if ( pCurrent->m_pParent && pCurrent->m_pParent->GetType() == k_EWebAPIValueType_NumericArray )
			{
				CNumStr numStr( nArrayElement );
				numStr.AddQuotes();
				outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
			}
			else
			{
				EmitVDFString( outputBuffer, pCurrent->GetName() );
			}

			if ( pCurrent->m_eValueType == k_EWebAPIValueType_Object || pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray )
			{
				outputBuffer.PutChar( '\n' );

				for( int i=0; i < nTabLevel; ++i )
					outputBuffer.PutChar ( '\t' );

				outputBuffer.PutChar( '{' );
				outputBuffer.PutChar( '\n' );

				if ( pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray && bIncludeArrayElementName )
				{
					for( int i=0; i < nTabLevel+1; ++i )
						outputBuffer.PutChar ( '\t' );

					EmitVDFString( outputBuffer, (const char *)pCurrent->m_pStringBuffer->Base() + pCurrent->m_nArrayChildElementNamePos );
					outputBuffer.PutChar( '\n' );

					for( int i=0; i < nTabLevel+1; ++i )
						outputBuffer.PutChar ( '\t' );

					outputBuffer.PutChar( '{' );
					outputBuffer.PutChar( '\n' );
				}

				if ( pCurrent->m_pFirstChild )
				{
					int nChildTabLevel = nTabLevel+1;
					if ( pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray && bIncludeArrayElementName )
						++nChildTabLevel;

					bSuccess = BEmitVDFRecursive( pCurrent->m_pFirstChild, outputBuffer, nChildTabLevel, 0, unMaxResultSize, bIncludeArrayElementName );
					if ( !bSuccess )
						return false;
				}

				if ( pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray && bIncludeArrayElementName  )
				{
					outputBuffer.PutChar( '\n' );

					for( int i=0; i < nTabLevel+1; ++i )
						outputBuffer.PutChar ( '\t' );

					outputBuffer.PutChar( '}' );
				}

				outputBuffer.PutChar( '\n' );

				for( int i=0; i < nTabLevel; ++i )
					outputBuffer.PutChar ( '\t' );

				outputBuffer.PutChar( '}' );
			}
			else
			{
				outputBuffer.PutChar( '\t' );

				switch ( pCurrent->m_eValueType )
				{
				case k_EWebAPIValueType_Int32:
					{
						CNumStr numStr( pCurrent->m_nValue );
						numStr.AddQuotes();
						outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
					}
					break;
				case k_EWebAPIValueType_Int64:
					{
						CNumStr numStr( pCurrent->m_lValue );
						numStr.AddQuotes();
						outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
					}
					break;
				case k_EWebAPIValueType_UInt32:
					{
						CNumStr numStr( pCurrent->m_unValue );
						numStr.AddQuotes();
						outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
					}
					break;
				case k_EWebAPIValueType_UInt64:
					{
						CNumStr numStr( pCurrent->m_ulValue );
						numStr.AddQuotes();
						outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
					}
					break;
				case k_EWebAPIValueType_Double:
					{
						CNumStr numStr( pCurrent->m_flValue );
						numStr.AddQuotes();
						outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
					}
					break;

				case k_EWebAPIValueType_String:
					{
						EmitVDFString( outputBuffer, pCurrent->m_nStrValuePos >= 0 ? ( (const char *)pCurrent->m_pStringBuffer->Base() + pCurrent->m_nStrValuePos ) : NULL );
					}
					break;

				case k_EWebAPIValueType_Bool:
					{
						if ( !pCurrent->m_bValue )
							outputBuffer.Put( "\"0\"", 3 );
						else
							outputBuffer.Put( "\"1\"", 3 );
					}
					break;

				case k_EWebAPIValueType_Null:
					{
						outputBuffer.Put ("\"\"", 2 );
					}
					break;

				case k_EWebAPIValueType_BinaryBlob:
					{
						if ( pCurrent->m_BinaryValue.m_unBytes == 0 )
							outputBuffer.Put( "\"\"", 2 );
						else
						{
							CUtlMemory<char> buffEncoded;
							DbgVerify( Base64EncodeIntoUTLMemory( (const uint8 *)pCurrent->m_pStringBuffer->Base() + pCurrent->m_BinaryValue.m_nDataPos, pCurrent->m_BinaryValue.m_unBytes, buffEncoded ) );
							EmitVDFString( outputBuffer, buffEncoded.Base() );
						}
					}
					break;
				default:
					break;
				}
			}
		}

		// Now, check for any peers
		if ( bSuccess && pCurrent->m_pNextPeer )
		{
			outputBuffer.PutChar( '\n' );
			pCurrent = pCurrent->m_pNextPeer;
			nArrayElement += 1;
		}
		else
		{
			// We're done, or failing early
			pCurrent = NULL;
		}
	}

	return bSuccess && outputBuffer.IsValid();
}


//-----------------------------------------------------------------------------
// Purpose: Emits XML formatted representation of values
//-----------------------------------------------------------------------------
bool CWebAPIValues::BEmitXMLRecursive( const CWebAPIValues *pCurrent, CUtlBuffer &outputBuffer, int nTabLevel, size_t unMaxResultSize )
{
	bool bSuccess = true;

	while( pCurrent )
	{

		// don't let the buffer grow until it consumes all available memory
		if( unMaxResultSize && (size_t)outputBuffer.TellMaxPut() > unMaxResultSize )
		{
			return false;
		}

		// Can't emit nameless nodes in XML.  Nodes should always have a name.
		Assert( pCurrent->GetName() );
		if ( pCurrent->GetName() )
		{
			for( int i=0; i < nTabLevel; ++i )
				outputBuffer.PutChar ( '\t' );

			// Open node
			outputBuffer.PutChar( '<' );
			EmitXMLString( outputBuffer, pCurrent->GetName() );
			outputBuffer.PutChar( '>' );

			if ( pCurrent->m_eValueType == k_EWebAPIValueType_Object || pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray )
			{
				// First add any children
				if ( pCurrent->m_pFirstChild )
				{
					outputBuffer.PutChar( '\n' );

					bSuccess = BEmitXMLRecursive( pCurrent->m_pFirstChild, outputBuffer, nTabLevel+1, unMaxResultSize );
					if ( !bSuccess )
						return false;

					outputBuffer.PutChar( '\n' );

					// Return to correct tab level, for when we close element below
					for( int i=0; i < nTabLevel; ++i )
						outputBuffer.PutChar ( '\t' );
				}
			}
			else
			{

				switch ( pCurrent->m_eValueType )
				{
				case k_EWebAPIValueType_Int32:
					{
						CNumStr numStr( pCurrent->m_nValue );
						outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
					}
					break;
				case k_EWebAPIValueType_Int64:
					{
						CNumStr numStr( pCurrent->m_lValue );
						outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
					}
					break;
				case k_EWebAPIValueType_UInt32:
					{
						CNumStr numStr( pCurrent->m_unValue );
						outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
					}
					break;
				case k_EWebAPIValueType_UInt64:
					{
						CNumStr numStr( pCurrent->m_ulValue );
						outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
					}
					break;
				case k_EWebAPIValueType_Double:
					{
						CNumStr numStr( pCurrent->m_flValue );
						outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
					}
					break;

				case k_EWebAPIValueType_String:
					{
						if ( pCurrent->m_nStrValuePos < 0 )
							outputBuffer.Put( "null", 4 );
						else
							EmitXMLString( outputBuffer, (const char *)pCurrent->m_pStringBuffer->Base() + pCurrent->m_nStrValuePos );
					}
					break;

				case k_EWebAPIValueType_Bool:
					{
						if ( !pCurrent->m_bValue )
							outputBuffer.Put( "false", 5 );
						else
							outputBuffer.Put( "true", 4 );
					}
					break;

				case k_EWebAPIValueType_Null:
					{
						outputBuffer.Put ("null", 4 );
					}
					break;

				case k_EWebAPIValueType_BinaryBlob:
					{
						if ( pCurrent->m_BinaryValue.m_unBytes == 0 )
							outputBuffer.Put( "null", 4 );
						else
						{
							CUtlMemory<char> buffEncoded;
							DbgVerify( Base64EncodeIntoUTLMemory( (const uint8 *)pCurrent->m_pStringBuffer->Base() + pCurrent->m_BinaryValue.m_nDataPos, pCurrent->m_BinaryValue.m_unBytes, buffEncoded ) );
							EmitXMLString( outputBuffer, buffEncoded.Base() );
						}
					}
					break;
				default:
					break;
				}
			}
		}

		// Close element
		outputBuffer.PutChar( '<' );
		outputBuffer.PutChar( '/' );
		EmitXMLString( outputBuffer, pCurrent->GetName() );
		outputBuffer.PutChar( '>' );

		// Now, check for any peers
		if ( bSuccess && pCurrent->m_pNextPeer )
		{
			outputBuffer.PutChar( '\n' );
			pCurrent = pCurrent->m_pNextPeer;
		}
		else
		{
			// We're done, or failing early
			pCurrent = NULL;
		}
	}

	return bSuccess && outputBuffer.IsValid();
}


struct JSONParserContext_t
{
	CWebAPIValues *m_pCurrentNode;
	CWebAPIValues *m_pRootNode;
	CUtlString m_sChildName;
	bool m_bIsKey;
};

static int JSONParserCallback(void* void_ctx, int type, const JSON_value* value)
{
	JSONParserContext_t *ctx = (JSONParserContext_t *)void_ctx;
	CWebAPIValues *pCreatedNode = NULL;
	switch(type) 
	{
		case JSON_T_ARRAY_BEGIN:
			// handle the root node
			if( !ctx->m_pRootNode )
			{
				Assert( !ctx->m_pCurrentNode );
				ctx->m_pRootNode = ctx->m_pCurrentNode = new CWebAPIValues( ctx->m_sChildName, "e" );
				break;
			}

			if( ctx->m_pCurrentNode->IsArray() )
			{
				Assert( ctx->m_sChildName.IsEmpty() );
				ctx->m_pCurrentNode = ctx->m_pCurrentNode->AddChildArrayToArray( "e" );
			}
			else
			{
				Assert( !ctx->m_sChildName.IsEmpty() );
				ctx->m_pCurrentNode = ctx->m_pCurrentNode->CreateChildArray( ctx->m_sChildName, "e" );
				ctx->m_sChildName.Clear();
			}
			break;

		case JSON_T_ARRAY_END:
			ctx->m_pCurrentNode = ctx->m_pCurrentNode->GetParent();
			break;

		case JSON_T_OBJECT_BEGIN:
			// this might be the start of the root object itself
			if( !ctx->m_pRootNode )
			{
				ctx->m_pRootNode = ctx->m_pCurrentNode = new CWebAPIValues( ctx->m_sChildName );
				break;
			}

			if( ctx->m_pCurrentNode->IsArray() )
			{
				Assert( ctx->m_sChildName.IsEmpty() );
				ctx->m_pCurrentNode = ctx->m_pCurrentNode->AddChildObjectToArray();
			}
			else
			{
				Assert( !ctx->m_sChildName.IsEmpty() );
				ctx->m_pCurrentNode = ctx->m_pCurrentNode->CreateChildObject( ctx->m_sChildName );
				ctx->m_sChildName.Clear();
			}
			break;

		case JSON_T_OBJECT_END:
			ctx->m_pCurrentNode = ctx->m_pCurrentNode->GetParent();
			break;

		case JSON_T_KEY:
			ctx->m_sChildName = value->vu.str.value;
			break;   

		case JSON_T_INTEGER:
		case JSON_T_FLOAT:
		case JSON_T_NULL:
		case JSON_T_TRUE:
		case JSON_T_FALSE:
		case JSON_T_STRING:
			// create the new node for this value
			if( ctx->m_pCurrentNode->IsArray() )
			{
				pCreatedNode = ctx->m_pCurrentNode->AddChildObjectToArray();
			}
			else
			{
				pCreatedNode = ctx->m_pCurrentNode->CreateChildObject( ctx->m_sChildName );
				ctx->m_sChildName.Clear();
			}

			// set the actual value
			switch( type )
			{
			case JSON_T_INTEGER:

				// try to figure out what type to use
				if( value->vu.integer_value >= 0 )
				{
					// unsigned
					if( value->vu.integer_value <= UINT_MAX )
					{
						pCreatedNode->SetUInt32Value( (uint32)value->vu.integer_value );
					}
					else
					{
						pCreatedNode->SetUInt64Value( (uint64)value->vu.integer_value );
					}
				}
				else
				{
					// signed
					if( value->vu.integer_value >= INT_MIN )
					{
						pCreatedNode->SetInt32Value( (int32)value->vu.integer_value );
					}
					else
					{
						pCreatedNode->SetInt64Value( value->vu.integer_value );
					}
				}
				break;
	
			case JSON_T_FLOAT:
				pCreatedNode->SetDoubleValue( value->vu.float_value );
				break;

			case JSON_T_NULL:
				pCreatedNode->SetNullValue();
				break;

			case JSON_T_TRUE:
				pCreatedNode->SetBoolValue( true );
				break;

			case JSON_T_FALSE:
				pCreatedNode->SetBoolValue( false );
				break;

			case JSON_T_STRING:
				pCreatedNode->SetStringValue( value->vu.str.value );
				break;

			}
			break;

		default:
			Assert( false );
			break;
	}

	return 1;
}


// parses JSON into a tree of CWebAPIValues nodes with this as the root
CWebAPIValues *CWebAPIValues::ParseJSON( CUtlBuffer &inputBuffer )
{
	//
	// if there's nothing to parse, just early out
	inputBuffer.EatWhiteSpace();
	if( inputBuffer.GetBytesRemaining() == 0 )
		return NULL;

	// if the first character is the start of a string,
	// wrap the whole thing in an object so we can parse it.
	// We'll unwrap it at the end
	char cFirst = *(char *)inputBuffer.PeekGet();
	bool bWrapContent =  cFirst == '\"';

	JSON_config config;

	struct JSON_parser_struct* jc = NULL;

	init_JSON_config(&config);

	JSONParserContext_t context;
	context.m_pCurrentNode = NULL;
	context.m_pRootNode = NULL;
	context.m_bIsKey = false;

	config.depth                  = 19;
	config.callback               = &JSONParserCallback;
	config.allow_comments         = 1;
	config.handle_floats_manually = 0;
	config.callback_ctx = &context;
	config.malloc = malloc;
	config.free = free;

	jc = new_JSON_parser(&config);

	bool bSuccess = true;
	if( bWrapContent )
		JSON_parser_char( jc, '{' );
	while( inputBuffer.GetBytesRemaining( ) > 0 )
	{
		if( !JSON_parser_char( jc, (unsigned char)inputBuffer.GetChar() ) )
		{
			bSuccess = false;
			break;
		}
	}

	if( bWrapContent )
	{
		JSON_parser_char( jc, '}' );
	}

	if( bSuccess )
	{
		if (!JSON_parser_done(jc)) 
		{
			bSuccess = false;
		}
	}

	delete_JSON_parser(jc);

	// unwrap the root node
	if( bWrapContent && bSuccess )
	{
		CWebAPIValues *pWrapRoot = context.m_pRootNode;
		if( !pWrapRoot )
		{
			bSuccess = false;
		}
		else
		{
			CWebAPIValues *pRealRoot = pWrapRoot->GetFirstChild();
			if( !pRealRoot )
			{
				bSuccess = false;
			}
			else
			{
				if( pRealRoot->GetNextChild() )
				{
					bSuccess = false;
				}
				else
				{
					pWrapRoot->m_pFirstChild = NULL;
					pRealRoot->m_pParent = NULL;
					context.m_pRootNode = pRealRoot;

					delete pWrapRoot;
				}
			}
		}
	}

	if( bSuccess )
	{
		return context.m_pRootNode;
	}
	else
	{
		delete context.m_pRootNode;
		return NULL;
	}
}


CWebAPIValues *CWebAPIValues::ParseJSON( const char *pchJSONString )
{
	CUtlBuffer bufJSON( pchJSONString, Q_strlen( pchJSONString), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
	return ParseJSON( bufJSON );
}


void CWebAPIValues::CopyFrom( const CWebAPIValues *pSource )
{
	switch( pSource->GetType() )
	{
		case k_EWebAPIValueType_Int32:
			SetInt32Value( pSource->GetInt32Value() );
			break;
		case k_EWebAPIValueType_Int64:
			SetInt64Value( pSource->GetInt64Value() );
			break;
		case k_EWebAPIValueType_UInt32:
			SetUInt32Value( pSource->GetUInt32Value() );
			break;
		case k_EWebAPIValueType_UInt64:
			SetUInt64Value( pSource->GetUInt64Value() );
			break;
		case k_EWebAPIValueType_Double:
			SetDoubleValue( pSource->GetDoubleValue() );
			break;

		case k_EWebAPIValueType_String:
			{
				CUtlString sValue;
				pSource->GetStringValue( sValue );
				SetStringValue( sValue );
			}
			break;

		case k_EWebAPIValueType_Bool:
			SetBoolValue( pSource->GetBoolValue() );
			break;

		case k_EWebAPIValueType_Null:
			SetNullValue( );
			break;

		case k_EWebAPIValueType_BinaryBlob:
			{
				CUtlBuffer bufValue;
				pSource->GetBinaryValue( bufValue );
				SetBinaryValue( (uint8 *)bufValue.Base(), bufValue.TellMaxPut() );
			}
			break;
			
		case k_EWebAPIValueType_Object:
			{
				ClearValue();
				m_eValueType = k_EWebAPIValueType_Object;

				const CWebAPIValues *pSourceChild = pSource->GetFirstChild();
				while( pSourceChild )
				{
					CWebAPIValues *pChild;
					if( pSourceChild->IsArray() )
						pChild = CreateChildArray( pSourceChild->GetName(), pSourceChild->GetElementName() );
					else
						pChild = CreateChildObject( pSourceChild->GetName() );

					pChild->CopyFrom( pSourceChild );
					pSourceChild = pSourceChild->GetNextChild();
				}
			}
			break;

		case k_EWebAPIValueType_NumericArray:
			{
				// the type should have been set when this node was first created
				Assert( m_eValueType == k_EWebAPIValueType_NumericArray );
				m_eValueType = k_EWebAPIValueType_NumericArray;

				const CWebAPIValues *pSourceChild = pSource->GetFirstChild();
				while( pSourceChild )
				{
					CWebAPIValues *pChild;
					if( pSourceChild->IsArray() )
						pChild = AddChildArrayToArray( pSourceChild->GetElementName() );
					else
						pChild = AddChildObjectToArray( );

					pChild->CopyFrom( pSourceChild );
					pSourceChild = pSourceChild->GetNextChild();
				}
			}
			break;

		default:
			AssertMsg( false, "Unknown type in CWebAPIValues::CopyFrom" );
			break;
	}
}

//-----------------------------------------------------------------------------
// Purpose: Fills a WebAPI key values from a corresponding protobuf message.
//-----------------------------------------------------------------------------
bool ProtoBufHelper::RecursiveAddProtoBufToWebAPIValues( CWebAPIValues *pWebAPIRoot, const ::google::protobuf::Message & msg )
{
	using ::google::protobuf::FieldDescriptor;
	const ::google::protobuf::Descriptor *pDescriptor = msg.GetDescriptor();
	const ::google::protobuf::Reflection *pReflection = msg.GetReflection();

	for ( int iField = 0; iField < pDescriptor->field_count(); iField++ )
	{
		const ::google::protobuf::FieldDescriptor *pField = pDescriptor->field( iField );
		const char *pFieldName = pField->name().c_str();

		if ( pField->is_repeated() )
		{
			if ( pReflection->FieldSize( msg, pField ) == 0 )
			{
				continue;		// No need to create the array if it is empty
								// If the field has been disabled externally, it has been cleared already (and thus will be skipped)
			}

			// bugbug: Is there a way to get this from the google API?
			const char *pchTypeName = "unknown_type";
			switch ( pField->cpp_type() )
			{
			case FieldDescriptor::CPPTYPE_INT32:	pchTypeName = "int32"; break;
			case FieldDescriptor::CPPTYPE_INT64:	pchTypeName = "int64"; break;
			case FieldDescriptor::CPPTYPE_UINT32:	pchTypeName = "uint32"; break;
			case FieldDescriptor::CPPTYPE_DOUBLE:	pchTypeName = "double"; break;
			case FieldDescriptor::CPPTYPE_FLOAT:	pchTypeName = "float"; break;
			case FieldDescriptor::CPPTYPE_BOOL:		pchTypeName = "bool"; break;
			case FieldDescriptor::CPPTYPE_ENUM:		pchTypeName = "enum"; break;
			case FieldDescriptor::CPPTYPE_STRING:
				{
					if ( pField->type() == FieldDescriptor::TYPE_STRING )
					{
						pchTypeName = "string";
					}
					else
					{
						AssertMsg1( pField->type() == FieldDescriptor::TYPE_BYTES, "Unrecognized field type: %d", pField->type() );
						pchTypeName = "bytes";
					}
					break;
				}
			case FieldDescriptor::CPPTYPE_MESSAGE:	pchTypeName = "message"; break;
			}

			CWebAPIValues *pContainer = pWebAPIRoot->CreateChildArray( pFieldName, pchTypeName );
			for ( int iRepeated = 0; iRepeated < pReflection->FieldSize( msg, pField ); iRepeated++ )
			{
				switch ( pField->cpp_type() )
				{
				case FieldDescriptor::CPPTYPE_INT32:	pContainer->SetChildInt32Value( NULL, pReflection->GetRepeatedInt32( msg, pField, iRepeated ) );			break;
				case FieldDescriptor::CPPTYPE_INT64:	pContainer->SetChildInt64Value( NULL, pReflection->GetRepeatedInt64( msg, pField, iRepeated ) );			break;
				case FieldDescriptor::CPPTYPE_UINT32:	pContainer->SetChildUInt32Value( NULL, pReflection->GetRepeatedUInt32( msg, pField, iRepeated ) );			break;
				case FieldDescriptor::CPPTYPE_UINT64:	pContainer->SetChildUInt64Value( NULL, pReflection->GetRepeatedUInt64( msg, pField, iRepeated ) );			break;
				case FieldDescriptor::CPPTYPE_DOUBLE:	pContainer->SetChildDoubleValue( NULL, pReflection->GetRepeatedDouble( msg, pField, iRepeated ) );			break;
				case FieldDescriptor::CPPTYPE_FLOAT:	pContainer->SetChildDoubleValue( NULL, pReflection->GetRepeatedFloat( msg, pField, iRepeated ) );			break;
				case FieldDescriptor::CPPTYPE_BOOL:		pContainer->SetChildBoolValue( NULL, pReflection->GetRepeatedBool( msg, pField, iRepeated ) );				break;
				case FieldDescriptor::CPPTYPE_ENUM:		pContainer->SetChildInt32Value( NULL, pReflection->GetRepeatedEnum( msg, pField, iRepeated )->number() );	break;
				case FieldDescriptor::CPPTYPE_STRING:
					{
						const std::string &strValue = pReflection->GetRepeatedString( msg, pField, iRepeated );
						if ( pField->type() == FieldDescriptor::TYPE_STRING )
						{
							pContainer->SetChildStringValue( NULL, strValue.c_str() );
						}
						else
						{
							// Binary blobs are automatically encoded in Base64 when converted to string by Web request
							pContainer->SetChildBinaryValue( NULL, (const uint8 *)strValue.c_str(), strValue.size() );
						}
						break;
					}
				case FieldDescriptor::CPPTYPE_MESSAGE:
					{
						const ::google::protobuf::Message &subMsg = pReflection->GetRepeatedMessage( msg, pField, iRepeated );
						CWebAPIValues *pChild = pContainer->CreateChildObject( NULL );
						if ( RecursiveAddProtoBufToWebAPIValues( pChild, subMsg ) == false )
						{
							return false;
						}
						break;
					}
				default:
					AssertMsg1( false, "Unknown cpp_type %d", pField->cpp_type() );
					return false;
				}
			}
		}
		else
		{
			if ( ( ( pReflection->HasField( msg, pField ) == false ) && ( pField->has_default_value() == false ) )
			//	|| pField->options().GetExtension( is_field_disabled_externally ) // Steam vesion supports this
			)
			{
				continue;		// If no field set and no default value set, there is no need to send the field
								// Or it is externally disabled (it has been cleared already but there still may be a default value set)
			}

			switch ( pField->cpp_type() )
			{
			case FieldDescriptor::CPPTYPE_INT32:	pWebAPIRoot->SetChildInt32Value( pFieldName, pReflection->GetInt32( msg, pField ) );			break;
			case FieldDescriptor::CPPTYPE_INT64:	pWebAPIRoot->SetChildInt64Value( pFieldName, pReflection->GetInt64( msg, pField ) );			break;
			case FieldDescriptor::CPPTYPE_UINT32:	pWebAPIRoot->SetChildUInt32Value( pFieldName, pReflection->GetUInt32( msg, pField ) );			break;
			case FieldDescriptor::CPPTYPE_UINT64:	pWebAPIRoot->SetChildUInt64Value( pFieldName, pReflection->GetUInt64( msg, pField ) );			break;
			case FieldDescriptor::CPPTYPE_DOUBLE:	pWebAPIRoot->SetChildDoubleValue( pFieldName, pReflection->GetDouble( msg, pField ) );			break;
			case FieldDescriptor::CPPTYPE_FLOAT:	pWebAPIRoot->SetChildDoubleValue( pFieldName, pReflection->GetFloat( msg, pField ) );			break;
			case FieldDescriptor::CPPTYPE_BOOL:		pWebAPIRoot->SetChildBoolValue( pFieldName, pReflection->GetBool( msg, pField ) );				break;
			case FieldDescriptor::CPPTYPE_ENUM:		pWebAPIRoot->SetChildInt32Value( pFieldName, pReflection->GetEnum( msg, pField )->number() );	break;
			case FieldDescriptor::CPPTYPE_STRING:
				{
					const std::string &strValue = pReflection->GetString( msg, pField );
					if ( pField->type() == FieldDescriptor::TYPE_STRING )
					{
						pWebAPIRoot->SetChildStringValue( pFieldName, strValue.c_str() );
					}
					else
					{
						AssertMsg1( pField->type() == FieldDescriptor::TYPE_BYTES, "Unrecognized field type: %d", pField->type() );
						// Binary blobs are automatically encoded in Base64 when converted to string by Web request
						pWebAPIRoot->SetChildBinaryValue( pFieldName, (const uint8 *)strValue.c_str(), strValue.size() );
					}
					break;
				}
			case FieldDescriptor::CPPTYPE_MESSAGE:
				{
					CWebAPIValues *pChild = pWebAPIRoot->CreateChildObject( pFieldName );
#undef GetMessage	// Work around unfortunate Microsoft macro
					const ::google::protobuf::Message &subMsg = pReflection->GetMessage( msg, pField );
#ifdef UNICODE
#define GetMessage  GetMessageW
#else
#define GetMessage  GetMessageA
#endif // !UNICODE
					if ( RecursiveAddProtoBufToWebAPIValues( pChild, subMsg ) == false )
					{
						return false;
					}
					break;
				}
			default:
				AssertMsg1( false, "Unknown cpp_type %d", pField->cpp_type() );
				return false;
			}
		}
	}
	return true;
}