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

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


#include "messbuf.h"
#include "utlvector.h"


class IWorkUnitDistributorCallbacks
{
public:
	// Called every 200ms or so as it does the work.
	// Return true to stop distributing work.
	virtual bool Update() { return false; }

	// Called when a subsequent number of work units is completed.
	// e.g. results received in the following order will trigger
	// the following calls to OnWorkUnitsCompleted:
	// Work unit numbers:                         wu2 wu4 wu5 wu1 wu0 wu6 wu3
	// Calling OnWorkUnitsCompleted with arg:      -   -   -   -   3   -   7
	// because when wu0 is received we already have { wu0, wu1, wu2 } so we signal
	// that 3 subsequent work units completed, like wise by the time when wu3 is
	// received we already have a full set { wu0, wu1, wu2, wu3, wu4, wu5, wu6 }
	// and signal that 7 work units completed.
	virtual void OnWorkUnitsCompleted( uint64 numWorkUnits ) { return; }
};


enum EWorkUnitDistributor
{
	k_eWorkUnitDistributor_Default,
	k_eWorkUnitDistributor_SDK
};

// Tells which work unit distributor is going to be used.
EWorkUnitDistributor VMPI_GetActiveWorkUnitDistributor();


// Before calling DistributeWork, you can set this and it'll call your virtual functions.
extern IWorkUnitDistributorCallbacks *g_pDistributeWorkCallbacks;


// You must append data to pBuf with the work unit results.
// Note: pBuf will be NULL if this is a local thread doing work on the master.
typedef void (*ProcessWorkUnitFn)( int iThread, uint64 iWorkUnit, MessageBuffer *pBuf );

// pBuf is ready to read the results written to the buffer in ProcessWorkUnitFn.
typedef void (*ReceiveWorkUnitFn)( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker );


// Use a CDispatchReg to register this function with whatever packet ID you give to DistributeWork.
bool DistributeWorkDispatch( MessageBuffer *pBuf, int iSource, int iPacketID );



// This is the function vrad and vvis use to divide the work units and send them out.
// It maintains a sliding window of work units so it can always keep the clients busy.
//
// The workers implement processFn to do the job work in a work unit.
// This function must send back a packet formatted with:
// cPacketID (char), cSubPacketID (char), iWorkUnit (int), (app-specific data for the results)
//
// The masters implement receiveFn to receive a work unit's results.
//
// Returns time it took to finish the work.
double DistributeWork( 
	uint64 nWorkUnits,				// how many work units to dole out
	char cPacketID,					// This packet ID must be reserved for DistributeWork and DistributeWorkDispatch
									// must be registered with it.
	ProcessWorkUnitFn processFn,	// workers implement this to process a work unit and send results back
	ReceiveWorkUnitFn receiveFn		// the master implements this to receive a work unit
	);


// VMPI calls this before shutting down because any threads that DistributeWork has running must stop,
// otherwise it can crash if a thread tries to send data in the middle of shutting down.
void DistributeWork_Cancel();


#endif // VMPI_DISTRIBUTE_WORK_H