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

#include "RemoteServer.h"
#include <assert.h>
#include <stdio.h>

#include "tier1/utlbuffer.h"
#include "IGameServerData.h"
extern IGameServerData *g_pGameServerData;

//-----------------------------------------------------------------------------
// Purpose: singleton accessor
//-----------------------------------------------------------------------------
CRemoteServer &RemoteServer()
{
	static CRemoteServer s_RemoteServer;	
	return s_RemoteServer;
}

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CRemoteServer::CRemoteServer()
{
	m_iCurrentRequestID = 0;
	m_ListenerID = INVALID_LISTENER_ID;
	m_bInitialized = false;
}

//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CRemoteServer::~CRemoteServer()
{
}

//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
void CRemoteServer::Initialize()
{
	m_bInitialized = true;
	Assert( g_pGameServerData );
	m_ListenerID = g_pGameServerData->GetNextListenerID( false ); // don't require auth on this connection
	g_pGameServerData->RegisterAdminUIID( m_ListenerID );
}

//-----------------------------------------------------------------------------
// Purpose: connects to a remote game server
//-----------------------------------------------------------------------------
void CRemoteServer::ConnectRemoteGameServer(unsigned int ip, unsigned short port, const char *password)
{
	assert(!("CRemoteServer::ConnectRemoteGameServer() not yet implemented"));
}

//-----------------------------------------------------------------------------
// Purpose: request a cvar/data from the server
//-----------------------------------------------------------------------------
void CRemoteServer::RequestValue(IServerDataResponse *requester, const char *variable)
{
	Assert( m_bInitialized );
	// add to the response handling table
	int i = m_ResponseHandlers.AddToTail();
	m_ResponseHandlers[i].requestID = m_iCurrentRequestID;
	m_ResponseHandlers[i].handler = requester;

	// build the command
	char buf[512];
	CUtlBuffer cmd(buf, sizeof(buf));

	cmd.PutInt(m_iCurrentRequestID++);
	cmd.PutInt(SERVERDATA_REQUESTVALUE);
	cmd.PutString(variable);
	cmd.PutString("");

	// send to server
	g_pGameServerData->WriteDataRequest(m_ListenerID, cmd.Base(), cmd.TellPut());
}

//-----------------------------------------------------------------------------
// Purpose: sets a value
//-----------------------------------------------------------------------------
void CRemoteServer::SetValue(const char *variable, const char *value)
{
	Assert( m_bInitialized );
	// build the command
	char buf[512];
	CUtlBuffer cmd(buf, sizeof(buf));

	cmd.PutInt(m_iCurrentRequestID++);
	cmd.PutInt(SERVERDATA_SETVALUE);
	cmd.PutString(variable);
	cmd.PutString(value);

	// send to server
	g_pGameServerData->WriteDataRequest(m_ListenerID, cmd.Base(), cmd.TellPut());
}

//-----------------------------------------------------------------------------
// Purpose: sends a custom command
//-----------------------------------------------------------------------------
void CRemoteServer::SendCommand(const char *commandString)
{
	Assert( m_bInitialized );
	// build the command
	char buf[512];
	CUtlBuffer cmd(buf, sizeof(buf));

	cmd.PutInt(m_iCurrentRequestID++);
	cmd.PutInt(SERVERDATA_EXECCOMMAND);
	cmd.PutString(commandString);
	cmd.PutString("");

	g_pGameServerData->WriteDataRequest(m_ListenerID, cmd.Base(), cmd.TellPut());
}

//-----------------------------------------------------------------------------
// Purpose: changes the current password on the server
//			responds with "PasswordChange" "true" or "PasswordChange" "false"
//-----------------------------------------------------------------------------
void CRemoteServer::ChangeAccessPassword(IServerDataResponse *requester, const char *newPassword)
{
}

