//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============
//
// Purpose: Helper classes and functions for the save/restore system. These
// 			classes are internally structured to distinguish simple from
//			complex types.
//
// $NoKeywords: $
//=============================================================================

#ifndef SAVERESTORE_H
#define SAVERESTORE_H

#include "isaverestore.h"
#include "utlvector.h"
#include "filesystem.h"

#ifdef _WIN32
#pragma once
#endif

//-------------------------------------

class CSaveRestoreData;
class CSaveRestoreSegment;
class CGameSaveRestoreInfo;
struct typedescription_t;
struct edict_t;
struct datamap_t;
class CBaseEntity;
struct interval_t;

//-----------------------------------------------------------------------------
//
// CSave
//
//-----------------------------------------------------------------------------

class CSave : public ISave
{
public:
	CSave( CSaveRestoreData *pdata );
	
	//---------------------------------
	// Logging
	void			StartLogging( const char *pszLogName );
	void			EndLogging( void );

	//---------------------------------

	int				GetWritePos() const;
	void			SetWritePos(int pos);

	//---------------------------------
	// Datamap based writing
	//
	
	int				WriteAll( const void *pLeafObject, datamap_t *pLeafMap )	{ return DoWriteAll( pLeafObject, pLeafMap, pLeafMap ); }
	
	int				WriteFields( const char *pname, const void *pBaseData, datamap_t *pMap, typedescription_t *pFields, int fieldCount );

	//---------------------------------
	// Block support
	//
	
	virtual void	StartBlock( const char *pszBlockName );
	virtual void	StartBlock();
	virtual void	EndBlock();
	
	//---------------------------------
	// Primitive types
	//
	
	void			WriteShort( const short *value, int count = 1 );
	void			WriteInt( const int *value, int count = 1 );		                           // Save an int
	void			WriteBool( const bool *value, int count = 1 );		                           // Save a bool
	void			WriteFloat( const float *value, int count = 1 );	                           // Save a float
	void			WriteData( const char *pdata, int size );		                               // Save a binary data block
	void			WriteString( const char *pstring );			                                   // Save a null-terminated string
	void			WriteString( const string_t *stringId, int count = 1 );	                       // Save a null-terminated string (engine string)
	void			WriteVector( const Vector &value );				                               // Save a vector
	void			WriteVector( const Vector *value, int count = 1 );	                           // Save a vector array
	void			WriteQuaternion( const Quaternion &value );				                        // Save a Quaternion
	void			WriteQuaternion( const Quaternion *value, int count = 1 );	                    // Save a Quaternion array
	void			WriteVMatrix( const VMatrix *value, int count = 1 );							// Save a vmatrix array

	// Note: All of the following will write out both a header and the data. On restore,
	// this needs to be cracked
	void			WriteShort( const char *pname, const short *value, int count = 1 );
	void			WriteInt( const char *pname, const int *value, int count = 1 );		           // Save an int
	void			WriteBool( const char *pname, const bool *value, int count = 1 );		       // Save a bool
	void			WriteFloat( const char *pname, const float *value, int count = 1 );	           // Save a float
	void			WriteData( const char *pname, int size, const char *pdata );		           // Save a binary data block
	void			WriteString( const char *pname, const char *pstring );			               // Save a null-terminated string
	void			WriteString( const char *pname, const string_t *stringId, int count = 1 );	   // Save a null-terminated string (engine string)
	void			WriteVector( const char *pname, const Vector &value );				           // Save a vector
	void			WriteVector( const char *pname, const Vector *value, int count = 1 );	       // Save a vector array
	void			WriteQuaternion( const char *pname, const Quaternion &value );				   // Save a Quaternion
	void			WriteQuaternion( const char *pname, const Quaternion *value, int count = 1 );  // Save a Quaternion array
	void			WriteVMatrix( const char *pname, const VMatrix *value, int count = 1 );
	//---------------------------------
	// Game types
	//
	
	void			WriteTime( const char *pname, const float *value, int count = 1 );	           // Save a float (timevalue)
	void			WriteTick( const char *pname, const int *value, int count = 1 );	           // Save a int (timevalue)
	void			WritePositionVector( const char *pname, const Vector &value );		           // Offset for landmark if necessary
	void			WritePositionVector( const char *pname, const Vector *value, int count = 1 );  // array of pos vectors
	void			WriteFunction( datamap_t *pMap, const char *pname, const int *value, int count = 1 ); // Save a function pointer

	void			WriteEntityPtr( const char *pname, CBaseEntity **ppEntity, int count = 1 );
	void			WriteEdictPtr( const char *pname, edict_t **ppEdict, int count = 1 );
	void			WriteEHandle( const char *pname, const EHANDLE *pEHandle, int count = 1 );

