//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include <tier0/dbg.h>
#include <tier0/vprof.h>
#include <tier0/icommandline.h>
#include <commonmacros.h>
#include <checksum_crc.h>

#include "dt_send_eng.h"
#include "dt_encode.h"
#include "dt_instrumentation_server.h"
#include "dt_stack.h"
#include "common.h"
#include "packed_entity.h"

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

extern int host_framecount;
CRC32_t SendTable_ComputeCRC();

class CSendTablePrecalc;
class CSendNode;

extern bool Sendprop_UsingDebugWatch();


// This stack doesn't actually call any proxies. It uses the CSendProxyRecipients to tell
// what can be sent to the specified client.
class CPropCullStack : public CDatatableStack
{
public:
						CPropCullStack( 
							CSendTablePrecalc *pPrecalc, 
							int iClient, 
							const CSendProxyRecipients *pOldStateProxies,
							const int nOldStateProxies, 
							const CSendProxyRecipients *pNewStateProxies,
							const int nNewStateProxies
							) :
							
							CDatatableStack( pPrecalc, (unsigned char*)1, -1 ),
							m_pOldStateProxies( pOldStateProxies ),
							m_nOldStateProxies( nOldStateProxies ),
							m_pNewStateProxies( pNewStateProxies ),
							m_nNewStateProxies( nNewStateProxies )
						{
							m_pPrecalc = pPrecalc;
							m_iClient = iClient;
						}

	inline unsigned char*	CallPropProxy( CSendNode *pNode, int iProp, unsigned char *pStructBase )
	{
		if ( pNode->GetDataTableProxyIndex() == DATATABLE_PROXY_INDEX_NOPROXY )
		{
			return (unsigned char*)1;
		}
		else
		{
			Assert( pNode->GetDataTableProxyIndex() < m_nNewStateProxies );
			bool bCur = m_pNewStateProxies[ pNode->GetDataTableProxyIndex() ].m_Bits.Get( m_iClient ) != 0;

			if ( m_pOldStateProxies )
			{
				Assert( pNode->GetDataTableProxyIndex() < m_nOldStateProxies );
				bool bPrev = m_pOldStateProxies[ pNode->GetDataTableProxyIndex() ].m_Bits.Get( m_iClient ) != 0;
				if ( bPrev != bCur )
				{
					if ( bPrev )
					{
						// Old state had the data and the new state doesn't.
						return 0;
					}
					else
					{
						// Add ALL properties under this proxy because the previous state didn't have any of them.
						for ( int i=0; i < pNode->m_nRecursiveProps; i++ )
						{
							if ( m_nNewProxyProps < ARRAYSIZE( m_NewProxyProps ) )
							{
								m_NewProxyProps[m_nNewProxyProps] = pNode->m_iFirstRecursiveProp + i;
							}
							else
							{
								Error( "CPropCullStack::CallPropProxy - overflowed m_nNewProxyProps" );
							}

							++m_nNewProxyProps;
						}

						// Tell the outer loop that writes to m_pOutProps not to write anything from this
						// proxy since we just did.
						return 0;
					}
				}
			}

			return (unsigned char*)bCur;
		}
	}

	virtual void RecurseAndCallProxies( CSendNode *pNode, unsigned char *pStructBase )
	{
		// Remember where the game code pointed us for this datatable's data so 
		m_pProxies[ pNode->GetRecursiveProxyIndex() ] = pStructBase;

		for ( int iChild=0; iChild < pNode->GetNumChildren(); iChild++ )
		{
			CSendNode *pCurChild = pNode->GetChild( iChild );
			
			unsigned char *pNewStructBase = NULL;
			if ( pStructBase )
			{
				pNewStructBase = CallPropProxy( pCurChild, pCurChild->m_iDatatableProp, pStructBase );
			}

			RecurseAndCallProxies( pCurChild, pNewStructBase );
		}
	}
	
	inline void AddProp( int iProp )
	{
		if ( m_nOutProps < m_nMaxOutProps )
		{
			m_pOutProps[m_nOutProps] = iProp;
		}
		else
		{
			Error( "CPropCullStack::AddProp - m_pOutProps overflowed" );
		}

		++m_nOutProps;
	}


