//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//
// ThreadedTCPSocketTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "IThreadedTCPSocket.h"
#include "threadhelpers.h"
#include "vstdlib/random.h"


CCriticalSection g_MsgCS;


IThreadedTCPSocket *g_pClientSocket = NULL;
IThreadedTCPSocket *g_pServerSocket = NULL;

CEvent g_ClientPacketEvent;
CUtlVector<char> g_ClientPacket;



SpewRetval_t MySpewFunc( SpewType_t type, char const *pMsg )
{
	CCriticalSectionLock csLock( &g_MsgCS );
	csLock.Lock();

		printf( "%s", pMsg );
		OutputDebugString( pMsg );

	csLock.Unlock();	

	if( type == SPEW_ASSERT )
		return SPEW_DEBUGGER;
	else if( type == SPEW_ERROR )
		return SPEW_ABORT;
	else
		return SPEW_CONTINUE;
}


class CHandler_Server : public ITCPSocketHandler
{
public:
	virtual void Init( IThreadedTCPSocket *pSocket )
	{
	}
	
	virtual void OnPacketReceived( CTCPPacket *pPacket )
	{
		// Echo the data back.
		g_pServerSocket->Send( pPacket->GetData(), pPacket->GetLen() );
		pPacket->Release();
	}

	virtual void OnError( int errorCode, const char *pErrorString )
	{
		Msg( "Server error: %s\n", pErrorString );
	}
};



class CHandler_Client : public ITCPSocketHandler
{
public:
	virtual void Init( IThreadedTCPSocket *pSocket )
	{
	}
	
	virtual void OnPacketReceived( CTCPPacket *pPacket )
	{
		if ( g_ClientPacket.Count() < pPacket->GetLen() )
			g_ClientPacket.SetSize( pPacket->GetLen() );
		
		memcpy( g_ClientPacket.Base(), pPacket->GetData(), pPacket->GetLen() );
		g_ClientPacketEvent.SetEvent();
		pPacket->Release();
	}

	virtual void OnError( int errorCode, const char *pErrorString )
	{
		Msg( "Client error: %s\n", pErrorString );
	}
};



class CHandlerCreator_Server : public IHandlerCreator
{
public:
	virtual ITCPSocketHandler* CreateNewHandler()
	{
		return new CHandler_Server;
	}
};

class CHandlerCreator_Client : public IHandlerCreator
{
public:
	virtual ITCPSocketHandler* CreateNewHandler()
	{
		return new CHandler_Client;
	}
};



int main(int argc, char* argv[])
{
	SpewOutputFunc( MySpewFunc );

	// Figure out a random port to use.
	CCycleCount cnt;
	cnt.Sample();
	CUniformRandomStream randomStream;
	randomStream.SetSeed( cnt.GetMicroseconds() );
	int iPort = randomStream.RandomInt( 20000, 30000 );


	g_ClientPacketEvent.Init( false, false );

	
	// Setup the "server".
	CHandlerCreator_Server serverHandler;
	CIPAddr addr( 127, 0, 0, 1, iPort );

	ITCPConnectSocket *pListener = ThreadedTCP_CreateListener( 
		&serverHandler,
		(unsigned short)iPort );

	
	// Setup the "client".
	CHandlerCreator_Client clientCreator;
	ITCPConnectSocket *pConnector = ThreadedTCP_CreateConnector( 
		CIPAddr( 127, 0, 0, 1, iPort ),
		CIPAddr(),
		&clientCreator );


	// Wait for them to connect.
	while ( !g_pClientSocket )
	{
		if ( !pConnector->Update( &g_pClientSocket ) )
		{
			Error( "Error in client connector!\n" );
		}
	}
	pConnector->Release();


	while ( !g_pServerSocket )
	{
		if ( !pListener->Update( &g_pServerSocket ) )
			Error( "Error in server connector!\n" );
	}
	pListener->Release();


	// Send some data.
	__int64 totalBytes = 0;
	CCycleCount startTime;
	int iPacket = 1;

	startTime.Sample();
	CUtlVector<char> buf;

	while ( (GetAsyncKeyState( VK_SHIFT ) & 0x8000) == 0 )
	{
		int size = randomStream.RandomInt( 1024*0, 1024*320 );
		if ( buf.Count() < size )
			buf.SetSize( size );

		if ( g_pClientSocket->Send( buf.Base(), size ) )
		{
			// Server receives the data and echoes it back. Verify that the data is good.
			WaitForSingleObject( g_ClientPacketEvent.GetEventHandle(), INFINITE );
			Assert( memcmp( g_ClientPacket.Base(), buf.Base(), size ) == 0 );

			totalBytes += size;
			CCycleCount curTime, elapsed;
			curTime.Sample();
			CCycleCount::Sub( curTime, startTime, elapsed );
			double flSeconds = elapsed.GetSeconds();
			Msg( "Packet %d, %d bytes, %dk/sec\n", iPacket++, size, (int)(((totalBytes+511)/1024) / flSeconds) );
		}
	}
	
	g_pClientSocket->Release();
	g_pServerSocket->Release();
	return 0;
}