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

#ifndef GCCLIENTJOB_H
#define GCCLIENTJOB_H
#ifdef _WIN32
#pragma once
#endif

namespace GCSDK
{
class CGCClient;

//-----------------------------------------------------------------------------
// Purpose: handles a network message job from the client
//-----------------------------------------------------------------------------
class CGCClientJob : public CJob
{
public:
	CGCClientJob( CGCClient *pGCClient ) : CJob( pGCClient->GetJobMgr() ), m_pGCClient( pGCClient ), m_cHeartbeatsBeforeTimeout( k_cJobHeartbeatsBeforeTimeoutDefault ) {}

	// all GCClient jobs must implement one of these
	virtual bool BYieldingRunGCJob( IMsgNetPacket *pNetPacket )	{ return false; }
	virtual bool BYieldingRunGCJob()							{ return false; }

	virtual EServerType GetServerType() { return k_EServerTypeGCClient; }

protected:
	CGCClient *m_pGCClient;

	bool BYldSendMessageAndGetReply( CGCMsgBase &msgOut, uint nTimeoutSec, CGCMsgBase *pMsgIn, MsgType_t eMsg )
	{
		IMsgNetPacket *pNetPacket = NULL;

		if ( !BYldSendMessageAndGetReply( msgOut, nTimeoutSec, &pNetPacket ) )
			return false;

		pMsgIn->SetPacket( pNetPacket );

		if ( pMsgIn->Hdr().m_eMsg != eMsg )
			return false;

		return true;
	}

	bool BYldSendMessageAndGetReply( CGCMsgBase &msgOut, uint nTimeoutSec, IMsgNetPacket **ppNetPacket )
	{
		msgOut.ExpectingReply( GetJobID() );

		if ( !m_pGCClient->BSendMessage( msgOut ) )
			return false;

		SetJobTimeout( nTimeoutSec );
		return BYieldingWaitForMsg( ppNetPacket );
	}

	enum BYldSendMessageAndGetReply_t
	{
		BYLDREPLY_SUCCESS,
		BYLDREPLY_SEND_FAILED,
		BYLDREPLY_TIMEOUT,
		BYLDREPLY_MSG_TYPE_MISMATCH,
	};
	BYldSendMessageAndGetReply_t BYldSendMessageAndGetReplyEx( CProtoBufMsgBase &msgOut, uint nTimeoutSec, CProtoBufMsgBase *pMsgIn, MsgType_t eMsg )
	{
		IMsgNetPacket *pNetPacket = NULL;

		msgOut.ExpectingReply( GetJobID() );

		if ( !m_pGCClient->BSendMessage( msgOut ) )
			return BYLDREPLY_SEND_FAILED;

		SetJobTimeout( nTimeoutSec );
		if( !BYieldingWaitForMsg( &pNetPacket ) )
			return BYLDREPLY_TIMEOUT;

		pMsgIn->InitFromPacket( pNetPacket );

		if ( pMsgIn->GetEMsg() != eMsg )
			return BYLDREPLY_MSG_TYPE_MISMATCH;

		return BYLDREPLY_SUCCESS;
	}

	bool BYldSendMessageAndGetReply( CProtoBufMsgBase &msgOut, uint nTimeoutSec, CProtoBufMsgBase *pMsgIn, MsgType_t eMsg )
	{
		if( BYldSendMessageAndGetReplyEx( msgOut, nTimeoutSec, pMsgIn, eMsg ) != BYLDREPLY_SUCCESS )
			return false;
		return true;
	}

	bool BYldSendMessageAndGetReply( CProtoBufMsgBase &msgOut, uint nTimeoutSec, IMsgNetPacket **ppNetPacket )
	{
		msgOut.ExpectingReply( GetJobID() );

		if ( !m_pGCClient->BSendMessage( msgOut ) )
			return false;

		SetJobTimeout( nTimeoutSec );
		return BYieldingWaitForMsg( ppNetPacket );
	}

	virtual uint32 CHeartbeatsBeforeTimeout() { return m_cHeartbeatsBeforeTimeout; }
	void SetJobTimeout( uint nTimeoutSec ) {  m_cHeartbeatsBeforeTimeout = 1 + ((nTimeoutSec * k_nMillion) / k_cMicroSecJobHeartbeat); }

private:
	virtual bool BYieldingRunJobFromMsg( IMsgNetPacket *pNetPacket )
	{
		// Protection against a NULL GCClient. Yields so the job is not deleted instantly
		if ( !m_pGCClient )
		{
			BYieldingWaitOneFrame();
			return false;
		}

		return BYieldingRunGCJob( pNetPacket );
	}

	virtual bool BYieldingRunJob( void *pvStartParam )
	{
		// Protection against a NULL GCClient. Yields so the job is not deleted instantly
		if ( !m_pGCClient )
		{
			BYieldingWaitOneFrame();
			return false;
		}

		return BYieldingRunGCJob();
	}

	uint32 m_cHeartbeatsBeforeTimeout;
};

} // namespace GCSDK

#endif // GCCLIENTJOB_H