	void CullPropsFromProxies( const int *pStartProps, int nStartProps, int *pOutProps, int nMaxOutProps )
	{
		m_nOutProps = 0;
		m_pOutProps = pOutProps;
		m_nMaxOutProps = nMaxOutProps;
		m_nNewProxyProps = 0;

		Init();

		// This list will have any newly available props written into it. Write a sentinel at the end.
		m_NewProxyProps[m_nNewProxyProps] = -1; // invalid marker
		int *pCurNewProxyProp = m_NewProxyProps;

		for ( int i=0; i < nStartProps; i++ )
		{
			int iProp = pStartProps[i];

			// Fill in the gaps with any properties that are newly enabled by the proxies.
			while ( (unsigned int) *pCurNewProxyProp < (unsigned int) iProp )
			{
				AddProp( *pCurNewProxyProp );
				++pCurNewProxyProp;
			}

			// Now write this property's index if the proxies are allowing this property to be written.
			if ( IsPropProxyValid( iProp ) )
			{
				AddProp( iProp );

				// avoid that we add it twice.
				if ( *pCurNewProxyProp == iProp )
					++pCurNewProxyProp;
			}
		}

		// add any remaining new proxy props
		while ( (unsigned int) *pCurNewProxyProp < MAX_DATATABLE_PROPS )
		{
			AddProp( *pCurNewProxyProp );
			++pCurNewProxyProp;
		}
	}

	int GetNumOutProps()
	{
		return m_nOutProps;
	}


private:
	CSendTablePrecalc		*m_pPrecalc;
	int						m_iClient;	// Which client it's encoding out for.
	const CSendProxyRecipients	*m_pOldStateProxies;
	const int					m_nOldStateProxies;
	
	const CSendProxyRecipients	*m_pNewStateProxies;
	const int					m_nNewStateProxies;

	// The output property list.
	int						*m_pOutProps;
	int						m_nMaxOutProps;
	int						m_nOutProps;

	int m_NewProxyProps[MAX_DATATABLE_PROPS+1];
	int m_nNewProxyProps;
};



// ----------------------------------------------------------------------------- //
// CEncodeInfo
// Used by SendTable_Encode.
// ----------------------------------------------------------------------------- //
class CEncodeInfo : public CServerDatatableStack
{
public:
	CEncodeInfo( CSendTablePrecalc *pPrecalc, unsigned char *pStructBase, int objectID, bf_write *pOut ) :
		CServerDatatableStack( pPrecalc, pStructBase, objectID ),
		m_DeltaBitsWriter( pOut )
	{
	}

public:
	CDeltaBitsWriter m_DeltaBitsWriter;
};



// ------------------------------------------------------------------------ //
// Globals.
// ------------------------------------------------------------------------ //

CUtlVector< SendTable* > g_SendTables;
CRC32_t	g_SendTableCRC = 0;



// ------------------------------------------------------------------------ //
// SendTable functions.
// ------------------------------------------------------------------------ //

static bool s_debug_info_shown = false;
static int  s_debug_bits_start = 0;


static inline void ShowEncodeDeltaWatchInfo( 
	const SendTable *pTable,
	const SendProp *pProp, 
	bf_read &buffer,
	const int objectID,
	const int index )
{
	if ( !ShouldWatchThisProp( pTable, objectID, pProp->GetName()) )
		return;
	
	static int lastframe = -1;
	if ( host_framecount != lastframe )
	{
		lastframe = host_framecount;
		ConDMsg( "delta entity: %i\n", objectID );
	}

	// work on copy of bitbuffer
	bf_read copy = buffer;

	s_debug_info_shown = true;

	DecodeInfo info;
	info.m_pStruct = NULL;
	info.m_pData = NULL;
	info.m_pRecvProp = NULL;
	info.m_pProp = pProp;
	info.m_pIn = &copy;
	info.m_Value.m_Type = (SendPropType)pProp->m_Type;
	
	int startBit = copy.GetNumBitsRead();

	g_PropTypeFns[pProp->m_Type].Decode( &info );

	int bits = copy.GetNumBitsRead() - startBit;

	const char *type = g_PropTypeFns[pProp->m_Type].GetTypeNameString();
	const char *value = info.m_Value.ToString();

	ConDMsg( "+ %s %s, %s, index %i, bits %i, value %s\n", pTable->GetName(), pProp->GetName(), type, index, bits, value );
}


