//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Generic named data buffer, declaration and implementation
//
//=============================================================================

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

#include "UtlMemory.h"

#pragma warning(disable: 4244) // warning C4244: '=' : conversion from 'int' to 'short', possible loss of data

//-----------------------------------------------------------------------------
// Purpose: Generic named data buffer
//-----------------------------------------------------------------------------
class CUtlMsgBuffer
{
public:
	CUtlMsgBuffer(unsigned short msgID, int initialSize);
	CUtlMsgBuffer(unsigned short msgID, void const *data, int dataSize);
	~CUtlMsgBuffer();

	unsigned short GetMsgID() const				{ return m_iMsgID; }
	void SetMsgID(unsigned short msgID)			{ m_iMsgID = msgID; }

	// read functions
	bool ReadInt(const char *name, int &data);
	bool ReadUInt(const char *name, unsigned int &data);
	bool ReadString(const char *name, char *data, int dataBufferSize);
	bool ReadBuffer(const char *name, CUtlMsgBuffer &buffer);
	// returns number of bytes read, 0 on failure
	int ReadBlob(const char *name, void *data, int dataBufferSize);	

	// reads out the next variable available in the buffer
	// fills out parameters with var details and data
	// returns false if no more vars available
	bool ReadNextVar(char name[32], bool &stringData, void *data, int &dataSize);

	// write functions
	void WriteInt(const char *name, int data);
	void WriteUInt(const char *name, unsigned int data);
	void WriteString(const char *name, const char *data);
	void WriteBlob(const char *name, const void *data, int dataSize);
	void WriteBuffer(const char *name, const CUtlMsgBuffer *buffer);

	// returns a pointer to the data buffer, and its size, of the specified variable
	void *FindVar(const char *name, int &dataSizeOut);

	// pads the buffer to the specified boundary (in bytes)
	void PadBuffer(int boundary);

    // makes sure the message has this much space allocated
    void EnsureCapacity(int dataSize);

	// returns the number of bytes used by the message
	int DataSize() const;

	// returns a pointer to the base data
	void *Base();

	// returns a const pointer to the base data
	const void *Base() const;

    // advances the write pointer - used when you write directly into the buffer
    void SetWritePos(int size);

    CUtlMsgBuffer& Copy(const CUtlMsgBuffer &rhs);

	// copy constructor
	CUtlMsgBuffer(const CUtlMsgBuffer &rhs)
	{
		m_iMsgID = rhs.m_iMsgID;
		m_iWritePos = rhs.m_iWritePos;
		m_iReadPos = rhs.m_iReadPos;
		m_iNextVarPos = rhs.m_iNextVarPos;

		m_Memory.EnsureCapacity(rhs.m_Memory.NumAllocated());
		if ( rhs.m_Memory.NumAllocated() > 0 )
		{
			memcpy(Base(), rhs.Base(), rhs.m_Memory.NumAllocated());
		}
	}

private:

	bool Read(void *buffer, int readAmount);
	bool ReadUntilNull(void *buffer, int bufferSize);
	void Write(void const *data, int size);

	CUtlMemory<char> m_Memory;

	unsigned short m_iMsgID;
	short m_iWritePos;		// position in buffer we are currently writing to
	short m_iReadPos;			// current reading position
	short m_iNextVarPos;		// a guess at which variable is most likely to be read next
};

//-----------------------------------------------------------------------------
// Purpose: returns the number of bytes used by the message
//-----------------------------------------------------------------------------
inline int CUtlMsgBuffer::DataSize() const
{
	// return the highest read/write mark
	if (m_iWritePos > m_iReadPos)
		return m_iWritePos;

	return m_iReadPos;
}

//-----------------------------------------------------------------------------
// Purpose: returns a pointer to the base data
//-----------------------------------------------------------------------------
inline void *CUtlMsgBuffer::Base()
{
	return &m_Memory[0];
}

//-----------------------------------------------------------------------------
// Purpose: returns a pointer to the base data
//-----------------------------------------------------------------------------
inline const void *CUtlMsgBuffer::Base() const
{
	return &m_Memory[0];
}

//-----------------------------------------------------------------------------
// Purpose: ensures capacity
//-----------------------------------------------------------------------------
inline void CUtlMsgBuffer::EnsureCapacity(int dataSize)
{
    m_Memory.EnsureCapacity(dataSize);
}

//-----------------------------------------------------------------------------
// Purpose: pads the buffer to the specified boundary (in bytes)
//-----------------------------------------------------------------------------
inline void CUtlMsgBuffer::PadBuffer(int boundary)
{
	// pad the buffer to be the right size for encryption
	int pad = (boundary - (DataSize() % boundary));	
	Write("\0\0\0\0\0\0\0\0\0\0\0\0", pad);
}

//-----------------------------------------------------------------------------
// Purpose: Reads in a named 4-byte int, returns true on sucess, false on failure
//-----------------------------------------------------------------------------
inline bool CUtlMsgBuffer::ReadInt(const char *name, int &data)
{
	return (ReadBlob(name, &data, 4) == 4);
}

//-----------------------------------------------------------------------------
// Purpose: Reads in a named 4-byte unsigned int, returns true on sucess, false on failure
//-----------------------------------------------------------------------------
inline bool CUtlMsgBuffer::ReadUInt(const char *name, unsigned int &data)
{
	return (ReadBlob(name, &data, 4) == 4);
}

//-----------------------------------------------------------------------------
// Purpose: Reads in a named variable length character string
//			returns true on sucess, false on failure
//-----------------------------------------------------------------------------
inline bool CUtlMsgBuffer::ReadString(const char *name, char *data, int dataBufferSize)
{
	return (ReadBlob(name, data, dataBufferSize) > 0);
}

//-----------------------------------------------------------------------------
// Purpose: Writes out an integer to the message
//-----------------------------------------------------------------------------
inline void CUtlMsgBuffer::WriteInt(const char *name, int data)
{
	WriteBlob(name, &data, 4);
}

//-----------------------------------------------------------------------------
// Purpose: Writes out an unsigned integer to the message
//-----------------------------------------------------------------------------
inline void CUtlMsgBuffer::WriteUInt(const char *name, unsigned int data)
{
	WriteBlob(name, &data, 4);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
inline void CUtlMsgBuffer::SetWritePos(int size)
{
    m_iWritePos = size;
}


#endif // UTLMSGBUFFER_H