/*
   Copyright (C) 1997-2001 Id Software, Inc.

   This program 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 2
   of the License, or (at your option) any later version.

   This program 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 this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

 */

#include "tv_local.h"

#include "tv_lobby.h"

#include "tv_relay.h"
#include "tv_downstream.h"

//=======================
//TV_Lobby_WriteFrameSnapToClient
//=======================
static void TV_Lobby_WriteFrameSnapToClient( client_t *client, msg_t *msg )
{
	int i, index, flags, pos, length;

	MSG_WriteByte( msg, svc_frame );

	pos = msg->cursize;
	MSG_WriteShort( msg, 0 );       // we will write length here later

	MSG_WriteLong( msg, tvs.realtime );
	MSG_WriteLong( msg, tvs.lobby.framenum );
	MSG_WriteLong( msg, client->lastframe );
	MSG_WriteLong( msg, client->UcmdReceived );

	flags = 0;
	MSG_WriteByte( msg, flags );

	MSG_WriteByte( msg, 0 ); // rate

	// add game comands
	MSG_WriteByte( msg, svc_gamecommands );
	for( i = client->gameCommandCurrent - MAX_RELIABLE_COMMANDS + 1; i <= client->gameCommandCurrent; i++ )
	{
		index = i & ( MAX_RELIABLE_COMMANDS - 1 );

		// check that it is valid command and that has not already been sent
		// we can only allow commands from certain amount of old frames, so the short won't overflow
		if( !client->gameCommands[index].command[0] || client->gameCommands[index].framenum + 256 < tvs.lobby.framenum ||
		   client->gameCommands[index].framenum > tvs.lobby.framenum ||
		   ( client->lastframe >= 0 && client->gameCommands[index].framenum <= (unsigned)client->lastframe ) )
			continue;

		// send it
		MSG_WriteShort( msg, tvs.lobby.framenum - client->gameCommands[index].framenum );
		MSG_WriteString( msg, client->gameCommands[index].command );
	}
	MSG_WriteShort( msg, -1 );

	// send over the areabits
	MSG_WriteByte( msg, 0 );

	// matchstate
	MSG_WriteByte( msg, svc_match );
	MSG_WriteByte( msg, 0 );

	// playerstate
	MSG_WriteByte( msg, 0 );

	// entities
	MSG_WriteByte( msg, svc_packetentities );
	MSG_WriteShort( msg, 0 );

	// sounds
	MSG_WriteByte( msg, 0 );

	// length
	length = msg->cursize - pos - 2;
	msg->cursize = pos;
	MSG_WriteShort( msg, length );
	msg->cursize += length;

	client->lastSentFrameNum = tvs.lobby.framenum;
}

//=======================
//TV_Lobby_SendClientDatagram
//=======================
static qboolean TV_Lobby_SendClientDatagram( client_t *client )
{
	qbyte msg_buf[MAX_MSGLEN];
	msg_t msg;

	assert( client );
	assert( !client->relay );

	TV_Downstream_InitClientMessage( client, &msg, msg_buf, sizeof( msg_buf ) );

	TV_Downstream_AddReliableCommandsToMessage( client, &msg );
	TV_Lobby_WriteFrameSnapToClient( client, &msg );

	return TV_Downstream_SendMessageToClient( client, &msg );
}

//=======================
//TV_Lobby_SendClientMessages
//=======================
static void TV_Lobby_SendClientMessages( void )
{
	int i;
	client_t *client;

	// send a message to each connected client
	for( i = 0, client = tvs.clients; i < tv_maxclients->integer; i++, client++ )
	{
		if( client->state != CS_SPAWNED )
			continue;

		if( client->relay )
			continue;

		if( !TV_Lobby_SendClientDatagram( client ) )
		{
			Com_Printf( "%s: Error sending message: %s\n", client->name, NET_ErrorString() );
			if( client->reliable )
			{
				TV_Downstream_DropClient( client, DROP_TYPE_GENERAL, "Error sending message: %s\n",
				                         NET_ErrorString() );
			}
		}
	}
}

//================
//TV_Lobby_RunSnap
//================
static qboolean TV_Lobby_RunSnap( void )
{
	if( tvs.lobby.lastrun + tvs.lobby.snapFrameTime > tvs.realtime )
		return qfalse;

	tvs.lobby.framenum++;
	return qtrue;
}

//=======================
//TV_Lobby_ClientBegin
//=======================
void TV_Lobby_ClientBegin( client_t *client )
{
	assert( client );
	assert( !client->relay );
}

//=======================
//TV_Lobby_ClientDisconnect
//=======================
void TV_Lobby_ClientDisconnect( client_t *client )
{
	assert( client );
}

//=======================
//TV_Lobby_CanConnect
//=======================
qboolean TV_Lobby_CanConnect( client_t *client, char *userinfo )
{
	char *value;

	assert( client );

	// check for a password
	value = Info_ValueForKey( userinfo, "password" );
	if( ( *tv_password->string && ( !value || strcmp( tv_password->string, value ) ) ) )
	{
		Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_PASSWORD ) );
		Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) );
		if( value && value[0] )
		{
			Info_SetValueForKey( userinfo, "rejmsg", "Incorrect password" );
		}
		else
		{
			Info_SetValueForKey( userinfo, "rejmsg", "Password required" );
		}
		return qfalse;
	}

	return qtrue;
}

//=======================
//TV_Lobby_ClientConnect
//=======================
void TV_Lobby_ClientConnect( client_t *client )
{
	assert( client );

	client->edict = NULL;
	client->relay = NULL;
}

//================
//TV_Lobby_Run
//================
void TV_Lobby_Run( void )
{
	if( TV_Lobby_RunSnap() )
	{
		tvs.lobby.lastrun = tvs.realtime;
		TV_Lobby_SendClientMessages();
	}
}