static FORCEINLINE void SendTable_EncodeProp( CEncodeInfo * pInfo, unsigned long iProp )
{
	// Call their proxy to get the property's value.
	DVariant var;
	
	const SendProp *pProp = pInfo->GetCurProp();
	unsigned char *pStructBase = pInfo->GetCurStructBase();

	pProp->GetProxyFn()( 
		pProp,
		pStructBase, 
		pStructBase + pProp->GetOffset(), 
		&var, 
		0, // iElement
		pInfo->GetObjectID()
		);

	// Write the index.
	pInfo->m_DeltaBitsWriter.WritePropIndex( iProp );

	g_PropTypeFns[pProp->m_Type].Encode( 
		pStructBase, 
		&var, 
		pProp, 
		pInfo->m_DeltaBitsWriter.GetBitBuf(), 
		pInfo->GetObjectID()
		); 
}


static bool SendTable_IsPropZero( CEncodeInfo *pInfo, unsigned long iProp )
{
	const SendProp *pProp = pInfo->GetCurProp();

	// Call their proxy to get the property's value.
	DVariant var;
	unsigned char *pBase = pInfo->GetCurStructBase();
	
	pProp->GetProxyFn()( 
		pProp,
		pBase, 
		pBase + pProp->GetOffset(), 
		&var, 
		0, // iElement
		pInfo->GetObjectID()
		);

	return g_PropTypeFns[pProp->m_Type].IsZero( pBase, &var, pProp );
}


int SendTable_CullPropsFromProxies( 
	const SendTable *pTable,
	
	const int *pStartProps,
	int nStartProps,

	const int iClient,
	
	const CSendProxyRecipients *pOldStateProxies,
	const int nOldStateProxies, 
	
	const CSendProxyRecipients *pNewStateProxies,
	const int nNewStateProxies,
	
	int *pOutProps,
	int nMaxOutProps
	)
{
	Assert( !( nNewStateProxies && !pNewStateProxies ) );
	CPropCullStack stack( pTable->m_pPrecalc, iClient, pOldStateProxies, nOldStateProxies, pNewStateProxies, nNewStateProxies );
	
	stack.CullPropsFromProxies( pStartProps, nStartProps, pOutProps, nMaxOutProps );

	ErrorIfNot( stack.GetNumOutProps() <= nMaxOutProps, ("CullPropsFromProxies: overflow in '%s'.", pTable->GetName()) );
	return stack.GetNumOutProps();
}


// compares properties and writes delta properties, it ignores reciepients
int SendTable_WriteAllDeltaProps(
	const SendTable *pTable,					
	const void *pFromData,
	const int	nFromDataBits,
	const void *pToData,
	const int nToDataBits,
	const int nObjectID,
	bf_write *pBufOut )
{
	// Calculate the delta props.
	int deltaProps[MAX_DATATABLE_PROPS];

	int nDeltaProps = SendTable_CalcDelta(
		pTable, 
		pFromData,
		nFromDataBits,
		pToData,
		nToDataBits,
		deltaProps,
		ARRAYSIZE( deltaProps ),
		nObjectID );

	// Write the properties.
	SendTable_WritePropList( 
		pTable,
		pToData,				// object data
		nToDataBits,
		pBufOut,				// output buffer
		nObjectID,
		deltaProps,
		nDeltaProps
	);

	return nDeltaProps;
}


