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

#include <utlbuffer.h>
#include "tooldemofile.h"
#include "filesystem.h"
#include "demofile/demoformat.h"

extern IBaseFileSystem *g_pFileSystem;

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CToolDemoFile::CToolDemoFile()
{
	m_hDemoFile = FILESYSTEM_INVALID_HANDLE;
}

CToolDemoFile::~CToolDemoFile()
{
	if ( IsOpen() )
	{
		Close();
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CToolDemoFile::ReadSequenceInfo(int &nSeqNrIn, int &nSeqNrOut)
{
	Assert( m_hDemoFile != FILESYSTEM_INVALID_HANDLE );

	g_pFileSystem->Read( &nSeqNrIn, sizeof(int), m_hDemoFile );
	g_pFileSystem->Read( &nSeqNrOut, sizeof(int), m_hDemoFile );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CToolDemoFile::ReadCmdInfo( democmdinfo_t& info )
{
	Assert( m_hDemoFile != FILESYSTEM_INVALID_HANDLE );
	g_pFileSystem->Read( &info, sizeof( democmdinfo_t ), m_hDemoFile );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : cmd - 
//			dt - 
//			frame - 
//-----------------------------------------------------------------------------
void CToolDemoFile::ReadCmdHeader( unsigned char& cmd, int& tick )
{
	Assert( m_hDemoFile != FILESYSTEM_INVALID_HANDLE );

	// Read the command
	int r = g_pFileSystem->Read ( &cmd, sizeof(byte), m_hDemoFile );
	
	if ( r <=0 )
	{
		Warning("Missing end tag in demo file.\n");
		cmd = dem_stop;
		return;
	}

	Assert( cmd >= 1 && cmd <= dem_lastcmd );

	// Read the timestamp
	g_pFileSystem->Read ( &tick, sizeof(int), m_hDemoFile );
	
	tick = LittleDWord( tick );
}

const char *CToolDemoFile::ReadConsoleCommand()
{
	static char cmdstring[1024];
	
	ReadRawData( cmdstring, sizeof(cmdstring) );

	return cmdstring;
}

unsigned int CToolDemoFile::GetCurPos()
{
	if ( m_hDemoFile == FILESYSTEM_INVALID_HANDLE )
		return 0;

	return g_pFileSystem->Tell( m_hDemoFile );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : expected_length - 
//			&demofile - 
//-----------------------------------------------------------------------------
int CToolDemoFile::ReadNetworkDataTables( CUtlBuffer *buf )
{
	Assert( m_hDemoFile != FILESYSTEM_INVALID_HANDLE );
	char data[ 1024 ];
	int length;

	g_pFileSystem->Read( &length, sizeof( int ), m_hDemoFile );

	while( length > 0 )
	{
		int chunk = min( length, 1024 );
		g_pFileSystem->Read( data, chunk, m_hDemoFile );
		length -= chunk;

		if ( buf )
		{
			buf->Put( data, chunk );
		}
	}

	return length;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : discard - 
//-----------------------------------------------------------------------------
int CToolDemoFile::ReadUserCmd( char *buffer, int &size )
{
	Assert( m_hDemoFile != FILESYSTEM_INVALID_HANDLE );

	int outgoing_sequence;
	
	g_pFileSystem->Read( &outgoing_sequence, sizeof( int ), m_hDemoFile );
	
	size = ReadRawData( buffer, size );

	return outgoing_sequence;
}

//-----------------------------------------------------------------------------
// Purpose: Rewind from the current spot by the time stamp, byte code and frame counter offsets
//-----------------------------------------------------------------------------
void CToolDemoFile::SeekTo( int position )
{
	Assert( m_hDemoFile != FILESYSTEM_INVALID_HANDLE );
	g_pFileSystem->Seek( m_hDemoFile, position, FILESYSTEM_SEEK_HEAD );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
int CToolDemoFile::ReadRawData( char *buffer, int length )
{
	Assert( m_hDemoFile != FILESYSTEM_INVALID_HANDLE );
	
	int size;
	
	// read length of data block
	g_pFileSystem->Read( &size, sizeof( int ), m_hDemoFile );

	if ( buffer && (length < size) )
	{
		DevMsg("CToolDemoFile::ReadRawData: buffe overflow (%i).\n", size );
		return -1;
	}

	if ( buffer )
	{
		if ( length < size )
		{
			// given buffer is too small
			DevMsg("CToolDemoFile::ReadRawData: buffe overflow (%i).\n", size );
			g_pFileSystem->Seek( m_hDemoFile, size, FILESYSTEM_SEEK_CURRENT );
			size = -1;
		}
		else
		{
			// read data into buffer
			int r = g_pFileSystem->Read( buffer, size, m_hDemoFile );
			if ( r != size )
			{
				Warning( "Error reading demo message data.\n");
				return -1;
			}
		}
	}
	else
	{
		// just skip it
		g_pFileSystem->Seek( m_hDemoFile, size, FILESYSTEM_SEEK_CURRENT );
	}

	return size;
}

demoheader_t *CToolDemoFile::ReadDemoHeader()
{
	if ( m_hDemoFile == FILESYSTEM_INVALID_HANDLE )
		return NULL;	// file not open
	
	// goto file start
	g_pFileSystem->Seek(m_hDemoFile, 0, FILESYSTEM_SEEK_HEAD);

	Q_memset( &m_DemoHeader, 0, sizeof(m_DemoHeader) );

	int r = g_pFileSystem->Read( &m_DemoHeader, sizeof(demoheader_t), m_hDemoFile );

	if ( r != sizeof(demoheader_t) )
		return NULL;  // reading failed

	if ( Q_strcmp ( m_DemoHeader.demofilestamp, DEMO_HEADER_ID ) )
	{
		Warning( "%s has invalid demo header ID.\n", m_szFileName );
		return NULL;
	}

	/*
// The current network protocol version.  Changing this makes clients and servers incompatible
#define PROTOCOL_VERSION    7

	if ( m_DemoHeader.networkprotocol != PROTOCOL_VERSION )
	{
		Warning ("ERROR: demo network protocol %i outdated, engine version is %i \n", 
			m_DemoHeader.networkprotocol, PROTOCOL_VERSION );

		return NULL;
	}
	*/

	if ( m_DemoHeader.demoprotocol != DEMO_PROTOCOL )
	{
		Warning ("ERROR: demo file protocol %i outdated, engine version is %i \n", 
			m_DemoHeader.demoprotocol, DEMO_PROTOCOL );

		return NULL;
	}

	return &m_DemoHeader;
}

bool CToolDemoFile::Open(const char *name, bool bReadOnly)
{
	if ( m_hDemoFile != FILESYSTEM_INVALID_HANDLE )
	{
		Warning ("CToolDemoFile::Open: file already open.\n");
		return false;
	}

	m_szFileName[0] = 0;  // clear name
	Q_memset( &m_DemoHeader, 0, sizeof(m_DemoHeader) ); // and demo header

	if ( bReadOnly )
	{
		// open existing file for reading only
		m_hDemoFile = g_pFileSystem->Open (name, "rb", "GAME" );
	}
	else
	{
		// create new file for writing only
		m_hDemoFile = g_pFileSystem->Open (name, "wb", "GAME" );
	}

	if ( m_hDemoFile == FILESYSTEM_INVALID_HANDLE )
	{
		Warning ("CToolDemoFile::Open: couldn't open file %s for %s.\n", 
			name, bReadOnly?"reading":"writing" );
		return false;
	}

	Q_strncpy( m_szFileName, name, sizeof(m_szFileName) );

	return true;
}

bool CToolDemoFile::IsOpen()
{
	return m_hDemoFile != FILESYSTEM_INVALID_HANDLE;
}

void CToolDemoFile::Close()
{
	if ( m_hDemoFile != FILESYSTEM_INVALID_HANDLE )
	{
		g_pFileSystem->Close(m_hDemoFile);
		m_hDemoFile = FILESYSTEM_INVALID_HANDLE;
	}
}

int CToolDemoFile::GetSize()
{
	return g_pFileSystem->Size( m_hDemoFile );
}