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

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 __FILE_SAVEGAME_H__
#define __FILE_SAVEGAME_H__

#include "zlib/zlib.h"

// Listing of the types of files within a savegame package
enum saveGameType_t {
	SAVEGAMEFILE_NONE			= 0,
	SAVEGAMEFILE_TEXT			= BIT( 0 ),	// implies that no checksum will be used
	SAVEGAMEFILE_BINARY			= BIT( 1 ),	// implies that a checksum will also be used
	SAVEGAMEFILE_COMPRESSED		= BIT( 2 ),
	SAVEGAMEFILE_PIPELINED		= BIT( 3 ),
	SAVEGAMEFILE_THUMB			= BIT( 4 ),	// for special processing on certain platforms
	SAVEGAMEFILE_BKGRND_IMAGE	= BIT( 5 ),	// for special processing on certain platforms, large background used on PS3
	SAVEGAMEFILE_AUTO_DELETE	= BIT( 6 ),	// to be deleted automatically after completed
	SAVEGAMEFILE_OPTIONAL		= BIT( 7 )	// if this flag is not set and missing, there is an error
};

/*
================================================
idFile_SaveGame 
================================================
*/
class idFile_SaveGame : public idFile_Memory {
public:
	idFile_SaveGame() : type( SAVEGAMEFILE_NONE ), error( false ) {}
	idFile_SaveGame( const char * _name ) : idFile_Memory( _name ), type( SAVEGAMEFILE_NONE ), error( false ) {}
	idFile_SaveGame( const char * _name, int type_ ) : idFile_Memory( _name ), type( type_ ), error( false ) {}

	virtual ~idFile_SaveGame() { }

	bool operator==( const idFile_SaveGame & other ) const {
		return idStr::Icmp( GetName(), other.GetName() ) == 0;
	}
	bool operator==( const char * _name ) const {
		return idStr::Icmp( GetName(), _name ) == 0;
	}
	void SetNameAndType( const char *_name, int _type ) {
		name = _name;
		type = _type;
	}
public: // TODO_KC_CR for now...

	int					type;			// helps platform determine what to do with the file (encrypt, checksum, etc.)
	bool				error;			// when loading, this is set if there is a problem
};

/*
================================================
idFile_SaveGamePipelined uses threads to pipeline overlap compression and IO
================================================
*/
class idSGFreadThread;
class idSGFwriteThread;
class idSGFdecompressThread;
class idSGFcompressThread;

struct blockForIO_t {
	byte *		data;
	size_t		bytes;
};

class idFile_SaveGamePipelined : public idFile {
public:
	// The buffers each hold two blocks of data, so one block can be operated on by
	// the next part of the generate / compress / IO pipeline.  The factor of two
	// size difference between the uncompressed and compressed blocks is unrelated
	// to the fact that there are two blocks in each buffer.
	static const int COMPRESSED_BLOCK_SIZE		= 128 * 1024;
	static const int UNCOMPRESSED_BLOCK_SIZE	= 256 * 1024;


							idFile_SaveGamePipelined();
	virtual					~idFile_SaveGamePipelined();

	bool					OpenForReading( const char * const filename, bool useNativeFile );
	bool					OpenForWriting( const char * const filename, bool useNativeFile );

	bool					OpenForReading( idFile * file );
	bool					OpenForWriting( idFile * file );

	// Finish any reading or writing.
	void					Finish();

	// Abort any reading or writing.
	void					Abort();

	// Cancel any reading or writing for app termination
	static void				CancelToTerminate() { cancelToTerminate = true; }

	bool					ReadBuildVersion();
	const char *			GetBuildVersion() const { return buildVersion; }
	
	bool					ReadSaveFormatVersion();
	int						GetSaveFormatVersion() const { return saveFormatVersion; }
	int						GetPointerSize() const;

	//------------------------
	// idFile Interface
	//------------------------

	virtual const char *	GetName() const { return name.c_str(); }
	virtual const char *	GetFullPath() const	{ return name.c_str(); }
	virtual int				Read( void * buffer, int len );
	virtual int				Write( const void * buffer, int len );