bool SendTable_Encode(
	const SendTable *pTable,
	const void *pStruct, 
	bf_write *pOut, 
	int objectID,
	CUtlMemory<CSendProxyRecipients> *pRecipients,
	bool bNonZeroOnly
	)
{
	CSendTablePrecalc *pPrecalc = pTable->m_pPrecalc;
	ErrorIfNot( pPrecalc, ("SendTable_Encode: Missing m_pPrecalc for SendTable %s.", pTable->m_pNetTableName) );
	if ( pRecipients )
	{
		ErrorIfNot(	pRecipients->NumAllocated() >= pPrecalc->GetNumDataTableProxies(), ("SendTable_Encode: pRecipients array too small.") );
	}

	VPROF( "SendTable_Encode" );

	CServerDTITimer timer( pTable, SERVERDTI_ENCODE );

	// Setup all the info we'll be walking the tree with.
	CEncodeInfo info( pPrecalc, (unsigned char*)pStruct, objectID, pOut );
	info.m_pRecipients = pRecipients;	// optional buffer to store the bits for which clients get what data.

	info.Init();
	
	int iNumProps = pPrecalc->GetNumProps();

	for ( int iProp=0; iProp < iNumProps; iProp++ )
	{
		// skip if we don't have a valid prop proxy
		if ( !info.IsPropProxyValid( iProp ) )
			continue;

		info.SeekToProp( iProp );
        
		// skip empty prop if we only encode non-zero values
		if ( bNonZeroOnly && SendTable_IsPropZero(&info, iProp) )
			continue;

		SendTable_EncodeProp( &info, iProp );
	}

	return !pOut->IsOverflowed();
}


void SendTable_WritePropList(
	const SendTable *pTable,
	const void *pState,
	const int nBits,
	bf_write *pOut,
	const int objectID,
	const int *pCheckProps,
	const int nCheckProps
	)
{
	if ( nCheckProps == 0 )
	{
		// Write single final zero bit, signifying that there no changed properties
		pOut->WriteOneBit( 0 );
		return;
	}

	bool bDebugWatch = Sendprop_UsingDebugWatch();

	s_debug_info_shown = false;
	s_debug_bits_start = pOut->GetNumBitsWritten();
	
	CSendTablePrecalc *pPrecalc = pTable->m_pPrecalc;
	CDeltaBitsWriter deltaBitsWriter( pOut );

	bf_read inputBuffer( "SendTable_WritePropList->inputBuffer", pState, BitByte( nBits ), nBits );
	CDeltaBitsReader inputBitsReader( &inputBuffer );

	// Ok, they want to specify a small list of properties to check.
	unsigned int iToProp = inputBitsReader.ReadNextPropIndex();
	int i = 0;
	while ( i < nCheckProps )
	{
		// Seek the 'to' state to the current property we want to check.
		while ( iToProp < (unsigned int) pCheckProps[i] )
		{
			inputBitsReader.SkipPropData( pPrecalc->GetProp( iToProp ) );
			iToProp = inputBitsReader.ReadNextPropIndex();
		}

		if ( iToProp >= MAX_DATATABLE_PROPS )
		{
			break;
		}
		
		if ( iToProp == (unsigned int) pCheckProps[i] )
		{
			const SendProp *pProp = pPrecalc->GetProp( iToProp );

			// Show debug stuff.
			if ( bDebugWatch )
			{
				ShowEncodeDeltaWatchInfo( pTable, pProp, inputBuffer, objectID, iToProp );
			}

			// See how many bits the data for this property takes up.
			int nToStateBits;
			int iStartBit = pOut->GetNumBitsWritten();

			deltaBitsWriter.WritePropIndex( iToProp );
			inputBitsReader.CopyPropData( deltaBitsWriter.GetBitBuf(), pProp ); 

			nToStateBits = pOut->GetNumBitsWritten() - iStartBit;

			TRACE_PACKET( ( "    Send Field (%s) = %d (%d bytes)\n", pProp->GetName(), nToStateBits, ( nToStateBits + 7 ) / 8 ) );

			// Seek to the next prop.
			iToProp = inputBitsReader.ReadNextPropIndex();
		}

		++i;
	}

	if ( s_debug_info_shown )
	{
		int  bits = pOut->GetNumBitsWritten() - s_debug_bits_start;
		ConDMsg( "= %i bits (%i bytes)\n", bits, Bits2Bytes(bits) );
	}

	inputBitsReader.ForceFinished(); // avoid a benign assert
}


