/*
===========================================================================

Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.

This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").

Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/
#ifndef __SNAP_PROCESSOR_H__
#define __SNAP_PROCESSOR_H__

/*
================================================
idSnapshotProcessor
================================================
*/
class idSnapshotProcessor
{
public:
	static const int INITIAL_SNAP_SEQUENCE = 42;
	
	idSnapshotProcessor();
	~idSnapshotProcessor();
	
	void Reset( bool cstor = false );
	
	// TrySetPendingSnapshot Sets the currently pending snap.
	// No new snaps will be sent until this snap has been fully sent.
	// Returns true of the newly supplied snapshot was accepted (there were no pending snaps)
	bool TrySetPendingSnapshot( idSnapShot& ss );
	// Peek into delta to get deltaSequence, and deltaBaseSequence
	void PeekDeltaSequence( const char* deltaMem, int deltaSize, int& deltaSequence, int& deltaBaseSequence );
	// Apply a delta to the supplied snapshot
	bool ApplyDeltaToSnapshot( idSnapShot& snap, const char* deltaMem, int deltaSize, int visIndex );
	// Attempts to write the currently pending snap to the supplied buffer, which can then be sent as an unreliable msg.
	// SubmitPendingSnap will submit the pending snap to a job, so that it can be retrieved later for sending.
	void SubmitPendingSnap( int visIndex, uint8* objMemory, int objMemorySize, lzwCompressionData_t* lzwData );
	// GetPendingSnapDelta
	int GetPendingSnapDelta( byte* outBuffer, int maxLength );
	// If PendingSnapReadyToSend is true, then GetPendingSnapDelta will return something to send
	bool PendingSnapReadyToSend() const
	{
		return jobMemory->lzwInOutData.numlzwDeltas > 0;
	}
	// When you call WritePendingSnapshot, and then send the resulting buffer as a unreliable msg, you will eventually
	// receive this on the client.  Call this function to receive and apply it to the base state, and possibly return a fully received snap
	// to then apply to the client game state
	bool ReceiveSnapshotDelta( const byte* deltaData, int deltaLength, int visIndex, int& outSeq, int& outBaseSeq, idSnapShot& outSnap, bool& fullSnap );
	// Function to apply a received (or ack'd) delta to the base state
	bool ApplySnapshotDelta( int visIndex, int snapshotNumber );
	// Remove deltas for basestate we no longer have.
	// We know we can remove them, because we will never be able to apply them, since
	// the basestate needed to generate a full snap from these deltas is gone.
	void RemoveDeltasForOldBaseSequence();
	// Make sure delta sequence and basesequence values are valid, and in order, etc
	void SanityCheckDeltas();
	// HasPendingSnap will return true if there is more of the last TrySetPendingSnapshot to be sent
	bool HasPendingSnap() const
	{
		return hasPendingSnap;
	}
	
	idSnapShot* GetBaseState()
	{
		return &baseState;
	}
	idSnapShot* GetPendingSnap()
	{
		return &pendingSnap;
	}
	
	int GetSnapSequence()
	{
		return snapSequence;
	}
	int GetBaseSequence()
	{
		return baseSequence;
	}
	int GetFullSnapBaseSequence()
	{
		return lastFullSnapBaseSequence;
	}
	
	// This is used to ack the latest delta we have.  If we have no deltas, we sent -1 to make sure
	// Server knows we don't want to ack, since we are as up to date as we can be
	int GetLastAppendedSequence()
	{
		return deltas.Num() == 0 ? -1 : deltas.ItemSequence( deltas.Num() - 1 );
	}
	
	int	GetSnapQueueSize()
	{
		return deltas.Num();
	}
	
	bool IsBusyConfirmingPartialSnap();
	
	void AddSnapObjTemplate( int objID, idBitMsg& msg );
	
	static const int MAX_SNAPSHOT_QUEUE		= 64;
	
private:

	// Internal commands to set up, and flush the compressors
	static const int MAX_SNAP_SIZE			= idPacketProcessor::MAX_MSG_SIZE;
	static const int MAX_SNAPSHOT_QUEUE_MEM	= 64 * 1024;	// 64k
	
	// sequence number of the last snapshot we sent/received
	// on the server, the sequencing is different for each network peer (net_verboseSnapshot 1)
	// on the jobbed snapshot compression path, the sequence is incremented in NewLZWStream and pulled into this in idSnapshotProcessor::GetPendingSnapDelta
	int				snapSequence;
	int				baseSequence;
	int				lastFullSnapBaseSequence;		// Latest base sequence number that is a full snap
	
	idSnapShot		baseState;			// known snapshot base on the client
	idDataQueue< MAX_SNAPSHOT_QUEUE, MAX_SNAPSHOT_QUEUE_MEM >	deltas;		// list of unacknowledged snapshot deltas
	
	idSnapShot		pendingSnap;		// Current snap waiting to be fully sent
	bool			hasPendingSnap;		// true if pendingSnap is still waiting to be sent
	
	struct jobMemory_t
	{
		static const int MAX_LZW_DELTAS		= 1;			// FIXME: cleanup the old multiple delta support completely
		
		// @TODO this is a hack fix to allow online to load into coop (where there are lots of entities).
		// The real solution should be coming soon.
		// Doom MP: we encountered the same problem, going from 1024 to 4096 as well until a better solution is in place
		// (initial, useless, exchange of func_statics is killing us)
		static const int MAX_OBJ_PARMS		= 4096;
		
		static const int MAX_LZW_PARMS		= 32;
		static const int MAX_OBJ_HEADERS	= 256;
		static const int MAX_LZW_MEM		= 1024 * 8;		// 8k in the byte * lzwMem buffers, must be <= PS3_DMA_MAX
		
		// Parm memory to jobs
		idArray<objParms_t, MAX_OBJ_PARMS>		objParms;
		idArray<objHeader_t, MAX_OBJ_HEADERS>	headers;
		idArray<lzwParm_t, MAX_LZW_PARMS>		lzwParms;
		
		// Output memory from jobs
		idArray<lzwDelta_t, MAX_LZW_DELTAS>	lzwDeltas;			// Info about each pending delta output from jobs
		idArray<byte, MAX_LZW_MEM>		lzwMem;				// Memory for output from lzw jobs
		
		lzwInOutData_t	lzwInOutData;						// In/Out data used so lzw data can persist across lzw jobs
	};
	
	jobMemory_t* 	jobMemory;
	
	idSnapShot		submittedState;
	
	idSnapShot		templateStates;			// holds default snapshot states for some newly spawned object
	idSnapShot		submittedTemplateStates;
	
	int				partialBaseSequence;
};

#endif /* !__SNAP_PROCESSOR_H__ */