	virtual void	WriteTime( const float *value, int count = 1 );	// Save a float (timevalue)
	virtual void	WriteTick( const int *value, int count = 1 );	// Save a int (timevalue)
	virtual void	WritePositionVector( const Vector &value );		// Offset for landmark if necessary
	virtual void	WritePositionVector( const Vector *value, int count = 1 );	// array of pos vectors

	virtual void	WriteEntityPtr( CBaseEntity **ppEntity, int count = 1 );
	virtual void	WriteEdictPtr( edict_t **ppEdict, int count = 1 );
	virtual void	WriteEHandle( const EHANDLE *pEHandle, int count = 1 );
	void			WriteVMatrixWorldspace( const char *pname, const VMatrix *value, int count = 1 );	       // Save a vmatrix array
	void			WriteVMatrixWorldspace( const VMatrix *value, int count = 1 );	       // Save a vmatrix array
	void			WriteMatrix3x4Worldspace( const matrix3x4_t *value, int count );
	void			WriteMatrix3x4Worldspace( const char *pname, const matrix3x4_t *value, int count );

	void			WriteInterval( const interval_t *value, int count = 1 );						// Save an interval
	void			WriteInterval( const char *pname, const interval_t *value, int count = 1 );

	//---------------------------------
	
	int				EntityIndex( const CBaseEntity *pEntity );
	int				EntityFlagsSet( int entityIndex, int flags );

	CGameSaveRestoreInfo *GetGameSaveRestoreInfo()	{ return m_pGameInfo; }

private:

	//---------------------------------
	bool			IsLogging( void );
	void			Log( const char *pName, fieldtype_t fieldType, void *value, int count );

	//---------------------------------
	
	void			BufferField( const char *pname, int size, const char *pdata );
	void			BufferData( const char *pdata, int size );
	void			WriteHeader( const char *pname, int size );

	int				DoWriteAll( const void *pLeafObject, datamap_t *pLeafMap, datamap_t *pCurMap );
	bool 			WriteField( const char *pname, void *pData, datamap_t *pRootMap, typedescription_t *pField );
	
	bool 			WriteBasicField( const char *pname, void *pData, datamap_t *pRootMap, typedescription_t *pField );
	
	int				DataEmpty( const char *pdata, int size );
	void			BufferString( char *pdata, int len );

	int				CountFieldsToSave( const void *pBaseData, typedescription_t *pFields, int fieldCount );
	bool			ShouldSaveField( const void *pData, typedescription_t *pField );

	//---------------------------------
	// Game info methods
	//
	
	bool			WriteGameField( const char *pname, void *pData, datamap_t *pRootMap, typedescription_t *pField );
	int				EntityIndex( const edict_t *pentLookup );
	
	//---------------------------------
	
	CUtlVector<int> m_BlockStartStack;
	
	// Stream data
	CSaveRestoreSegment *m_pData;
	
	// Game data
	CGameSaveRestoreInfo *m_pGameInfo;

	FileHandle_t		m_hLogFile;
};

//-----------------------------------------------------------------------------
//
// CRestore
//
//-----------------------------------------------------------------------------

class CRestore : public IRestore
{
public:
	CRestore( CSaveRestoreData *pdata );
	
	int				GetReadPos() const;
	void			SetReadPos( int pos );

	//---------------------------------
	// Datamap based reading
	//
	
	int				ReadAll( void *pLeafObject, datamap_t *pLeafMap )	{ return DoReadAll( pLeafObject, pLeafMap, pLeafMap ); }
	
	int				ReadFields( const char *pname, void *pBaseData, datamap_t *pMap, typedescription_t *pFields, int fieldCount );
	void 			EmptyFields( void *pBaseData, typedescription_t *pFields, int fieldCount );

	//---------------------------------
	// Block support
	//
	
	virtual void	StartBlock( SaveRestoreRecordHeader_t *pHeader );
	virtual void	StartBlock( char szBlockName[SIZE_BLOCK_NAME_BUF] );
	virtual void	StartBlock();
	virtual void	EndBlock();
	
	//---------------------------------
	// Field header cracking
	//
	
	void			ReadHeader( SaveRestoreRecordHeader_t *pheader );
	int				SkipHeader()	{ SaveRestoreRecordHeader_t header; ReadHeader( &header ); return header.size; }
	const char *	StringFromHeaderSymbol( int symbol );

	//---------------------------------
	// Primitive types
	//
	