int SendTable_CalcDelta(
	const SendTable *pTable,
	
	const void *pFromState,
	const int nFromBits,
	
	const void *pToState,
	const int nToBits,
	
	int *pDeltaProps,
	int nMaxDeltaProps,

	const int objectID
	)
{
	CServerDTITimer timer( pTable, SERVERDTI_CALCDELTA );

	int *pDeltaPropsBase = pDeltaProps;
	int *pDeltaPropsEnd = pDeltaProps + nMaxDeltaProps;

	VPROF( "SendTable_CalcDelta" );
	
	// Trivial reject.
	//if ( CompareBitArrays( pFromState, pToState, nFromBits, nToBits ) )
	//{
	//	return 0;
	//}

	CSendTablePrecalc* pPrecalc = pTable->m_pPrecalc;

	bf_read toBits( "SendTable_CalcDelta/toBits", pToState, BitByte(nToBits), nToBits );
	CDeltaBitsReader toBitsReader( &toBits );
	unsigned int iToProp = toBitsReader.ReadNextPropIndex();

	if ( pFromState )
	{
		bf_read fromBits( "SendTable_CalcDelta/fromBits", pFromState, BitByte(nFromBits), nFromBits );
		CDeltaBitsReader fromBitsReader( &fromBits );
		unsigned int iFromProp = fromBitsReader.ReadNextPropIndex();

		for ( ; iToProp < MAX_DATATABLE_PROPS; iToProp = toBitsReader.ReadNextPropIndex() )
		{
			Assert( (int)iToProp >= 0 );

			// Skip any properties in the from state that aren't in the to state.
			while ( iFromProp < iToProp )
			{
				fromBitsReader.SkipPropData( pPrecalc->GetProp( iFromProp ) );
				iFromProp = fromBitsReader.ReadNextPropIndex();
			}

			if ( iFromProp == iToProp )
			{
				// The property is in both states, so compare them and write the index 
				// if the states are different.
				if ( fromBitsReader.ComparePropData( &toBitsReader, pPrecalc->GetProp( iToProp ) ) )
				{
					*pDeltaProps++ = iToProp;
					if ( pDeltaProps >= pDeltaPropsEnd )
					{
						break;
					}
				}

				// Seek to the next property.
				iFromProp = fromBitsReader.ReadNextPropIndex();
			}
			else
			{
				// Only the 'to' state has this property, so just skip its data and register a change.
				toBitsReader.SkipPropData( pPrecalc->GetProp( iToProp ) );
				*pDeltaProps++ = iToProp;
				if ( pDeltaProps >= pDeltaPropsEnd )
				{
					break;
				}
			}
		}

		Assert( iToProp == ~0u );

		fromBitsReader.ForceFinished();
	}
	else
	{
		for ( ; iToProp != (uint)-1; iToProp = toBitsReader.ReadNextPropIndex() )
		{
			Assert( (int)iToProp >= 0 && iToProp < MAX_DATATABLE_PROPS );

			const SendProp *pProp = pPrecalc->GetProp( iToProp );
			if ( !g_PropTypeFns[pProp->m_Type].IsEncodedZero( pProp, &toBits ) )
			{
				*pDeltaProps++ = iToProp;
				if ( pDeltaProps >= pDeltaPropsEnd )
				{
					break;
				}
			}
		}
	}

	// Return the # of properties that changed between 'from' and 'to'.
	return pDeltaProps - pDeltaPropsBase;
}

bool SendTable_WriteInfos( SendTable *pTable, bf_write *pBuf )
{
	pBuf->WriteString( pTable->GetName() );
	pBuf->WriteUBitLong( pTable->GetNumProps(), PROPINFOBITS_NUMPROPS );

	// Send each property.
	for ( int iProp=0; iProp < pTable->m_nProps; iProp++ )
	{
		const SendProp *pProp = &pTable->m_pProps[iProp];

		pBuf->WriteUBitLong( (unsigned int)pProp->m_Type, PROPINFOBITS_TYPE );
		pBuf->WriteString( pProp->GetName() );
		// we now have some flags that aren't networked so strip them off
		unsigned int networkFlags = pProp->GetFlags() & ((1<<PROPINFOBITS_FLAGS)-1);
		pBuf->WriteUBitLong( networkFlags, PROPINFOBITS_FLAGS );

		if( pProp->m_Type == DPT_DataTable )
		{
			// Just write the name and it will be able to reuse the table with a matching name.
			pBuf->WriteString( pProp->GetDataTable()->m_pNetTableName );
		}
		else
		{
			if ( pProp->IsExcludeProp() )
			{
				pBuf->WriteString( pProp->GetExcludeDTName() );
			}
			else if ( pProp->GetType() == DPT_Array )
			{
				pBuf->WriteUBitLong( pProp->GetNumElements(), PROPINFOBITS_NUMELEMENTS );
			}
			else
			{			
				pBuf->WriteBitFloat( pProp->m_fLowValue );
				pBuf->WriteBitFloat( pProp->m_fHighValue );
				pBuf->WriteUBitLong( pProp->m_nBits, PROPINFOBITS_NUMBITS );
			}
		}
	}

	return !pBuf->IsOverflowed();
}