//-----------------------------------------------------------------------------
// Purpose: process any return values, firing any IServerDataResponse items
// Output : returns true if any items were fired
//-----------------------------------------------------------------------------
bool CRemoteServer::ProcessServerResponse()
{
	Assert(g_pGameServerData != NULL);
	Assert( m_bInitialized );

	char charbuf[4096];
	bool bProcessedAnyPackets = false;
	while (1)
	{
		// get packet from networking
		int bytesRead = g_pGameServerData->ReadDataResponse(m_ListenerID, charbuf, sizeof(charbuf));
		if (bytesRead < 1)
			break;
		bProcessedAnyPackets = true;

		// parse response
		CUtlBuffer buf(charbuf, bytesRead, CUtlBuffer::READ_ONLY);
		int requestID = buf.GetInt();
		int responseType = buf.GetInt();
		char variable[64];
		buf.GetString(variable);

		switch (responseType)
		{
		case SERVERDATA_RESPONSE_VALUE:
			{
				int valueSize = buf.GetInt();
				Assert(valueSize > 0);
				CUtlBuffer value(0, valueSize);
				if (valueSize > 0)
				{
					value.Put(buf.PeekGet(), valueSize);
				}
				else
				{
					// null terminate
					value.PutChar(0);
				}

				// find callback (usually will be the first one in the list)
				for (int i = m_ResponseHandlers.Head(); m_ResponseHandlers.IsValidIndex(i); i = m_ResponseHandlers.Next(i))
				{
					if (m_ResponseHandlers[i].requestID == requestID)
					{
						// found, call
						m_ResponseHandlers[i].handler->OnServerDataResponse(variable, (const char *)value.Base());

						// remove from list
						m_ResponseHandlers.Remove(i);
						
						// there is only ever one handler for a message
						break;
					}
				}
			}
			break;

		case SERVERDATA_UPDATE:
			{
				// find all the people watching for this message
				for (int i = m_MessageHandlers.Head(); m_MessageHandlers.IsValidIndex(i); i = m_MessageHandlers.Next(i))
				{
					if (!stricmp(m_MessageHandlers[i].messageName, variable))
					{
						// found, call
						m_MessageHandlers[i].handler->OnServerDataResponse(variable, "");

						// keep looking, there can be more than one handler for a message
					}
				}
			}
			break;
		default:
			Assert(responseType == SERVERDATA_RESPONSE_VALUE || responseType == SERVERDATA_UPDATE);
			break;
		}
	}

	return bProcessedAnyPackets;
}

//-----------------------------------------------------------------------------
// Purpose: adds a constant watches for a particular message
//-----------------------------------------------------------------------------
void CRemoteServer::AddServerMessageHandler(IServerDataResponse *handler, const char *watch)
{
	// add to the server message handling table
	int i = m_MessageHandlers.AddToTail();
	strncpy(m_MessageHandlers[i].messageName, watch, sizeof(m_MessageHandlers[i].messageName) - 1);
	m_MessageHandlers[i].messageName[sizeof(m_MessageHandlers[i].messageName) - 1] = 0;
	m_MessageHandlers[i].handler = handler;
}

//-----------------------------------------------------------------------------
// Purpose: removes a requester from the list to guarantee the pointer won't be used
//-----------------------------------------------------------------------------
void CRemoteServer::RemoveServerDataResponseTarget(IServerDataResponse *invalidRequester)
{
	// iterate the responses
	for (int i = 0; i < m_ResponseHandlers.MaxElementIndex(); i++)
	{
		if (m_ResponseHandlers.IsValidIndex(i))
		{
			if (m_ResponseHandlers[i].handler == invalidRequester)
			{
				// found invalid handler, remove from list
				m_ResponseHandlers.Remove(i);
			}
		}
	}
	// iterate the message handlers
	for (int i = 0; i < m_MessageHandlers.MaxElementIndex(); i++)
	{
		if (m_MessageHandlers.IsValidIndex(i))
		{
			if (m_MessageHandlers[i].handler == invalidRequester)
			{
				// found invalid handler, remove from list
				m_MessageHandlers.Remove(i);
			}
		}
	}
}