	short			ReadShort( void );
	int				ReadShort( short *pValue, int count = 1, int nBytesAvailable = 0);
	int				ReadInt( int *pValue, int count = 1, int nBytesAvailable = 0);
	int				ReadInt( void );
	int 			ReadBool( bool *pValue, int count = 1, int nBytesAvailable = 0);
	int				ReadFloat( float *pValue, int count = 1, int nBytesAvailable = 0);
	int				ReadData( char *pData, int size, int nBytesAvailable );
	void			ReadString( char *pDest, int nSizeDest, int nBytesAvailable );			// A null-terminated string
	int				ReadString( string_t *pString, int count = 1, int nBytesAvailable = 0);
	int				ReadVector( Vector *pValue );
	int				ReadVector( Vector *pValue, int count = 1, int nBytesAvailable = 0);
	int				ReadQuaternion( Quaternion *pValue );
	int				ReadQuaternion( Quaternion *pValue, int count = 1, int nBytesAvailable = 0);
	int				ReadVMatrix( VMatrix *pValue, int count = 1, int nBytesAvailable = 0);
	
	//---------------------------------
	// Game types
	//
	
	int				ReadTime( float *pValue, int count = 1, int nBytesAvailable = 0);
	int				ReadTick( int *pValue, int count = 1, int nBytesAvailable = 0);
	int				ReadPositionVector( Vector *pValue );
	int				ReadPositionVector( Vector *pValue, int count = 1, int nBytesAvailable = 0);
	int				ReadFunction( datamap_t *pMap, void **pValue, int count = 1, int nBytesAvailable = 0);
	
	int 			ReadEntityPtr( CBaseEntity **ppEntity, int count = 1, int nBytesAvailable = 0 );
	int				ReadEdictPtr( edict_t **ppEdict, int count = 1, int nBytesAvailable = 0 );
	int				ReadEHandle( EHANDLE *pEHandle, int count = 1, int nBytesAvailable = 0 );
	int				ReadVMatrixWorldspace( VMatrix *pValue, int count = 1, int nBytesAvailable = 0);
	int				ReadMatrix3x4Worldspace( matrix3x4_t *pValue, int nElems = 1, int nBytesAvailable = 0 );
	int				ReadInterval( interval_t *interval, int count = 1, int nBytesAvailable = 0 );
	
	//---------------------------------
	
	void			SetGlobalMode( int global ) { m_global = global; }
	void			PrecacheMode( bool mode ) { m_precache = mode; }
	bool			GetPrecacheMode( void ) { return m_precache; }

	CGameSaveRestoreInfo *GetGameSaveRestoreInfo()	{ return m_pGameInfo; }

private:
	//---------------------------------
	// Read primitives
	//
	
	char *			BufferPointer( void );
	void			BufferSkipBytes( int bytes );
	
	int				DoReadAll( void *pLeafObject, datamap_t *pLeafMap, datamap_t *pCurMap );
	
	typedescription_t *FindField( const char *pszFieldName, typedescription_t *pFields, int fieldCount, int *pIterator );
	void			ReadField( const SaveRestoreRecordHeader_t &header, void *pDest, datamap_t *pRootMap, typedescription_t *pField );
	
	void 			ReadBasicField( const SaveRestoreRecordHeader_t &header, void *pDest, datamap_t *pRootMap, typedescription_t *pField );
	
	void			BufferReadBytes( char *pOutput, int size );

	template <typename T>
	int ReadSimple( T *pValue, int nElems, int nBytesAvailable ) // must be inline in class to keep MSVS happy
	{
		int desired = nElems * sizeof(T);
		int actual;
		
		if ( nBytesAvailable == 0 )
			actual = desired;
		else
		{
			Assert( nBytesAvailable % sizeof(T) == 0 );
			actual = min( desired, nBytesAvailable );
		}

		BufferReadBytes( (char *)pValue, actual );
		
		if ( actual < nBytesAvailable )
			BufferSkipBytes( nBytesAvailable - actual );
		
		return ( actual / sizeof(T) );
	}

	bool			ShouldReadField( typedescription_t *pField );
	bool 			ShouldEmptyField( typedescription_t *pField );

	//---------------------------------
	// Game info methods
	//
	CBaseEntity *	EntityFromIndex( int entityIndex );
	void 			ReadGameField( const SaveRestoreRecordHeader_t &header, void *pDest, datamap_t *pRootMap, typedescription_t *pField );
	
	//---------------------------------
	
	CUtlVector<int> m_BlockEndStack;
	
	// Stream data
	CSaveRestoreSegment *m_pData;

	// Game data
	CGameSaveRestoreInfo *	m_pGameInfo;
	int						m_global;		// Restoring a global entity?
	bool					m_precache;
};

//=============================================================================

#endif // SAVERESTORE_H