// Spits out warnings for invalid properties and forces property values to
// be in valid ranges for the encoders and decoders.
static void SendTable_Validate( CSendTablePrecalc *pPrecalc )
{
	SendTable *pTable = pPrecalc->m_pSendTable;
	for( int i=0; i < pTable->m_nProps; i++ )
	{
		SendProp *pProp = &pTable->m_pProps[i];
		
		if ( pProp->GetArrayProp() )
		{
			if ( pProp->GetArrayProp()->GetType() == DPT_DataTable )
			{
				Error( "Invalid property: %s/%s (array of datatables) [on prop %d of %d (%s)].", pTable->m_pNetTableName, pProp->GetName(), i, pTable->m_nProps, pProp->GetArrayProp()->GetName() );
			}
		}
		else
		{
			ErrorIfNot( pProp->GetNumElements() == 1, ("Prop %s/%s has an invalid element count for a non-array.", pTable->m_pNetTableName, pProp->GetName()) );
		}
			
		// Check for 1-bit signed properties (their value doesn't get down to the client).
		if ( pProp->m_nBits == 1 && !(pProp->GetFlags() & SPROP_UNSIGNED) )
		{
			DataTable_Warning("SendTable prop %s::%s is a 1-bit signed property. Use SPROP_UNSIGNED or the client will never receive a value.\n", pTable->m_pNetTableName, pProp->GetName());
		}
	}

	for ( int i = 0; i < pPrecalc->GetNumProps(); ++i )
	{
		const SendProp *pProp = pPrecalc->GetProp( i );
		if ( pProp->GetFlags() & SPROP_ENCODED_AGAINST_TICKCOUNT )
		{
			pTable->SetHasPropsEncodedAgainstTickcount( true );
			break;
		}
	}
}


static void SendTable_CalcNextVectorElems( SendTable *pTable )
{
	for ( int i=0; i < pTable->GetNumProps(); i++ )
	{
		SendProp *pProp = pTable->GetProp( i );
		
		if ( pProp->GetType() == DPT_DataTable )
		{
			SendTable_CalcNextVectorElems( pProp->GetDataTable() );
		}
		else if ( pProp->GetOffset() < 0 )
		{
			pProp->SetOffset( -pProp->GetOffset() );
			pProp->SetFlags( pProp->GetFlags() | SPROP_IS_A_VECTOR_ELEM );
		}
	}
}


static bool SendTable_InitTable( SendTable *pTable )
{
	if( pTable->m_pPrecalc )
		return true;
	
	// Create the CSendTablePrecalc.	
	CSendTablePrecalc *pPrecalc = new CSendTablePrecalc;
	pTable->m_pPrecalc = pPrecalc;

	pPrecalc->m_pSendTable = pTable;
	pTable->m_pPrecalc = pPrecalc;

	SendTable_CalcNextVectorElems( pTable );

	// Bind the instrumentation if -dti was specified.
	pPrecalc->m_pDTITable = ServerDTI_HookTable( pTable );

	// Setup its flat property array.
	if ( !pPrecalc->SetupFlatPropertyArray() )
		return false;

	SendTable_Validate( pPrecalc );
	return true;
}


static void SendTable_TermTable( SendTable *pTable )
{
	if( !pTable->m_pPrecalc )
		return;

	delete pTable->m_pPrecalc;
	Assert( !pTable->m_pPrecalc ); // Make sure it unbound itself.
}