	// this file is strictly streaming, you can't seek at all
	virtual int				Length() const  { return compressedLength; }
	virtual void			SetLength( size_t len ) { compressedLength = len; }
	virtual int				Tell() const { assert( 0 ); return 0; }
	virtual int				Seek( long offset, fsOrigin_t origin ) { assert( 0 ); return 0; }

	virtual ID_TIME_T		Timestamp()	const { return 0; }

	//------------------------
	// These can be used by a background thread to read/write data
	// when the file was opened with 'useNativeFile' set to false.
	//------------------------

	enum mode_t {
		CLOSED,
		WRITE,
		READ
	};

	// Get the file mode: read/write.
	mode_t					GetMode() const { return mode; }

	// Called by a background thread to get the next block to be written out.
	// This may block until a block has been made available through the pipeline.
	// Pass in NULL to notify the last write failed.
	// Returns false if there are no more blocks.
	bool					NextWriteBlock( blockForIO_t * block );

	// Called by a background thread to get the next block to read data into and to
	// report the number of bytes written to the previous block.
	// This may block until space is available to place the next block.
	// Pass in NULL to notify the end of the file was reached.
	// Returns false if there are no more blocks.
	bool					NextReadBlock( blockForIO_t * block, size_t lastReadBytes );

private:
	friend class idSGFreadThread;
	friend class idSGFwriteThread;
	friend class idSGFdecompressThread;
	friend class idSGFcompressThread;

	idStr					name;		// Name of the file.
	idStr					osPath;		// OS path.
	mode_t					mode;		// Open mode.
	size_t					compressedLength;

	static const int COMPRESSED_BUFFER_SIZE		= COMPRESSED_BLOCK_SIZE * 2;
	static const int UNCOMPRESSED_BUFFER_SIZE	= UNCOMPRESSED_BLOCK_SIZE * 2;

	byte					uncompressed[UNCOMPRESSED_BUFFER_SIZE];
	size_t					uncompressedProducedBytes;	// not masked
	size_t					uncompressedConsumedBytes;	// not masked

	byte					compressed[COMPRESSED_BUFFER_SIZE];
	size_t					compressedProducedBytes;	// not masked
	size_t					compressedConsumedBytes;	// not masked

	//------------------------
	// These variables are used to pass data between threads in a thread-safe manner.
	//------------------------

	byte *					dataZlib;
	size_t					bytesZlib;

	byte *					dataIO;
	size_t					bytesIO;

	//------------------------
	// These variables are used by CompressBlock() and DecompressBlock().
	//------------------------

	z_stream				zStream;
	int						zLibFlushType;		// Z_NO_FLUSH or Z_FINISH
	bool					zStreamEndHit;
	int						numChecksums;

	//------------------------
	// These variables are used by WriteBlock() and ReadBlock().
	//------------------------

	idFile *				nativeFile;
	bool					nativeFileEndHit;
	bool					finished;

	//------------------------
	// The background threads and signals for NextWriteBlock() and NextReadBlock().
	//------------------------

	idSGFreadThread *		readThread;
	idSGFwriteThread *		writeThread;

	idSGFdecompressThread *	decompressThread;
	idSGFcompressThread *	compressThread;

	idSysSignal				blockRequested;
	idSysSignal				blockAvailable;
	idSysSignal				blockFinished;

	idStrStatic< 32 >		buildVersion;		// build version this file was saved with
	int16					pointerSize;		// the number of bytes in a pointer, because different pointer sizes mean different offsets into objects a 64 bit build cannot load games saved from a 32 bit build or vice version (a value of 0 is interpreted as 4 bytes)
	int16					saveFormatVersion;	// version number specific to save games (for maintaining save compatibility across builds)

	//------------------------
	// These variables are used when we want to abort due to the termination of the application
	//------------------------
	static bool				cancelToTerminate;

	void					FlushUncompressedBlock();
	void					FlushCompressedBlock();
	void					CompressBlock();
	void					WriteBlock();

	void					PumpUncompressedBlock();
	void					PumpCompressedBlock();
	void					DecompressBlock();
	void					ReadBlock();
};

#endif // !__FILE_SAVEGAME_H__