int SendTable_GetNumFlatProps( SendTable *pSendTable )
{
	CSendTablePrecalc *pPrecalc = pSendTable->m_pPrecalc;
	ErrorIfNot( pPrecalc,
		("SendTable_GetNumFlatProps: missing pPrecalc.")
	);
	return pPrecalc->GetNumProps();
}

CRC32_t SendTable_CRCTable( CRC32_t &crc, SendTable *pTable )
{
	CRC32_ProcessBuffer( &crc, (void *)pTable->m_pNetTableName, Q_strlen( pTable->m_pNetTableName) );

	int nProps = LittleLong( pTable->m_nProps );
	CRC32_ProcessBuffer( &crc, (void *)&nProps, sizeof( pTable->m_nProps ) );

	// Send each property.
	for ( int iProp=0; iProp < pTable->m_nProps; iProp++ )
	{
		const SendProp *pProp = &pTable->m_pProps[iProp];

		int type = LittleLong( pProp->m_Type );
		CRC32_ProcessBuffer( &crc, (void *)&type, sizeof( type ) );
		CRC32_ProcessBuffer( &crc, (void *)pProp->GetName() , Q_strlen( pProp->GetName() ) );

		int flags = LittleLong( pProp->GetFlags() );
		CRC32_ProcessBuffer( &crc, (void *)&flags, sizeof( flags ) );

		if( pProp->m_Type == DPT_DataTable )
		{
			CRC32_ProcessBuffer( &crc, (void *)pProp->GetDataTable()->m_pNetTableName, Q_strlen( pProp->GetDataTable()->m_pNetTableName ) );
		}
		else
		{
			if ( pProp->IsExcludeProp() )
			{
				CRC32_ProcessBuffer( &crc, (void *)pProp->GetExcludeDTName(), Q_strlen( pProp->GetExcludeDTName() ) );
			}
			else if ( pProp->GetType() == DPT_Array )
			{
				int numelements = LittleLong( pProp->GetNumElements() );
				CRC32_ProcessBuffer( &crc, (void *)&numelements, sizeof( numelements ) );
			}
			else
			{	
				float lowvalue;
				LittleFloat( &lowvalue, &pProp->m_fLowValue );
				CRC32_ProcessBuffer( &crc, (void *)&lowvalue, sizeof( lowvalue ) );

				float highvalue;
				LittleFloat( &highvalue, &pProp->m_fHighValue );
				CRC32_ProcessBuffer( &crc, (void *)&highvalue, sizeof( highvalue ) );

				int	bits = LittleLong( pProp->m_nBits );
				CRC32_ProcessBuffer( &crc, (void *)&bits, sizeof( bits ) );
			}
		}
	}

	return crc;
}

void SendTable_PrintStats( void )
{
	int numTables = 0;
	int numFloats = 0;
	int numStrings = 0;
	int numArrays = 0;
	int numInts = 0;
	int numVecs = 0;
	int numVecXYs = 0;
	int numSubTables = 0;
	int numSendProps = 0;
	int numFlatProps = 0;
	int numExcludeProps = 0;

	for ( int i=0; i < g_SendTables.Count(); i++ )
	{
		SendTable *st =  g_SendTables[i];
		
		numTables++;
		numSendProps += st->GetNumProps();
		numFlatProps += st->m_pPrecalc->GetNumProps();

		for ( int j=0; j < st->GetNumProps(); j++ )
		{
			SendProp* sp = st->GetProp( j );

			if ( sp->IsExcludeProp() )
			{
				numExcludeProps++;
				continue; // no real sendprops
			}

			if ( sp->IsInsideArray() )
				continue;

			switch( sp->GetType() )
			{
				case DPT_Int	: numInts++; break;
				case DPT_Float	: numFloats++; break;
				case DPT_Vector : numVecs++; break;
				case DPT_VectorXY : numVecXYs++; break;
				case DPT_String : numStrings++; break;
				case DPT_Array	: numArrays++; break;
				case DPT_DataTable : numSubTables++; break;
			}
		}
	}

	Msg("Total Send Table stats\n");
	Msg("Send Tables   : %i\n", numTables );
	Msg("Send Props    : %i\n", numSendProps );
	Msg("Flat Props    : %i\n", numFlatProps );
	Msg("Int Props     : %i\n", numInts );
	Msg("Float Props   : %i\n", numFloats );
	Msg("Vector Props  : %i\n", numVecs );
	Msg("VectorXY Props: %i\n", numVecXYs );
	Msg("String Props  : %i\n", numStrings );
	Msg("Array Props   : %i\n", numArrays );
	Msg("Table Props   : %i\n", numSubTables );
	Msg("Exclu Props   : %i\n", numExcludeProps );
}



bool SendTable_Init( SendTable **pTables, int nTables )
{
	ErrorIfNot( g_SendTables.Count() == 0,
		("SendTable_Init: called twice.")
	);

	// Initialize them all.
	for ( int i=0; i < nTables; i++ )
	{
		if ( !SendTable_InitTable( pTables[i] ) )
			return false;
	}

	// Store off the SendTable list.
	g_SendTables.CopyArray( pTables, nTables );

	g_SendTableCRC = SendTable_ComputeCRC( );

	if ( CommandLine()->FindParm("-dti" ) )
	{
		SendTable_PrintStats();
	}

	return true;	
}
void SendTable_Term()
{
	// Term all the SendTables.
	for ( int i=0; i < g_SendTables.Count(); i++ )
		SendTable_TermTable( g_SendTables[i] );

	// Clear the list of SendTables.
	g_SendTables.Purge();
	g_SendTableCRC = 0;
}

//-----------------------------------------------------------------------------
// Purpose: Computes the crc for all sendtables for the data sent in the class/table definitions
// Output : CRC32_t
//-----------------------------------------------------------------------------
CRC32_t SendTable_ComputeCRC()
{
	CRC32_t result;
	CRC32_Init( &result );

	// walk the tables and checksum them
	int c = g_SendTables.Count();
	for ( int i = 0 ; i < c; i++ )
	{
		SendTable *st = g_SendTables[ i ];
		result = SendTable_CRCTable( result, st );
	}


	CRC32_Final( &result );

	return result;
}

SendTable *SendTabe_GetTable(int index)
{
	return  g_SendTables[index];
}

int	SendTable_GetNum()
{
	return g_SendTables.Count();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : CRC32_t
//-----------------------------------------------------------------------------
CRC32_t SendTable_GetCRC()
{
	return g_SendTableCRC;
}


//-----------------------------------------------------------------------------
// Purpose: check integrity of an unpacked entity send table
//-----------------------------------------------------------------------------
bool SendTable_CheckIntegrity( SendTable *pTable, const void *pData, const int nDataBits )
{
#ifdef _DEBUG
	if ( pData == NULL && nDataBits == 0 )
		return true;

	bf_read bfRead(	"SendTable_CheckIntegrity", pData, Bits2Bytes(nDataBits), nDataBits );
	CDeltaBitsReader bitsReader( &bfRead );

	int iProp = -1;
	int iLastProp = -1;
	int nMaxProps = pTable->m_pPrecalc->GetNumProps();
	int nPropCount = 0;

	Assert( nMaxProps > 0 && nMaxProps < MAX_DATATABLE_PROPS );

	while( -1 != (iProp = bitsReader.ReadNextPropIndex()) )
	{
		Assert( (iProp>=0) && (iProp<nMaxProps) );

		// must be larger
		Assert( iProp > iLastProp );

		const SendProp *pProp = pTable->m_pPrecalc->GetProp( iProp );

		Assert( pProp );

		// ok check that SkipProp & IsEncodedZero read the same bit length
		int iStartBit = bfRead.GetNumBitsRead();
		g_PropTypeFns[ pProp->GetType() ].SkipProp( pProp, &bfRead );
		int nLength = bfRead.GetNumBitsRead() - iStartBit;

		Assert( nLength > 0 ); // a prop must have some bits

		bfRead.Seek( iStartBit ); // read it again

		g_PropTypeFns[ pProp->GetType() ].IsEncodedZero( pProp, &bfRead );

		Assert( nLength == (bfRead.GetNumBitsRead() - iStartBit) ); 

		nPropCount++;
		iLastProp = iProp;
	}

	Assert( nPropCount <= nMaxProps );
	Assert( bfRead.GetNumBytesLeft() < 4 ); 
	Assert( !bfRead.IsOverflowed() );

#endif

	return true;
}